diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index 2bbee0e..cc3ceb6 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -11,6 +11,11 @@ rust-version = "1.56" homepage = "https://xash.su" repository = "https://git.mentality.rip/numas13/xash3d-master" +[features] +default = ["std"] +std = ["alloc"] +alloc = [] + [dependencies] log = "0.4.18" bitflags = "2.4" diff --git a/protocol/src/color.rs b/protocol/src/color.rs index 95c977b..c46d1cc 100644 --- a/protocol/src/color.rs +++ b/protocol/src/color.rs @@ -3,7 +3,8 @@ //! Color codes for strings. -use std::borrow::Cow; +#[cfg(feature = "alloc")] +use alloc::{borrow::Cow, string::String}; /// Color codes `^digit`. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -131,6 +132,7 @@ impl<'a> Iterator for ColorIter<'a> { /// # use xash3d_protocol::color::trim_color; /// assert_eq!(trim_color("^1no^7 ^2colors^7"), "no colors"); /// ``` +#[cfg(feature = "alloc")] pub fn trim_color(s: &str) -> Cow<'_, str> { let (_, s) = trim_start_color(s); if !s.chars().any(|c| c == '^') { @@ -157,6 +159,7 @@ mod tests { assert_eq!(trim_start_color("^1^2^3foo^2bar"), ("^3", "foo^2bar")); } + #[cfg(feature = "alloc")] #[test] fn trim_colors() { assert_eq!(trim_color("foo^2bar"), "foobar"); diff --git a/protocol/src/cursor.rs b/protocol/src/cursor.rs index 1902064..f0aeafb 100644 --- a/protocol/src/cursor.rs +++ b/protocol/src/cursor.rs @@ -1,11 +1,14 @@ // SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: 2023 Denis Drakhnia -use std::{ +use core::{ fmt::{self, Write}, mem, str, }; +#[cfg(feature = "alloc")] +use alloc::{borrow::ToOwned, boxed::Box, string::String}; + use super::color; use super::wrappers::Str; @@ -49,9 +52,9 @@ impl fmt::Display for CursorError { } } -impl std::error::Error for CursorError {} +impl core::error::Error for CursorError {} -pub type Result = std::result::Result; +pub type Result = core::result::Result; pub trait GetKeyValue<'a>: Sized { fn get_key_value(cur: &mut Cursor<'a>) -> Result; @@ -76,6 +79,7 @@ impl<'a> GetKeyValue<'a> for &'a str { } } +#[cfg(feature = "alloc")] impl<'a> GetKeyValue<'a> for Box { fn get_key_value(cur: &mut Cursor<'a>) -> Result { let raw = cur.get_key_value_raw()?; @@ -85,6 +89,7 @@ impl<'a> GetKeyValue<'a> for Box { } } +#[cfg(feature = "alloc")] impl<'a> GetKeyValue<'a> for String { fn get_key_value(cur: &mut Cursor<'a>) -> Result { let raw = cur.get_key_value_raw()?; diff --git a/protocol/src/filter.rs b/protocol/src/filter.rs index 135dc9e..3235d2d 100644 --- a/protocol/src/filter.rs +++ b/protocol/src/filter.rs @@ -29,16 +29,16 @@ //! * Do not have bots //! * Is not protected by a password -use std::fmt; -use std::net::SocketAddr; -use std::str::FromStr; +use core::{fmt, net::SocketAddr, str::FromStr}; use bitflags::bitflags; -use crate::cursor::{Cursor, GetKeyValue, PutKeyValue}; -use crate::server::{ServerAdd, ServerFlags, ServerType}; -use crate::wrappers::Str; -use crate::{CursorError, Error, ServerInfo}; +use crate::{ + cursor::{Cursor, GetKeyValue, PutKeyValue}, + server::{ServerAdd, ServerFlags, ServerType}, + wrappers::Str, + ServerInfo, {CursorError, Error}, +}; bitflags! { /// Additional filter flags. @@ -196,11 +196,14 @@ impl Filter<'_> { } /// Returns `true` if a server matches the filter. - pub fn matches(&self, _addr: SocketAddr, info: &ServerInfo) -> bool { + pub fn matches(&self, _addr: SocketAddr, info: &ServerInfo) -> bool + where + T: AsRef<[u8]>, + { // TODO: match addr !((info.flags & self.flags_mask) != self.flags - || self.gamedir.map_or(false, |s| *s != &*info.gamedir) - || self.map.map_or(false, |s| *s != &*info.map) + || self.gamedir.map_or(false, |s| *s != info.gamedir.as_ref()) + || self.map.map_or(false, |s| *s != info.map.as_ref()) || self.protocol.map_or(false, |s| s != info.protocol)) } } @@ -306,11 +309,14 @@ impl fmt::Display for &Filter<'_> { #[cfg(test)] mod tests { - use super::*; - use crate::cursor::CursorMut; - use crate::wrappers::Str; use std::net::SocketAddr; + use crate::{cursor::CursorMut, wrappers::Str}; + + use super::*; + + type ServerInfo = crate::ServerInfo>; + macro_rules! tests { ($($name:ident$(($($predefined_f:ident: $predefined_v:expr),+ $(,)?))? { $($src:expr => { diff --git a/protocol/src/game.rs b/protocol/src/game.rs index 1d4819d..b0b7f12 100644 --- a/protocol/src/game.rs +++ b/protocol/src/game.rs @@ -3,8 +3,7 @@ //! Game client packets. -use std::fmt; -use std::net::SocketAddr; +use core::{fmt, net::SocketAddr}; use crate::cursor::{Cursor, CursorMut}; use crate::filter::Filter; @@ -129,10 +128,14 @@ impl<'a> Packet<'a> { #[cfg(test)] mod tests { use super::*; - use crate::filter::{FilterFlags, Version}; - use crate::wrappers::Str; + use std::net::{IpAddr, Ipv4Addr}; + use crate::{ + filter::{FilterFlags, Version}, + wrappers::Str, + }; + #[test] fn query_servers() { let p = QueryServers { diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 0fd2dfe..edd3adf 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -6,6 +6,11 @@ //! Xash3D protocol between clients, servers and masters. +#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] + +#[cfg(feature = "alloc")] +extern crate alloc; + #[macro_use] extern crate log; @@ -20,7 +25,7 @@ pub mod master; pub mod server; pub mod wrappers; -use std::fmt; +use core::fmt; pub use cursor::CursorError; pub use server_info::ServerInfo; @@ -76,7 +81,7 @@ impl fmt::Display for Error { } } -impl std::error::Error for Error {} +impl core::error::Error for Error {} impl From for Error { fn from(source: CursorError) -> Self { diff --git a/protocol/src/master.rs b/protocol/src/master.rs index 7af6c9e..8263336 100644 --- a/protocol/src/master.rs +++ b/protocol/src/master.rs @@ -3,7 +3,7 @@ //! Master server packets. -use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use super::cursor::{Cursor, CursorMut}; use super::Error; @@ -143,7 +143,7 @@ impl<'a> QueryServersResponse<&'a [u8]> { A: ServerAddress, { let mut cur = Cursor::new(self.inner); - std::iter::from_fn(move || { + core::iter::from_fn(move || { if cur.remaining() == A::size() && cur.end().ends_with(&[0; 2]) { // skip last address with port 0 return None; diff --git a/protocol/src/server.rs b/protocol/src/server.rs index 231d7b4..bbd7421 100644 --- a/protocol/src/server.rs +++ b/protocol/src/server.rs @@ -3,7 +3,7 @@ //! Game server packets. -use std::fmt; +use core::fmt; use bitflags::bitflags; diff --git a/protocol/src/server_info.rs b/protocol/src/server_info.rs index fdb163b..73be80b 100644 --- a/protocol/src/server_info.rs +++ b/protocol/src/server_info.rs @@ -1,28 +1,46 @@ // SPDX-License-Identifier: LGPL-3.0-only // SPDX-FileCopyrightText: 2023 Denis Drakhnia +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + use super::filter::{FilterFlags, Version}; use super::server::{Region, ServerAdd}; use super::wrappers::Str; /// Game server information. #[derive(Clone, Debug)] -pub struct ServerInfo { +pub struct ServerInfo { /// Server version. pub version: Version, /// Server protocol version. pub protocol: u8, /// Server midification. - pub gamedir: Box<[u8]>, + pub gamedir: T, /// Server map. - pub map: Box<[u8]>, + pub map: T, /// Server additional filter flags. pub flags: FilterFlags, /// Server region. pub region: Region, } -impl ServerInfo { +impl<'a> ServerInfo<&'a [u8]> { + /// Creates a new `ServerInfo`. + pub fn new(info: &ServerAdd>) -> Self { + Self { + version: info.version, + protocol: info.protocol, + gamedir: &info.gamedir, + map: &info.map, + flags: FilterFlags::from(info), + region: info.region, + } + } +} + +#[cfg(any(feature = "alloc", test))] +impl ServerInfo> { /// Creates a new `ServerInfo`. pub fn new(info: &ServerAdd>) -> Self { Self { diff --git a/protocol/src/wrappers.rs b/protocol/src/wrappers.rs index 4a57e67..cd11187 100644 --- a/protocol/src/wrappers.rs +++ b/protocol/src/wrappers.rs @@ -3,11 +3,9 @@ //! Wrappers for byte slices with pretty-printers. -use std::fmt; -use std::ops::Deref; +use core::{fmt, ops::Deref}; -use crate::cursor::{CursorMut, PutKeyValue}; -use crate::CursorError; +use crate::cursor::{CursorError, CursorMut, PutKeyValue}; /// Wrapper for slice of bytes with printing the bytes as a string. ///