veloren_query_server/
proto.rs

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