Master server for Half-Life/Xash3D.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
3.4 KiB

// SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
use std::net::SocketAddrV4;
use crate::cursor::{Cursor, CursorMut};
use crate::filter::Filter;
use crate::server::Region;
use crate::Error;
#[derive(Clone, Debug, PartialEq)]
pub struct QueryServers<'a> {
pub region: Region,
pub last: SocketAddrV4,
pub filter: Filter<'a>,
}
impl<'a> QueryServers<'a> {
pub const HEADER: &'static [u8] = b"1";
pub fn decode(src: &'a [u8]) -> Result<Self, Error> {
let mut cur = Cursor::new(src);
cur.expect(Self::HEADER)?;
let region = cur.get_u8()?.try_into().map_err(|_| Error::InvalidPacket)?;
let last = cur.get_cstr_as_str()?;
let filter = cur.get_cstr()?;
cur.expect_empty()?;
Ok(Self {
region,
last: last.parse().map_err(|_| Error::InvalidPacket)?,
filter: Filter::from_bytes(&filter)?,
})
}
pub fn encode(&self, buf: &mut [u8]) -> Result<usize, Error> {
Ok(CursorMut::new(buf)
.put_bytes(Self::HEADER)?
.put_u8(self.region as u8)?
.put_as_str(self.last)?
.put_u8(0)?
.put_as_str(&self.filter)?
.put_u8(0)?
.pos())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct GetServerInfo {
pub protocol: u8,
}
impl GetServerInfo {
pub const HEADER: &'static [u8] = b"\xff\xff\xff\xffinfo ";
pub fn new(protocol: u8) -> Self {
Self { protocol }
}
pub fn decode(src: &[u8]) -> Result<Self, Error> {
let mut cur = Cursor::new(src);
cur.expect(Self::HEADER)?;
let protocol = cur
.get_str(cur.remaining())?
.parse()
.map_err(|_| Error::InvalidPacket)?;
Ok(Self { protocol })
}
pub fn encode(&self, buf: &mut [u8]) -> Result<usize, Error> {
Ok(CursorMut::new(buf)
.put_bytes(Self::HEADER)?
.put_as_str(self.protocol)?
.pos())
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Packet<'a> {
QueryServers(QueryServers<'a>),
GetServerInfo(GetServerInfo),
}
impl<'a> Packet<'a> {
pub fn decode(src: &'a [u8]) -> Result<Self, Error> {
if let Ok(p) = QueryServers::decode(src) {
return Ok(Self::QueryServers(p));
}
if let Ok(p) = GetServerInfo::decode(src) {
return Ok(Self::GetServerInfo(p));
}
Err(Error::InvalidPacket)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::filter::{FilterFlags, Version};
use std::net::Ipv4Addr;
#[test]
fn query_servers() {
let p = QueryServers {
region: Region::RestOfTheWorld,
last: SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0),
filter: Filter {
gamedir: &b"valve"[..],
map: &b"crossfire"[..],
clver: Version::new(0, 20),
flags: FilterFlags::all(),
flags_mask: FilterFlags::all(),
},
};
let mut buf = [0; 512];
let n = p.encode(&mut buf).unwrap();
assert_eq!(QueryServers::decode(&buf[..n]), Ok(p));
}
#[test]
fn get_server_info() {
let p = GetServerInfo::new(49);
let mut buf = [0; 512];
let n = p.encode(&mut buf).unwrap();
assert_eq!(GetServerInfo::decode(&buf[..n]), Ok(p));
}
}