initial commit

This commit is contained in:
Niklas Kapelle 2023-10-10 00:16:19 +02:00
commit ccc3c01f2a
Signed by: niklas
GPG Key ID: 4EB651B36D841D16
6 changed files with 1758 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

1503
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

9
Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "cs2_gsi"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = { version = "=0.5.0-rc.3", features = ["json"] }

View File

@ -0,0 +1,23 @@
"Observer All Players v.1"
{
"uri" "http://127.0.0.01:8000/gsi"
"timeout" "5.0"
"buffer" "0.1"
"throttle" "0.1"
"heartbeat" "30.0"
"auth"
{
"token" "Q79v5tcxVQ8u"
}
"data"
{
"map_round_wins" "1" // history of round wins
"map" "1" // mode, map, phase, team scores
"player_id" "1" // steamid
"player_match_stats" "1" // scoreboard info
"player_state" "1" // armor, flashed, equip_value, health, etc.
"player_weapons" "1" // list of player weapons and weapon state
"provider" "1" // info about the game providing info
"round" "1" // round phase and the winning team
}
}

123
src/gamestate.rs Normal file
View File

@ -0,0 +1,123 @@
use rocket::serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct GamestateData {
pub provider: Provider,
pub auth: Auth,
// pub map: Option<Map>,
pub round: Option<Round>,
pub player: Option<Player>,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Provider {
pub name: String,
pub appid: u16,
pub version: u16,
pub steamid: String,
pub timestamp: u64,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Auth {
pub token: String
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Map {
pub mode: String,
pub name: String,
pub phase: String,
pub round: u16,
pub team_ct: Team,
pub team_t: Team,
pub num_matches_to_win_series: u16,
pub current_spectators: u16,
pub souvenirs_total: u16,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Team {
pub score: u16,
pub consecutive_round_losses: u16,
pub timeouts_remaining: u16,
pub matches_won_this_series: u16,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Player {
pub clan: Option<String>,
pub spectarget: Option<String>,
pub steamid: String,
pub name: String,
pub activity: String,
// pub match_stats: MatchStats,
pub state: Option<State>,
// pub weapons: Weapons,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct MatchStats {
pub kills: u16,
pub assists: u16,
pub deaths: u16,
pub mvps: u16,
pub score: u16,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct State {
pub health: u16,
pub armor: u16,
pub helmet: bool,
pub flashed: u16,
pub smoked: u16,
pub burning: u16,
pub money: u16,
pub round_kills: u16,
pub round_killhs: u16,
pub equip_value: u16,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Weapons {
pub weapon_0: Weapon,
pub weapon_1: Weapon,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Weapon {
pub name: String,
pub paintkit: String,
pub type_: String,
pub ammo_clip: u16,
pub ammo_clip_max: u16,
pub ammo_reserve: u16,
pub state: String,
// pub ammo_type: String,
// pub stat_trak: u16,
// pub weapon_type: String,
// pub weapon_state: String,
}
#[derive(Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
pub struct Round {
pub phase: String,
pub bomb: Option<String>,
// pub win_team: String,
// pub bomb_planted: bool,
// pub round_wins: RoundWins,
}

99
src/main.rs Normal file
View File

@ -0,0 +1,99 @@
#[macro_use]
extern crate rocket;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU16;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use rocket::serde::json::{json, Json, Value};
use std::thread;
mod gamestate;
#[derive(Clone)]
struct State {
flashed: Arc<AtomicU16>,
bomb_planted: Arc<AtomicBool>,
}
impl State {
fn new() -> State {
State {
flashed: Arc::new(AtomicU16::new(0)),
bomb_planted: Arc::new(AtomicBool::new(false)),
}
}
}
#[post("/gsi", data = "<data>")]
fn gsi(data: Json<gamestate::GamestateData>, gobal_state: &rocket::State<State>) -> Value {
let flashed = match &data.player {
Some(player) => match &player.state {
Some(state) => state.flashed,
None => 0,
},
None => 0,
};
gobal_state.flashed.store(flashed, Ordering::Relaxed);
let planted = match &data.round {
Some(round) => match &round.bomb {
Some(bomb) => match bomb.as_str() {
"planted" => true,
_ => false,
},
None => false,
},
None => false,
};
gobal_state.bomb_planted.store(planted, Ordering::Relaxed);
json!({ "status": "ok"})
}
fn build_led_package(len: u32, color: (u8, u8, u8)) -> Vec<u8> {
let mut packet: Vec<u8> = vec![1, 1];
// loop through leds
for i in 0..len {
packet.push(i as u8);
packet.push(color.0);
packet.push(color.1);
packet.push(color.2);
}
return packet;
}
#[launch]
fn rocket() -> _ {
let state: State = State::new();
let state_clone = state.clone();
let addr = "192.168.0.59:21324";
thread::spawn(move || {
let socket = std::net::UdpSocket::bind("0.0.0.0:0").unwrap();
loop {
thread::sleep(std::time::Duration::from_millis(50));
let flashed = u8::try_from(state_clone.flashed.load(Ordering::Relaxed)).unwrap_or(0);
if flashed > 0 {
let packet = build_led_package(36, (flashed, flashed, flashed));
socket.send_to(&packet, addr).unwrap();
continue;
}
let planted = state_clone.bomb_planted.load(Ordering::Relaxed);
if planted {
let packet = build_led_package(36, (255, 0, 0));
socket.send_to(&packet, addr).unwrap();
continue;
}
}
});
rocket::build().manage(state).mount("/", routes![gsi])
}