Browse Source

protocol: add net feature

master
Denis Drakhnia 2 months ago
parent
commit
cfe44a04de
  1. 3
      protocol/Cargo.toml
  2. 477
      protocol/src/cursor.rs
  3. 305
      protocol/src/cursor/cursor.rs
  4. 207
      protocol/src/cursor/cursor_mut.rs
  5. 30
      protocol/src/filter.rs
  6. 13
      protocol/src/lib.rs
  7. 6
      protocol/src/net.rs
  8. 0
      protocol/src/net/admin.rs
  9. 10
      protocol/src/net/game.rs
  10. 6
      protocol/src/net/master.rs
  11. 159
      protocol/src/net/server.rs
  12. 134
      protocol/src/server_info.rs

3
protocol/Cargo.toml

@ -12,9 +12,10 @@ homepage = "https://xash.su"
repository = "https://git.mentality.rip/numas13/xash3d-master" repository = "https://git.mentality.rip/numas13/xash3d-master"
[features] [features]
default = ["std"] default = ["std", "net"]
std = ["alloc"] std = ["alloc"]
alloc = [] alloc = []
net = []
[dependencies] [dependencies]
log = "0.4.18" log = "0.4.18"

477
protocol/src/cursor.rs

@ -1,16 +1,13 @@
// SPDX-License-Identifier: LGPL-3.0-only // SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com> // SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
use core::{ mod cursor;
fmt::{self, Write}, mod cursor_mut;
mem, str,
};
#[cfg(feature = "alloc")] use core::fmt;
use alloc::{borrow::ToOwned, boxed::Box, string::String};
use super::color; pub use cursor::{Cursor, GetKeyValue};
use super::wrappers::Str; pub use cursor_mut::{CursorMut, PutKeyValue};
/// The error type for `Cursor` and `CursorMut`. /// The error type for `Cursor` and `CursorMut`.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@ -56,472 +53,12 @@ impl core::error::Error for CursorError {}
pub type Result<T, E = CursorError> = core::result::Result<T, E>; pub type Result<T, E = CursorError> = core::result::Result<T, E>;
pub trait GetKeyValue<'a>: Sized {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self>;
}
impl<'a> GetKeyValue<'a> for &'a [u8] {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
cur.get_key_value_raw()
}
}
impl<'a> GetKeyValue<'a> for Str<&'a [u8]> {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
cur.get_key_value_raw().map(Str)
}
}
impl<'a> GetKeyValue<'a> for &'a str {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
let raw = cur.get_key_value_raw()?;
str::from_utf8(raw).map_err(|_| CursorError::InvalidString)
}
}
#[cfg(feature = "alloc")]
impl<'a> GetKeyValue<'a> for Box<str> {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
let raw = cur.get_key_value_raw()?;
str::from_utf8(raw)
.map(|s| s.to_owned().into_boxed_str())
.map_err(|_| CursorError::InvalidString)
}
}
#[cfg(feature = "alloc")]
impl<'a> GetKeyValue<'a> for String {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
let raw = cur.get_key_value_raw()?;
str::from_utf8(raw)
.map(|s| s.to_owned())
.map_err(|_| CursorError::InvalidString)
}
}
impl<'a> GetKeyValue<'a> for bool {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
match cur.get_key_value_raw()? {
b"0" => Ok(false),
b"1" => Ok(true),
_ => Err(CursorError::InvalidBool),
}
}
}
macro_rules! impl_get_value {
($($t:ty),+ $(,)?) => {
$(impl<'a> GetKeyValue<'a> for $t {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
let s = cur.get_key_value::<&str>()?;
// HACK: special case for one asshole
let (_, s) = color::trim_start_color(s);
s.parse().map_err(|_| CursorError::InvalidNumber)
}
})+
};
}
impl_get_value! {
u8,
u16,
u32,
u64,
i8,
i16,
i32,
i64,
}
// TODO: impl GetKeyValue for f32 and f64
#[derive(Copy, Clone)]
pub struct Cursor<'a> {
buffer: &'a [u8],
}
macro_rules! impl_get {
($($n:ident: $t:ty = $f:ident),+ $(,)?) => (
$(#[inline]
pub fn $n(&mut self) -> Result<$t> {
const N: usize = mem::size_of::<$t>();
self.get_array::<N>().map(<$t>::$f)
})+
);
}
impl<'a> Cursor<'a> {
pub fn new(buffer: &'a [u8]) -> Self {
Self { buffer }
}
pub fn end(self) -> &'a [u8] {
self.buffer
}
pub fn as_slice(&'a self) -> &'a [u8] {
self.buffer
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.buffer.len()
}
#[inline(always)]
pub fn has_remaining(&self) -> bool {
self.remaining() != 0
}
pub fn get_bytes(&mut self, count: usize) -> Result<&'a [u8]> {
if count <= self.remaining() {
let (head, tail) = self.buffer.split_at(count);
self.buffer = tail;
Ok(head)
} else {
Err(CursorError::UnexpectedEnd)
}
}
pub fn advance(&mut self, count: usize) -> Result<()> {
self.get_bytes(count).map(|_| ())
}
pub fn get_array<const N: usize>(&mut self) -> Result<[u8; N]> {
self.get_bytes(N).map(|s| {
let mut array = [0; N];
array.copy_from_slice(s);
array
})
}
pub fn get_str(&mut self, n: usize) -> Result<&'a str> {
let mut cur = *self;
let s = cur
.get_bytes(n)
.and_then(|s| str::from_utf8(s).map_err(|_| CursorError::InvalidString))?;
*self = cur;
Ok(s)
}
pub fn get_cstr(&mut self) -> Result<Str<&'a [u8]>> {
let pos = self
.buffer
.iter()
.position(|&c| c == b'\0')
.ok_or(CursorError::UnexpectedEnd)?;
let (head, tail) = self.buffer.split_at(pos);
self.buffer = &tail[1..];
Ok(Str(&head[..pos]))
}
pub fn get_cstr_as_str(&mut self) -> Result<&'a str> {
str::from_utf8(&self.get_cstr()?).map_err(|_| CursorError::InvalidString)
}
#[inline(always)]
pub fn get_u8(&mut self) -> Result<u8> {
self.get_array::<1>().map(|s| s[0])
}
#[inline(always)]
pub fn get_i8(&mut self) -> Result<i8> {
self.get_array::<1>().map(|s| s[0] as i8)
}
impl_get! {
get_u16_le: u16 = from_le_bytes,
get_u32_le: u32 = from_le_bytes,
get_u64_le: u64 = from_le_bytes,
get_i16_le: i16 = from_le_bytes,
get_i32_le: i32 = from_le_bytes,
get_i64_le: i64 = from_le_bytes,
get_f32_le: f32 = from_le_bytes,
get_f64_le: f64 = from_le_bytes,
get_u16_be: u16 = from_be_bytes,
get_u32_be: u32 = from_be_bytes,
get_u64_be: u64 = from_be_bytes,
get_i16_be: i16 = from_be_bytes,
get_i32_be: i32 = from_be_bytes,
get_i64_be: i64 = from_be_bytes,
get_f32_be: f32 = from_be_bytes,
get_f64_be: f64 = from_be_bytes,
get_u16_ne: u16 = from_ne_bytes,
get_u32_ne: u32 = from_ne_bytes,
get_u64_ne: u64 = from_ne_bytes,
get_i16_ne: i16 = from_ne_bytes,
get_i32_ne: i32 = from_ne_bytes,
get_i64_ne: i64 = from_ne_bytes,
get_f32_ne: f32 = from_ne_bytes,
get_f64_ne: f64 = from_ne_bytes,
}
pub fn expect(&mut self, s: &[u8]) -> Result<()> {
if self.buffer.starts_with(s) {
self.advance(s.len())?;
Ok(())
} else {
Err(CursorError::Expect)
}
}
pub fn expect_empty(&self) -> Result<()> {
if self.has_remaining() {
Err(CursorError::ExpectEmpty)
} else {
Ok(())
}
}
pub fn take_while<F>(&mut self, mut cond: F) -> Result<&'a [u8]>
where
F: FnMut(u8) -> bool,
{
self.buffer
.iter()
.position(|&i| !cond(i))
.ok_or(CursorError::UnexpectedEnd)
.and_then(|n| self.get_bytes(n))
}
pub fn take_while_or_all<F>(&mut self, cond: F) -> &'a [u8]
where
F: FnMut(u8) -> bool,
{
self.take_while(cond).unwrap_or_else(|_| {
let (head, tail) = self.buffer.split_at(self.buffer.len());
self.buffer = tail;
head
})
}
pub fn get_key_value_raw(&mut self) -> Result<&'a [u8]> {
let mut cur = *self;
match cur.get_u8()? {
b'\\' => {
let value = cur.take_while_or_all(|c| c != b'\\' && c != b'\n');
*self = cur;
Ok(value)
}
_ => Err(CursorError::InvalidTableValue),
}
}
pub fn get_key_value<T: GetKeyValue<'a>>(&mut self) -> Result<T> {
T::get_key_value(self)
}
pub fn skip_key_value<T: GetKeyValue<'a>>(&mut self) -> Result<()> {
T::get_key_value(self).map(|_| ())
}
pub fn get_key_raw(&mut self) -> Result<&'a [u8]> {
let mut cur = *self;
match cur.get_u8() {
Ok(b'\\') => {
let value = cur.take_while(|c| c != b'\\' && c != b'\n')?;
*self = cur;
Ok(value)
}
Ok(b'\n') | Err(CursorError::UnexpectedEnd) => Err(CursorError::TableEnd),
_ => Err(CursorError::InvalidTableKey),
}
}
pub fn get_key<T: GetKeyValue<'a>>(&mut self) -> Result<(&'a [u8], T)> {
Ok((self.get_key_raw()?, self.get_key_value()?))
}
}
pub trait PutKeyValue {
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>>;
}
impl<T> PutKeyValue for &T
where
T: PutKeyValue,
{
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>> {
(*self).put_key_value(cur)
}
}
impl PutKeyValue for &str {
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>> {
cur.put_str(self)
}
}
impl PutKeyValue for bool {
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>> {
cur.put_u8(if *self { b'1' } else { b'0' })
}
}
macro_rules! impl_put_key_value {
($($t:ty),+ $(,)?) => {
$(impl PutKeyValue for $t {
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>> {
cur.put_as_str(self)
}
})+
};
}
impl_put_key_value! {
u8,
u16,
u32,
u64,
i8,
i16,
i32,
i64,
f32,
f64,
}
pub struct CursorMut<'a> {
buffer: &'a mut [u8],
pos: usize,
}
macro_rules! impl_put {
($($n:ident: $t:ty = $f:ident),+ $(,)?) => (
$(#[inline]
pub fn $n(&mut self, n: $t) -> Result<&mut Self> {
self.put_array(&n.$f())
})+
);
}
impl<'a> CursorMut<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
Self { buffer, pos: 0 }
}
pub fn pos(&mut self) -> usize {
self.pos
}
#[inline(always)]
pub fn available(&self) -> usize {
self.buffer.len() - self.pos
}
pub fn advance<F>(&mut self, count: usize, mut f: F) -> Result<&mut Self>
where
F: FnMut(&mut [u8]),
{
if count <= self.available() {
f(&mut self.buffer[self.pos..self.pos + count]);
self.pos += count;
Ok(self)
} else {
Err(CursorError::UnexpectedEnd)
}
}
pub fn put_bytes(&mut self, s: &[u8]) -> Result<&mut Self> {
self.advance(s.len(), |i| {
i.copy_from_slice(s);
})
}
pub fn put_array<const N: usize>(&mut self, s: &[u8; N]) -> Result<&mut Self> {
self.advance(N, |i| {
i.copy_from_slice(s);
})
}
pub fn put_str(&mut self, s: &str) -> Result<&mut Self> {
self.put_bytes(s.as_bytes())
}
pub fn put_cstr(&mut self, s: &str) -> Result<&mut Self> {
self.put_str(s)?.put_u8(0)
}
#[inline(always)]
pub fn put_u8(&mut self, n: u8) -> Result<&mut Self> {
self.put_array(&[n])
}
#[inline(always)]
pub fn put_i8(&mut self, n: i8) -> Result<&mut Self> {
self.put_u8(n as u8)
}
impl_put! {
put_u16_le: u16 = to_le_bytes,
put_u32_le: u32 = to_le_bytes,
put_u64_le: u64 = to_le_bytes,
put_i16_le: i16 = to_le_bytes,
put_i32_le: i32 = to_le_bytes,
put_i64_le: i64 = to_le_bytes,
put_f32_le: f32 = to_le_bytes,
put_f64_le: f64 = to_le_bytes,
put_u16_be: u16 = to_be_bytes,
put_u32_be: u32 = to_be_bytes,
put_u64_be: u64 = to_be_bytes,
put_i16_be: i16 = to_be_bytes,
put_i32_be: i32 = to_be_bytes,
put_i64_be: i64 = to_be_bytes,
put_f32_be: f32 = to_be_bytes,
put_f64_be: f64 = to_be_bytes,
put_u16_ne: u16 = to_ne_bytes,
put_u32_ne: u32 = to_ne_bytes,
put_u64_ne: u64 = to_ne_bytes,
put_i16_ne: i16 = to_ne_bytes,
put_i32_ne: i32 = to_ne_bytes,
put_i64_ne: i64 = to_ne_bytes,
put_f32_ne: f32 = to_ne_bytes,
put_f64_ne: f64 = to_ne_bytes,
}
pub fn put_as_str<T: fmt::Display>(&mut self, value: T) -> Result<&mut Self> {
write!(self, "{}", value).map_err(|_| CursorError::UnexpectedEnd)?;
Ok(self)
}
pub fn put_key_value<T: PutKeyValue>(&mut self, value: T) -> Result<&mut Self> {
value.put_key_value(self)
}
pub fn put_key_raw(&mut self, key: &str, value: &[u8]) -> Result<&mut Self> {
self.put_u8(b'\\')?
.put_str(key)?
.put_u8(b'\\')?
.put_bytes(value)
}
pub fn put_key<T: PutKeyValue>(&mut self, key: &str, value: T) -> Result<&mut Self> {
self.put_u8(b'\\')?
.put_str(key)?
.put_u8(b'\\')?
.put_key_value(value)
}
}
impl fmt::Write for CursorMut<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.put_bytes(s.as_bytes())
.map(|_| ())
.map_err(|_| fmt::Error)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::wrappers::Str;
#[test] #[test]
fn cursor() -> Result<()> { fn cursor() -> Result<()> {
let mut buf = [0; 64]; let mut buf = [0; 64];

305
protocol/src/cursor/cursor.rs

@ -0,0 +1,305 @@
// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
#![cfg_attr(not(feature = "net"), allow(dead_code))]
use core::{mem, str};
#[cfg(feature = "alloc")]
use alloc::{borrow::ToOwned, boxed::Box, string::String};
use crate::{color, wrappers::Str};
use super::{CursorError, Result};
pub trait GetKeyValue<'a>: Sized {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self>;
}
impl<'a> GetKeyValue<'a> for &'a [u8] {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
cur.get_key_value_raw()
}
}
impl<'a> GetKeyValue<'a> for Str<&'a [u8]> {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
cur.get_key_value_raw().map(Str)
}
}
impl<'a> GetKeyValue<'a> for &'a str {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
let raw = cur.get_key_value_raw()?;
str::from_utf8(raw).map_err(|_| CursorError::InvalidString)
}
}
#[cfg(feature = "alloc")]
impl<'a> GetKeyValue<'a> for Box<str> {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
let raw = cur.get_key_value_raw()?;
str::from_utf8(raw)
.map(|s| s.to_owned().into_boxed_str())
.map_err(|_| CursorError::InvalidString)
}
}
#[cfg(feature = "alloc")]
impl<'a> GetKeyValue<'a> for String {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
let raw = cur.get_key_value_raw()?;
str::from_utf8(raw)
.map(|s| s.to_owned())
.map_err(|_| CursorError::InvalidString)
}
}
impl<'a> GetKeyValue<'a> for bool {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
match cur.get_key_value_raw()? {
b"0" => Ok(false),
b"1" => Ok(true),
_ => Err(CursorError::InvalidBool),
}
}
}
impl GetKeyValue<'_> for crate::server_info::Region {
fn get_key_value(cur: &mut Cursor) -> Result<Self, CursorError> {
cur.get_key_value::<u8>()?.try_into()
}
}
impl GetKeyValue<'_> for crate::server_info::ServerType {
fn get_key_value(cur: &mut Cursor) -> Result<Self, CursorError> {
cur.get_key_value_raw()?.try_into()
}
}
macro_rules! impl_get_value {
($($t:ty),+ $(,)?) => {
$(impl<'a> GetKeyValue<'a> for $t {
fn get_key_value(cur: &mut Cursor<'a>) -> Result<Self> {
let s = cur.get_key_value::<&str>()?;
// HACK: special case for one asshole
let (_, s) = color::trim_start_color(s);
s.parse().map_err(|_| CursorError::InvalidNumber)
}
})+
};
}
impl_get_value! {
u8,
u16,
u32,
u64,
i8,
i16,
i32,
i64,
}
// TODO: impl GetKeyValue for f32 and f64
#[derive(Copy, Clone)]
pub struct Cursor<'a> {
buffer: &'a [u8],
}
macro_rules! impl_get {
($($n:ident: $t:ty = $f:ident),+ $(,)?) => (
$(#[inline]
pub fn $n(&mut self) -> Result<$t> {
const N: usize = mem::size_of::<$t>();
self.get_array::<N>().map(<$t>::$f)
})+
);
}
impl<'a> Cursor<'a> {
pub fn new(buffer: &'a [u8]) -> Self {
Self { buffer }
}
pub fn end(self) -> &'a [u8] {
self.buffer
}
pub fn as_slice(&'a self) -> &'a [u8] {
self.buffer
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.buffer.len()
}
#[inline(always)]
pub fn has_remaining(&self) -> bool {
self.remaining() != 0
}
pub fn get_bytes(&mut self, count: usize) -> Result<&'a [u8]> {
if count <= self.remaining() {
let (head, tail) = self.buffer.split_at(count);
self.buffer = tail;
Ok(head)
} else {
Err(CursorError::UnexpectedEnd)
}
}
pub fn advance(&mut self, count: usize) -> Result<()> {
self.get_bytes(count).map(|_| ())
}
pub fn get_array<const N: usize>(&mut self) -> Result<[u8; N]> {
self.get_bytes(N).map(|s| {
let mut array = [0; N];
array.copy_from_slice(s);
array
})
}
pub fn get_str(&mut self, n: usize) -> Result<&'a str> {
let mut cur = *self;
let s = cur
.get_bytes(n)
.and_then(|s| str::from_utf8(s).map_err(|_| CursorError::InvalidString))?;
*self = cur;
Ok(s)
}
pub fn get_cstr(&mut self) -> Result<Str<&'a [u8]>> {
let pos = self
.buffer
.iter()
.position(|&c| c == b'\0')
.ok_or(CursorError::UnexpectedEnd)?;
let (head, tail) = self.buffer.split_at(pos);
self.buffer = &tail[1..];
Ok(Str(&head[..pos]))
}
pub fn get_cstr_as_str(&mut self) -> Result<&'a str> {
str::from_utf8(&self.get_cstr()?).map_err(|_| CursorError::InvalidString)
}
#[inline(always)]
pub fn get_u8(&mut self) -> Result<u8> {
self.get_array::<1>().map(|s| s[0])
}
#[inline(always)]
pub fn get_i8(&mut self) -> Result<i8> {
self.get_array::<1>().map(|s| s[0] as i8)
}
impl_get! {
get_u16_le: u16 = from_le_bytes,
get_u32_le: u32 = from_le_bytes,
get_u64_le: u64 = from_le_bytes,
get_i16_le: i16 = from_le_bytes,
get_i32_le: i32 = from_le_bytes,
get_i64_le: i64 = from_le_bytes,
get_f32_le: f32 = from_le_bytes,
get_f64_le: f64 = from_le_bytes,
get_u16_be: u16 = from_be_bytes,
get_u32_be: u32 = from_be_bytes,
get_u64_be: u64 = from_be_bytes,
get_i16_be: i16 = from_be_bytes,
get_i32_be: i32 = from_be_bytes,
get_i64_be: i64 = from_be_bytes,
get_f32_be: f32 = from_be_bytes,
get_f64_be: f64 = from_be_bytes,
get_u16_ne: u16 = from_ne_bytes,
get_u32_ne: u32 = from_ne_bytes,
get_u64_ne: u64 = from_ne_bytes,
get_i16_ne: i16 = from_ne_bytes,
get_i32_ne: i32 = from_ne_bytes,
get_i64_ne: i64 = from_ne_bytes,
get_f32_ne: f32 = from_ne_bytes,
get_f64_ne: f64 = from_ne_bytes,
}
pub fn expect(&mut self, s: &[u8]) -> Result<()> {
if self.buffer.starts_with(s) {
self.advance(s.len())?;
Ok(())
} else {
Err(CursorError::Expect)
}
}
pub fn expect_empty(&self) -> Result<()> {
if self.has_remaining() {
Err(CursorError::ExpectEmpty)
} else {
Ok(())
}
}
pub fn take_while<F>(&mut self, mut cond: F) -> Result<&'a [u8]>
where
F: FnMut(u8) -> bool,
{
self.buffer
.iter()
.position(|&i| !cond(i))
.ok_or(CursorError::UnexpectedEnd)
.and_then(|n| self.get_bytes(n))
}
pub fn take_while_or_all<F>(&mut self, cond: F) -> &'a [u8]
where
F: FnMut(u8) -> bool,
{
self.take_while(cond).unwrap_or_else(|_| {
let (head, tail) = self.buffer.split_at(self.buffer.len());
self.buffer = tail;
head
})
}
pub fn get_key_value_raw(&mut self) -> Result<&'a [u8]> {
let mut cur = *self;
match cur.get_u8()? {
b'\\' => {
let value = cur.take_while_or_all(|c| c != b'\\' && c != b'\n');
*self = cur;
Ok(value)
}
_ => Err(CursorError::InvalidTableValue),
}
}
pub fn get_key_value<T: GetKeyValue<'a>>(&mut self) -> Result<T> {
T::get_key_value(self)
}
pub fn skip_key_value<T: GetKeyValue<'a>>(&mut self) -> Result<()> {
T::get_key_value(self).map(|_| ())
}
pub fn get_key_raw(&mut self) -> Result<&'a [u8]> {
let mut cur = *self;
match cur.get_u8() {
Ok(b'\\') => {
let value = cur.take_while(|c| c != b'\\' && c != b'\n')?;
*self = cur;
Ok(value)
}
Ok(b'\n') | Err(CursorError::UnexpectedEnd) => Err(CursorError::TableEnd),
_ => Err(CursorError::InvalidTableKey),
}
}
pub fn get_key<T: GetKeyValue<'a>>(&mut self) -> Result<(&'a [u8], T)> {
Ok((self.get_key_raw()?, self.get_key_value()?))
}
}

207
protocol/src/cursor/cursor_mut.rs

@ -0,0 +1,207 @@
// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
#![cfg_attr(not(feature = "net"), allow(dead_code))]
use core::{
fmt::{self, Write},
str,
};
use super::{CursorError, Result};
pub trait PutKeyValue {
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>>;
}
impl<T> PutKeyValue for &T
where
T: PutKeyValue,
{
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>> {
(*self).put_key_value(cur)
}
}
impl PutKeyValue for &str {
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>> {
cur.put_str(self)
}
}
impl PutKeyValue for bool {
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>> {
cur.put_u8(if *self { b'1' } else { b'0' })
}
}
impl PutKeyValue for crate::server_info::ServerType {
fn put_key_value<'a, 'b>(
&self,
cur: &'b mut CursorMut<'a>,
) -> Result<&'b mut CursorMut<'a>, CursorError> {
match self {
Self::Dedicated => cur.put_str("d"),
Self::Local => cur.put_str("l"),
Self::Proxy => cur.put_str("p"),
Self::Unknown => cur.put_str("?"),
}
}
}
macro_rules! impl_put_key_value {
($($t:ty),+ $(,)?) => {
$(impl PutKeyValue for $t {
fn put_key_value<'a, 'b>(&self, cur: &'b mut CursorMut<'a>) -> Result<&'b mut CursorMut<'a>> {
cur.put_as_str(self)
}
})+
};
}
impl_put_key_value! {
u8,
u16,
u32,
u64,
i8,
i16,
i32,
i64,
f32,
f64,
}
pub struct CursorMut<'a> {
buffer: &'a mut [u8],
pos: usize,
}
macro_rules! impl_put {
($($n:ident: $t:ty = $f:ident),+ $(,)?) => (
$(#[inline]
pub fn $n(&mut self, n: $t) -> Result<&mut Self> {
self.put_array(&n.$f())
})+
);
}
impl<'a> CursorMut<'a> {
pub fn new(buffer: &'a mut [u8]) -> Self {
Self { buffer, pos: 0 }
}
pub fn pos(&mut self) -> usize {
self.pos
}
#[inline(always)]
pub fn available(&self) -> usize {
self.buffer.len() - self.pos
}
pub fn advance<F>(&mut self, count: usize, mut f: F) -> Result<&mut Self>
where
F: FnMut(&mut [u8]),
{
if count <= self.available() {
f(&mut self.buffer[self.pos..self.pos + count]);
self.pos += count;
Ok(self)
} else {
Err(CursorError::UnexpectedEnd)
}
}
pub fn put_bytes(&mut self, s: &[u8]) -> Result<&mut Self> {
self.advance(s.len(), |i| {
i.copy_from_slice(s);
})
}
pub fn put_array<const N: usize>(&mut self, s: &[u8; N]) -> Result<&mut Self> {
self.advance(N, |i| {
i.copy_from_slice(s);
})
}
pub fn put_str(&mut self, s: &str) -> Result<&mut Self> {
self.put_bytes(s.as_bytes())
}
pub fn put_cstr(&mut self, s: &str) -> Result<&mut Self> {
self.put_str(s)?.put_u8(0)
}
#[inline(always)]
pub fn put_u8(&mut self, n: u8) -> Result<&mut Self> {
self.put_array(&[n])
}
#[inline(always)]
pub fn put_i8(&mut self, n: i8) -> Result<&mut Self> {
self.put_u8(n as u8)
}
impl_put! {
put_u16_le: u16 = to_le_bytes,
put_u32_le: u32 = to_le_bytes,
put_u64_le: u64 = to_le_bytes,
put_i16_le: i16 = to_le_bytes,
put_i32_le: i32 = to_le_bytes,
put_i64_le: i64 = to_le_bytes,
put_f32_le: f32 = to_le_bytes,
put_f64_le: f64 = to_le_bytes,
put_u16_be: u16 = to_be_bytes,
put_u32_be: u32 = to_be_bytes,
put_u64_be: u64 = to_be_bytes,
put_i16_be: i16 = to_be_bytes,
put_i32_be: i32 = to_be_bytes,
put_i64_be: i64 = to_be_bytes,
put_f32_be: f32 = to_be_bytes,
put_f64_be: f64 = to_be_bytes,
put_u16_ne: u16 = to_ne_bytes,
put_u32_ne: u32 = to_ne_bytes,
put_u64_ne: u64 = to_ne_bytes,
put_i16_ne: i16 = to_ne_bytes,
put_i32_ne: i32 = to_ne_bytes,
put_i64_ne: i64 = to_ne_bytes,
put_f32_ne: f32 = to_ne_bytes,
put_f64_ne: f64 = to_ne_bytes,
}
pub fn put_as_str<T: fmt::Display>(&mut self, value: T) -> Result<&mut Self> {
write!(self, "{}", value).map_err(|_| CursorError::UnexpectedEnd)?;
Ok(self)
}
pub fn put_key_value<T: PutKeyValue>(&mut self, value: T) -> Result<&mut Self> {
value.put_key_value(self)
}
pub fn put_key_raw(&mut self, key: &str, value: &[u8]) -> Result<&mut Self> {
self.put_u8(b'\\')?
.put_str(key)?
.put_u8(b'\\')?
.put_bytes(value)
}
pub fn put_key<T: PutKeyValue>(&mut self, key: &str, value: T) -> Result<&mut Self> {
self.put_u8(b'\\')?
.put_str(key)?
.put_u8(b'\\')?
.put_key_value(value)
}
}
impl fmt::Write for CursorMut<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.put_bytes(s.as_bytes())
.map(|_| ())
.map_err(|_| fmt::Error)
}
}

30
protocol/src/filter.rs

@ -34,10 +34,16 @@ use core::{fmt, net::SocketAddr, str::FromStr};
use bitflags::bitflags; use bitflags::bitflags;
use crate::{ use crate::{
cursor::{Cursor, GetKeyValue, PutKeyValue}, cursor::{Cursor, CursorError, GetKeyValue, PutKeyValue},
server::{ServerAdd, ServerFlags, ServerType}, server_info::ServerInfo,
wrappers::Str, wrappers::Str,
ServerInfo, {CursorError, Error}, Error,
};
#[cfg(feature = "net")]
use crate::{
net::server::ServerAdd,
server_info::{ServerFlags, ServerType},
}; };
bitflags! { bitflags! {
@ -65,6 +71,7 @@ bitflags! {
} }
} }
#[cfg(feature = "net")]
impl<T> From<&ServerAdd<T>> for FilterFlags { impl<T> From<&ServerAdd<T>> for FilterFlags {
fn from(info: &ServerAdd<T>) -> Self { fn from(info: &ServerAdd<T>) -> Self {
let mut flags = Self::empty(); let mut flags = Self::empty();
@ -309,13 +316,9 @@ impl fmt::Display for &Filter<'_> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::net::SocketAddr;
use crate::{cursor::CursorMut, wrappers::Str};
use super::*; use super::*;
type ServerInfo = crate::ServerInfo<Box<[u8]>>; use crate::{cursor::CursorMut, wrappers::Str};
macro_rules! tests { macro_rules! tests {
($($name:ident$(($($predefined_f:ident: $predefined_v:expr),+ $(,)?))? { ($($name:ident$(($($predefined_f:ident: $predefined_v:expr),+ $(,)?))? {
@ -454,6 +457,17 @@ mod tests {
.pos(); .pos();
assert_eq!(&buf[..n], b"0.19.3"); assert_eq!(&buf[..n], b"0.19.3");
} }
}
#[cfg(all(test, feature = "net"))]
mod match_tests {
use std::net::SocketAddr;
use crate::{cursor::CursorMut, wrappers::Str};
use super::*;
type ServerInfo = crate::ServerInfo<Box<[u8]>>;
macro_rules! servers { macro_rules! servers {
($($addr:expr => $info:expr $(=> $func:expr)?)+) => ( ($($addr:expr => $info:expr $(=> $func:expr)?)+) => (

13
protocol/src/lib.rs

@ -15,16 +15,19 @@ extern crate alloc;
extern crate log; extern crate log;
mod cursor; mod cursor;
mod server_info;
pub mod admin; #[cfg(feature = "net")]
pub mod net;
pub mod color; pub mod color;
pub mod filter; pub mod filter;
pub mod game; pub mod server_info;
pub mod master;
pub mod server;
pub mod wrappers; pub mod wrappers;
#[deprecated(since = "0.2.1", note = "use net module instead")]
#[cfg(feature = "net")]
pub use crate::net::{admin, game, master, server};
use core::fmt; use core::fmt;
pub use cursor::CursorError; pub use cursor::CursorError;

6
protocol/src/net.rs

@ -0,0 +1,6 @@
//! Network packets decoders and encoders.
pub mod admin;
pub mod game;
pub mod master;
pub mod server;

0
protocol/src/admin.rs → protocol/src/net/admin.rs

10
protocol/src/game.rs → protocol/src/net/game.rs

@ -5,10 +5,12 @@
use core::{fmt, net::SocketAddr}; use core::{fmt, net::SocketAddr};
use crate::cursor::{Cursor, CursorMut}; use crate::{
use crate::filter::Filter; cursor::{Cursor, CursorMut},
use crate::server::Region; filter::Filter,
use crate::Error; net::server::Region,
Error,
};
/// Request a list of server addresses from master servers. /// Request a list of server addresses from master servers.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]

6
protocol/src/master.rs → protocol/src/net/master.rs

@ -5,8 +5,10 @@
use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use super::cursor::{Cursor, CursorMut}; use crate::{
use super::Error; cursor::{Cursor, CursorMut},
Error,
};
/// Master server challenge response packet. /// Master server challenge response packet.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]

159
protocol/src/server.rs → protocol/src/net/server.rs

@ -5,12 +5,21 @@
use core::fmt; use core::fmt;
use bitflags::bitflags; use crate::{
cursor::{Cursor, CursorMut, GetKeyValue, PutKeyValue},
filter::Version,
wrappers::Str,
{CursorError, Error},
};
use super::cursor::{Cursor, CursorMut, GetKeyValue, PutKeyValue}; #[deprecated(since = "0.2.1", note = "use server_info::Region instead")]
use super::filter::Version; pub use crate::server_info::Region;
use super::wrappers::Str;
use super::{CursorError, Error}; #[deprecated(since = "0.2.1", note = "use server_info::ServerType instead")]
pub use crate::server_info::ServerType;
#[deprecated(since = "0.2.1", note = "use server_info::ServerFlags instead")]
pub use crate::server_info::ServerFlags;
/// Sended to a master server before `ServerAdd` packet. /// Sended to a master server before `ServerAdd` packet.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -117,146 +126,6 @@ impl fmt::Display for Os {
} }
} }
/// Game server type.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum ServerType {
/// Dedicated server.
Dedicated,
/// Game client.
Local,
/// Spectator proxy.
Proxy,
/// Unknown.
Unknown,
}
impl Default for ServerType {
fn default() -> Self {
Self::Unknown
}
}
impl TryFrom<&[u8]> for ServerType {
type Error = CursorError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
match value {
b"d" => Ok(Self::Dedicated),
b"l" => Ok(Self::Local),
b"p" => Ok(Self::Proxy),
_ => Ok(Self::Unknown),
}
}
}
impl GetKeyValue<'_> for ServerType {
fn get_key_value(cur: &mut Cursor) -> Result<Self, CursorError> {
cur.get_key_value_raw()?.try_into()
}
}
impl PutKeyValue for ServerType {
fn put_key_value<'a, 'b>(
&self,
cur: &'b mut CursorMut<'a>,
) -> Result<&'b mut CursorMut<'a>, CursorError> {
match self {
Self::Dedicated => cur.put_str("d"),
Self::Local => cur.put_str("l"),
Self::Proxy => cur.put_str("p"),
Self::Unknown => cur.put_str("?"),
}
}
}
impl fmt::Display for ServerType {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use ServerType as E;
let s = match self {
E::Dedicated => "dedicated",
E::Local => "local",
E::Proxy => "proxy",
E::Unknown => "unknown",
};
write!(fmt, "{}", s)
}
}
/// The region of the world in which the server is located.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Region {
/// US East coast.
USEastCoast = 0x00,
/// US West coast.
USWestCoast = 0x01,
/// South America.
SouthAmerica = 0x02,
/// Europe.
Europe = 0x03,
/// Asia.
Asia = 0x04,
/// Australia.
Australia = 0x05,
/// Middle East.
MiddleEast = 0x06,
/// Africa.
Africa = 0x07,
/// Rest of the world.
RestOfTheWorld = 0xff,
}
impl Default for Region {
fn default() -> Self {
Self::RestOfTheWorld
}
}
impl TryFrom<u8> for Region {
type Error = CursorError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(Region::USEastCoast),
0x01 => Ok(Region::USWestCoast),
0x02 => Ok(Region::SouthAmerica),
0x03 => Ok(Region::Europe),
0x04 => Ok(Region::Asia),
0x05 => Ok(Region::Australia),
0x06 => Ok(Region::MiddleEast),
0x07 => Ok(Region::Africa),
0xff => Ok(Region::RestOfTheWorld),
_ => Err(CursorError::InvalidNumber),
}
}
}
impl GetKeyValue<'_> for Region {
fn get_key_value(cur: &mut Cursor) -> Result<Self, CursorError> {
cur.get_key_value::<u8>()?.try_into()
}
}
bitflags! {
/// Additional server flags.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct ServerFlags: u8 {
/// Server has bots.
const BOTS = 1 << 0;
/// Server is behind a password.
const PASSWORD = 1 << 1;
/// Server using anti-cheat.
const SECURE = 1 << 2;
/// Server is LAN.
const LAN = 1 << 3;
/// Server behind NAT.
const NAT = 1 << 4;
}
}
/// Add/update game server information on the master server. /// Add/update game server information on the master server.
#[derive(Clone, Debug, PartialEq, Default)] #[derive(Clone, Debug, PartialEq, Default)]
pub struct ServerAdd<T> { pub struct ServerAdd<T> {

134
protocol/src/server_info.rs

@ -1,12 +1,136 @@
// SPDX-License-Identifier: LGPL-3.0-only // SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com> // SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com>
#[cfg(feature = "alloc")] //! Server info structures used in filter.
use core::fmt;
#[cfg(all(feature = "alloc", feature = "net"))]
use alloc::boxed::Box; use alloc::boxed::Box;
use super::filter::{FilterFlags, Version}; use bitflags::bitflags;
use super::server::{Region, ServerAdd};
use super::wrappers::Str; use crate::{
cursor::CursorError,
filter::{FilterFlags, Version},
};
#[cfg(feature = "net")]
use crate::{net::server::ServerAdd, wrappers::Str};
/// The region of the world in which the server is located.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum Region {
/// US East coast.
USEastCoast = 0x00,
/// US West coast.
USWestCoast = 0x01,
/// South America.
SouthAmerica = 0x02,
/// Europe.
Europe = 0x03,
/// Asia.
Asia = 0x04,
/// Australia.
Australia = 0x05,
/// Middle East.
MiddleEast = 0x06,
/// Africa.
Africa = 0x07,
/// Rest of the world.
RestOfTheWorld = 0xff,
}
impl Default for Region {
fn default() -> Self {
Self::RestOfTheWorld
}
}
impl TryFrom<u8> for Region {
type Error = CursorError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(Region::USEastCoast),
0x01 => Ok(Region::USWestCoast),
0x02 => Ok(Region::SouthAmerica),
0x03 => Ok(Region::Europe),
0x04 => Ok(Region::Asia),
0x05 => Ok(Region::Australia),
0x06 => Ok(Region::MiddleEast),
0x07 => Ok(Region::Africa),
0xff => Ok(Region::RestOfTheWorld),
_ => Err(CursorError::InvalidNumber),
}
}
}
/// Game server type.
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum ServerType {
/// Dedicated server.
Dedicated,
/// Game client.
Local,
/// Spectator proxy.
Proxy,
/// Unknown.
Unknown,
}
impl Default for ServerType {
fn default() -> Self {
Self::Unknown
}
}
impl TryFrom<&[u8]> for ServerType {
type Error = CursorError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
match value {
b"d" => Ok(Self::Dedicated),
b"l" => Ok(Self::Local),
b"p" => Ok(Self::Proxy),
_ => Ok(Self::Unknown),
}
}
}
impl fmt::Display for ServerType {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use ServerType as E;
let s = match self {
E::Dedicated => "dedicated",
E::Local => "local",
E::Proxy => "proxy",
E::Unknown => "unknown",
};
write!(fmt, "{}", s)
}
}
bitflags! {
/// Additional server flags.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct ServerFlags: u8 {
/// Server has bots.
const BOTS = 1 << 0;
/// Server is behind a password.
const PASSWORD = 1 << 1;
/// Server using anti-cheat.
const SECURE = 1 << 2;
/// Server is LAN.
const LAN = 1 << 3;
/// Server behind NAT.
const NAT = 1 << 4;
}
}
/// Game server information. /// Game server information.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -25,6 +149,7 @@ pub struct ServerInfo<T> {
pub region: Region, pub region: Region,
} }
#[cfg(feature = "net")]
impl<'a> ServerInfo<&'a [u8]> { impl<'a> ServerInfo<&'a [u8]> {
/// Creates a new `ServerInfo`. /// Creates a new `ServerInfo`.
pub fn new(info: &ServerAdd<Str<&'a [u8]>>) -> Self { pub fn new(info: &ServerAdd<Str<&'a [u8]>>) -> Self {
@ -39,6 +164,7 @@ impl<'a> ServerInfo<&'a [u8]> {
} }
} }
#[cfg(feature = "net")]
#[cfg(any(feature = "alloc", test))] #[cfg(any(feature = "alloc", test))]
impl ServerInfo<Box<[u8]>> { impl ServerInfo<Box<[u8]>> {
/// Creates a new `ServerInfo`. /// Creates a new `ServerInfo`.

Loading…
Cancel
Save