veloren_query_server/
proto.rs

1#![expect(non_local_definitions)] // necessary because of the Protocol derive macro
2use protocol::Protocol;
3
4pub(crate) const VERSION: u16 = 0;
5pub(crate) const VELOREN_HEADER: [u8; 7] = [b'v', b'e', b'l', b'o', b'r', b'e', b'n'];
6pub(crate) const MAX_REQUEST_CONTENT_SIZE: usize = 300;
7// NOTE: The actual maximum size must never exceed 1200 or we risk getting near
8// MTU limits for some networks.
9pub(crate) const MAX_REQUEST_SIZE: usize = MAX_REQUEST_CONTENT_SIZE + VELOREN_HEADER.len() + 2;
10pub(crate) const MAX_RESPONSE_SIZE: usize = 256;
11
12#[derive(Protocol, Debug, Clone, Copy)]
13pub(crate) struct RawQueryServerRequest {
14    /// See comment on [`Init::p`]
15    pub p: u64,
16    pub request: QueryServerRequest,
17}
18
19#[derive(Protocol, Debug, Clone, Copy)]
20#[protocol(discriminant = "integer")]
21#[protocol(discriminator(u8))]
22pub enum QueryServerRequest {
23    /// This requests exists mostly for backwards-compatibilty reasons. As the
24    /// first message sent to the server should always be in the V0 version
25    /// of the protocol, if future versions of the protocol have more
26    /// requests than server info it may be confusing to request `P` and the max
27    /// version with a `QueryServerRequest::ServerInfo` request (the request
28    /// will still be dropped as the supplied `P` value is invalid).
29    Init,
30    ServerInfo,
31    // New requests should be added at the end to prevent breakage.
32    // NOTE: Any new (sub-)variants must be added to the `check_request_sizes` test at the end of
33    // this file
34}
35
36#[derive(Protocol, Debug, Clone, Copy)]
37pub(crate) struct Init {
38    /// This is used as a challenge to prevent IP address spoofing by verifying
39    /// that the client can receive from the source address.
40    ///
41    /// Any request to the server must include this value to be processed,
42    /// otherwise this response will be returned (giving clients a value to pass
43    /// for later requests).
44    pub p: u64,
45    /// The maximum supported protocol version by the server. The first request
46    /// to a server must always be done in the V0 protocol to query this value.
47    /// Following requests (when the version is known), can be done in the
48    /// maximum version or below, responses will be sent in the same version as
49    /// the requests.
50    pub max_supported_version: u16,
51}
52
53#[derive(Protocol, Debug, Clone, Copy)]
54#[protocol(discriminant = "integer")]
55#[protocol(discriminator(u8))]
56pub(crate) enum RawQueryServerResponse {
57    Response(QueryServerResponse),
58    Init(Init),
59}
60
61#[derive(Protocol, Debug, Clone, Copy)]
62#[protocol(discriminant = "integer")]
63#[protocol(discriminator(u8))]
64pub enum QueryServerResponse {
65    ServerInfo(ServerInfo),
66    // New responses should be added at the end to prevent breakage
67}
68
69#[derive(Protocol, Debug, Clone, Copy, PartialEq, Eq)]
70pub struct ServerInfo {
71    pub git_hash: u32,
72    pub git_timestamp: i64,
73    pub players_count: u16,
74    pub player_cap: u16,
75    pub battlemode: ServerBattleMode,
76}
77
78#[derive(Protocol, Debug, Clone, Copy, PartialEq, Eq)]
79#[protocol(discriminant = "integer")]
80#[protocol(discriminator(u8))]
81#[repr(u8)]
82pub enum ServerBattleMode {
83    GlobalPvP,
84    GlobalPvE,
85    PerPlayer,
86}
87
88impl RawQueryServerRequest {
89    #[cfg(any(feature = "client", test))]
90    pub fn serialize(&self) -> Result<Vec<u8>, protocol::Error> {
91        use protocol::Parcel;
92
93        let mut buf = Vec::with_capacity(MAX_REQUEST_SIZE);
94
95        // 2 extra bytes for version information, currently unused
96        buf.extend(VERSION.to_le_bytes());
97        buf.extend({
98            let request_data =
99                <RawQueryServerRequest as Parcel>::raw_bytes(self, &Default::default())?;
100            if request_data.len() > MAX_REQUEST_CONTENT_SIZE {
101                panic!(
102                    "Attempted to send request larger than the max size (size: {}, max size: \
103                     {MAX_REQUEST_CONTENT_SIZE}, request: {self:?})",
104                    request_data.len()
105                );
106            }
107            request_data
108        });
109        const _: () = assert!(MAX_RESPONSE_SIZE + VELOREN_HEADER.len() <= MAX_REQUEST_SIZE);
110        buf.resize(MAX_RESPONSE_SIZE.max(buf.len()), 0);
111        buf.extend(VELOREN_HEADER);
112        Ok(buf)
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::{QueryServerRequest, RawQueryServerRequest};
119
120    #[test]
121    fn check_request_sizes() {
122        const ALL_REQUESTS: &[QueryServerRequest] =
123            &[QueryServerRequest::ServerInfo, QueryServerRequest::Init];
124        for request in ALL_REQUESTS {
125            let request = RawQueryServerRequest {
126                p: 0,
127                request: *request,
128            };
129            request.serialize().unwrap(); // This will panic if the size is above MAX_REQUEST_SIZE
130        }
131    }
132}