1#![expect(non_local_definitions)] // necessary because of the Protocol derive macro
2use protocol::Protocol;
34pub(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;
1112#[derive(Protocol, Debug, Clone, Copy)]
13pub(crate) struct RawQueryServerRequest {
14/// See comment on [`Init::p`]
15pub p: u64,
16pub request: QueryServerRequest,
17}
1819#[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).
29Init,
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}
3536#[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).
44pub 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.
50pub max_supported_version: u16,
51}
5253#[derive(Protocol, Debug, Clone, Copy)]
54#[protocol(discriminant = "integer")]
55#[protocol(discriminator(u8))]
56pub(crate) enum RawQueryServerResponse {
57 Response(QueryServerResponse),
58 Init(Init),
59}
6061#[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}
6869#[derive(Protocol, Debug, Clone, Copy, PartialEq, Eq)]
70pub struct ServerInfo {
71pub git_hash: u32,
72pub git_timestamp: i64,
73pub players_count: u16,
74pub player_cap: u16,
75pub battlemode: ServerBattleMode,
76}
7778#[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}
8788impl RawQueryServerRequest {
89#[cfg(any(feature = "client", test))]
90pub fn serialize(&self) -> Result<Vec<u8>, protocol::Error> {
91use protocol::Parcel;
9293let mut buf = Vec::with_capacity(MAX_REQUEST_SIZE);
9495// 2 extra bytes for version information, currently unused
96buf.extend(VERSION.to_le_bytes());
97 buf.extend({
98let request_data =
99 <RawQueryServerRequest as Parcel>::raw_bytes(self, &Default::default())?;
100if request_data.len() > MAX_REQUEST_CONTENT_SIZE {
101panic!(
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 });
109const _: () = 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);
112Ok(buf)
113 }
114}
115116#[cfg(test)]
117mod tests {
118use super::{QueryServerRequest, RawQueryServerRequest};
119120#[test]
121fn check_request_sizes() {
122const ALL_REQUESTS: &[QueryServerRequest] =
123&[QueryServerRequest::ServerInfo, QueryServerRequest::Init];
124for request in ALL_REQUESTS {
125let 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}