a2s/
players.rs

1use std::io::Cursor;
2#[cfg(not(feature = "async"))]
3use std::net::ToSocketAddrs;
4
5#[cfg(feature = "async")]
6use tokio::net::ToSocketAddrs;
7
8use byteorder::{LittleEndian, ReadBytesExt};
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13use crate::errors::{Error, Result};
14use crate::{A2SClient, ReadCString};
15
16const PLAYER_REQUEST: [u8; 5] = [0xff, 0xff, 0xff, 0xff, 0x55];
17
18#[derive(Debug, Clone)]
19#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
20pub struct Player {
21    /// Index of player chunk starting from 0.
22    /// This seems to be always 0?
23    pub index: u8,
24
25    /// Name of the player.
26    pub name: String,
27
28    /// Player's score (usually "frags" or "kills".)
29    pub score: i32,
30
31    /// Time (in seconds) player has been connected to the server.
32    pub duration: f32,
33
34    /// The Ship additional player info
35    pub the_ship: Option<TheShipPlayer>,
36}
37
38#[derive(Debug, Clone)]
39#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
40pub struct TheShipPlayer {
41    pub deaths: u32,
42
43    pub money: u32,
44}
45
46impl Player {
47    pub fn from_cursor(mut data: Cursor<Vec<u8>>, app_id: u16) -> Result<Vec<Self>> {
48        if data.read_u8()? != 0x44 {
49            return Err(Error::InvalidResponse);
50        }
51
52        let player_count = data.read_u8()?;
53
54        let mut players: Vec<Self> = Vec::with_capacity(player_count as usize);
55
56        for _ in 0..player_count {
57            players.push(Self {
58                index: data.read_u8()?,
59                name: data.read_cstring()?,
60                score: data.read_i32::<LittleEndian>()?,
61                duration: data.read_f32::<LittleEndian>()?,
62                the_ship: {
63                    if app_id == 2400 {
64                        Some(TheShipPlayer {
65                            deaths: data.read_u32::<LittleEndian>()?,
66                            money: data.read_u32::<LittleEndian>()?,
67                        })
68                    } else {
69                        None
70                    }
71                },
72            })
73        }
74
75        Ok(players)
76    }
77}
78
79impl A2SClient {
80    #[cfg(feature = "async")]
81    pub async fn players<A: ToSocketAddrs>(&self, addr: A) -> Result<Vec<Player>> {
82        let data = self.do_challenge_request(addr, &PLAYER_REQUEST).await?;
83        Player::from_cursor(Cursor::new(data), self.app_id)
84    }
85
86    #[cfg(not(feature = "async"))]
87    pub fn players<A: ToSocketAddrs>(&self, addr: A) -> Result<Vec<Player>> {
88        let data = self.do_challenge_request(addr, &PLAYER_REQUEST)?;
89        Player::from_cursor(Cursor::new(data), self.app_id)
90    }
91}