Skip to main content

cs2_gsi/model/
player.rs

1//! Player node — both the local observer and entries inside `allplayers`.
2
3use super::helpers::{de_num_or_str, de_opt_num_or_str};
4use serde::{Deserialize, Serialize};
5use std::collections::BTreeMap;
6
7/// A single player snapshot.
8#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
9pub struct Player {
10    /// 64-bit SteamID. Empty for bots.
11    #[serde(default)]
12    pub steamid: String,
13    /// In-game display name.
14    #[serde(default)]
15    pub name: String,
16    /// Clan tag, may be empty.
17    #[serde(default)]
18    pub clan: String,
19    /// Observer slot number (0..=9 in standard 5v5).
20    #[serde(default, deserialize_with = "de_opt_num_or_str")]
21    pub observer_slot: Option<u8>,
22    /// XP overload level (CS2 progression).
23    #[serde(default, deserialize_with = "de_opt_num_or_str")]
24    pub xp_overload_level: Option<u32>,
25    /// Side the player belongs to.
26    #[serde(default)]
27    pub team: PlayerTeam,
28    /// Activity (menu / playing / textinput).
29    #[serde(default)]
30    pub activity: PlayerActivity,
31    /// Volatile per-tick state (health, armor, money, …).
32    #[serde(default)]
33    pub state: PlayerState,
34    /// Per-match aggregate statistics.
35    #[serde(default, rename = "match_stats")]
36    pub match_stats: MatchStats,
37    /// Inventory keyed by weapon slot id (e.g. `"weapon_0"`).
38    #[serde(default)]
39    pub weapons: BTreeMap<String, Weapon>,
40    /// SteamID of the player currently being spectated (when `activity = playing` is false).
41    #[serde(default)]
42    pub spectarget: String,
43    /// World position vector, formatted by CS2 as `"x, y, z"`.
44    #[serde(default)]
45    pub position: String,
46    /// Forward look direction vector, formatted by CS2 as `"x, y, z"`.
47    #[serde(default)]
48    pub forward: String,
49}
50
51/// Side the player is on.
52#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq, Hash)]
53pub enum PlayerTeam {
54    /// Counter-Terrorists.
55    #[serde(alias = "ct", alias = "CT")]
56    CT,
57    /// Terrorists.
58    #[serde(alias = "t", alias = "T")]
59    T,
60    /// Player has not joined a side yet.
61    #[serde(other)]
62    #[default]
63    Unassigned,
64}
65
66/// What the player is currently doing.
67#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq, Hash)]
68#[serde(rename_all = "lowercase")]
69pub enum PlayerActivity {
70    /// Player is in a menu (e.g. team / loadout selection).
71    Menu,
72    /// Player is alive in-game.
73    Playing,
74    /// Player is typing in chat.
75    Textinput,
76    /// Unrecognized activity.
77    #[serde(other)]
78    #[default]
79    Unknown,
80}
81
82/// Volatile per-tick state. Mirrors `player_state` block in the GSI cfg.
83#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
84pub struct PlayerState {
85    /// HP (0..=100). 0 means dead.
86    #[serde(default, deserialize_with = "de_num_or_str")]
87    pub health: i32,
88    /// Armor value (0..=100).
89    #[serde(default, deserialize_with = "de_num_or_str")]
90    pub armor: i32,
91    /// Whether the player has a helmet.
92    #[serde(default)]
93    pub helmet: bool,
94    /// Whether the player carries a defuse kit (CT only).
95    #[serde(default)]
96    pub defusekit: bool,
97    /// Flash blindness level, 0..=255.
98    #[serde(default, deserialize_with = "de_num_or_str")]
99    pub flashed: i32,
100    /// Smoke obscurance level, 0..=255.
101    #[serde(default, deserialize_with = "de_num_or_str")]
102    pub smoked: i32,
103    /// Molotov burning level, 0..=255.
104    #[serde(default, deserialize_with = "de_num_or_str")]
105    pub burning: i32,
106    /// Cash on hand.
107    #[serde(default, deserialize_with = "de_num_or_str")]
108    pub money: i32,
109    /// Kills accumulated in the current round.
110    #[serde(default, deserialize_with = "de_num_or_str")]
111    pub round_kills: i32,
112    /// Headshot kills accumulated in the current round.
113    #[serde(default, deserialize_with = "de_num_or_str")]
114    pub round_killhs: i32,
115    /// Total damage dealt in the current round.
116    #[serde(default, deserialize_with = "de_num_or_str")]
117    pub round_totaldmg: i32,
118    /// Total equipment value carried.
119    #[serde(default, deserialize_with = "de_num_or_str")]
120    pub equip_value: i32,
121}
122
123/// Per-match aggregate stats. Mirrors `player_match_stats`.
124#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
125pub struct MatchStats {
126    /// Match kills.
127    #[serde(default, deserialize_with = "de_num_or_str")]
128    pub kills: i32,
129    /// Match assists.
130    #[serde(default, deserialize_with = "de_num_or_str")]
131    pub assists: i32,
132    /// Match deaths.
133    #[serde(default, deserialize_with = "de_num_or_str")]
134    pub deaths: i32,
135    /// Round-MVP awards earned this match.
136    #[serde(default, deserialize_with = "de_num_or_str")]
137    pub mvps: i32,
138    /// Match score.
139    #[serde(default, deserialize_with = "de_num_or_str")]
140    pub score: i32,
141}
142
143/// A single weapon slot entry. Slot key is e.g. `"weapon_0"`.
144#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
145pub struct Weapon {
146    /// Weapon class name, e.g. `"weapon_ak47"`.
147    #[serde(default)]
148    pub name: String,
149    /// Paint-kit identifier (cosmetics).
150    #[serde(default, deserialize_with = "de_opt_num_or_str")]
151    pub paintkit: Option<String>,
152    /// Weapon category.
153    #[serde(default, rename = "type")]
154    pub kind: WeaponKind,
155    /// Rounds in the current magazine.
156    #[serde(default, deserialize_with = "de_opt_num_or_str")]
157    pub ammo_clip: Option<i32>,
158    /// Magazine capacity.
159    #[serde(default, deserialize_with = "de_opt_num_or_str")]
160    pub ammo_clip_max: Option<i32>,
161    /// Reserve ammo carried.
162    #[serde(default, deserialize_with = "de_opt_num_or_str")]
163    pub ammo_reserve: Option<i32>,
164    /// Whether the weapon is `holstered`, `active`, or `reloading`.
165    #[serde(default)]
166    pub state: WeaponState,
167}
168
169/// Weapon category as reported by CS2.
170#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq, Hash)]
171pub enum WeaponKind {
172    /// Knife.
173    #[serde(alias = "Knife")]
174    Knife,
175    /// Pistol.
176    #[serde(alias = "Pistol")]
177    Pistol,
178    /// Submachine gun.
179    #[serde(alias = "Submachine Gun", alias = "SubmachineGun")]
180    SubmachineGun,
181    /// Rifle.
182    #[serde(alias = "Rifle")]
183    Rifle,
184    /// Sniper rifle.
185    #[serde(alias = "SniperRifle", alias = "Sniper Rifle")]
186    SniperRifle,
187    /// Shotgun.
188    #[serde(alias = "Shotgun")]
189    Shotgun,
190    /// Heavy / machine gun.
191    #[serde(alias = "Machine Gun", alias = "MachineGun")]
192    MachineGun,
193    /// Grenade.
194    #[serde(alias = "Grenade")]
195    Grenade,
196    /// C4 explosive.
197    #[serde(alias = "C4")]
198    C4,
199    /// Taser / Zeus.
200    #[serde(alias = "StackableItem")]
201    StackableItem,
202    /// Unrecognized / future weapon class.
203    #[serde(other)]
204    #[default]
205    Unknown,
206}
207
208/// Weapon slot state.
209#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq, Hash)]
210#[serde(rename_all = "lowercase")]
211pub enum WeaponState {
212    /// In inventory but not equipped.
213    Holstered,
214    /// Currently equipped.
215    Active,
216    /// Reloading.
217    Reloading,
218    /// Unrecognized state.
219    #[serde(other)]
220    #[default]
221    Unknown,
222}