385 lines
12 KiB
Rust
385 lines
12 KiB
Rust
use crate::{csgo_proto, RawValue, UserId};
|
|
|
|
macro_rules! define_event {
|
|
($name:ident, $target:path $(, ($field:ident, $field_ty:ty))*) => {
|
|
#[derive(Debug, PartialEq)]
|
|
#[allow(dead_code)]
|
|
pub struct $name {
|
|
$(pub $field: Option<$field_ty>,)*
|
|
pub remaining: ::std::collections::HashMap<String, crate::csgo_proto::c_msg_source1_legacy_game_event::KeyT>,
|
|
}
|
|
|
|
impl $name {
|
|
#[allow(unused_mut)]
|
|
fn parse(keys: &[crate::csgo_proto::csvc_msg_game_event_list::KeyT], event: crate::csgo_proto::CMsgSource1LegacyGameEvent) -> Result<GameEvent, ParseGameEventError> {
|
|
|
|
$(let mut $field: Option<RawValue> = None;)*
|
|
let mut remaining = std::collections::HashMap::new();
|
|
|
|
for (k, f) in keys.iter().zip(event.keys.into_iter()) {
|
|
let name = k.name();
|
|
$(
|
|
if name == stringify!($field) {
|
|
$field = Some(f.try_into().ok()).flatten();
|
|
continue;
|
|
}
|
|
)*
|
|
|
|
remaining.insert(name.to_owned(), f);
|
|
}
|
|
|
|
let value = $name {
|
|
$($field: $field.map(|f| f.try_into().ok()).flatten(),)*
|
|
remaining,
|
|
};
|
|
|
|
Ok($target(value))
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
define_event!(HltvVersionInfo, GameEvent::HltvVersionInfo);
|
|
|
|
define_event!(
|
|
ItemEquip,
|
|
GameEvent::ItemEquip,
|
|
(userid, UserId),
|
|
(hassilencer, bool),
|
|
(hastracers, bool),
|
|
(item, String),
|
|
(issilenced, bool),
|
|
(canzoom, bool),
|
|
(ispainted, bool),
|
|
(weptype, i32),
|
|
(defindex, i32)
|
|
);
|
|
define_event!(
|
|
ItemPickup,
|
|
GameEvent::ItemPickup,
|
|
(userid, UserId),
|
|
(item, RawValue),
|
|
(silent, RawValue),
|
|
(defindex, RawValue)
|
|
);
|
|
|
|
define_event!(
|
|
WeaponReload,
|
|
GameEvent::WeaponReload,
|
|
(userid, UserId),
|
|
(userid_pawn, RawValue)
|
|
);
|
|
define_event!(
|
|
WeaponZoom,
|
|
GameEvent::WeaponZoom,
|
|
(userid, UserId),
|
|
(userid_pawn, RawValue)
|
|
);
|
|
define_event!(
|
|
WeaponFire,
|
|
GameEvent::WeaponFire,
|
|
(userid, UserId),
|
|
(weapon, RawValue),
|
|
(silenced, RawValue),
|
|
(userid_pawn, RawValue)
|
|
);
|
|
|
|
define_event!(SmokeGrenadeDetonate, GameEvent::SmokeGrenadeDetonate);
|
|
define_event!(SmokeGrenadeExpired, GameEvent::SmokeGrenadeExpired);
|
|
define_event!(HEGrenadeDetonate, GameEvent::HEGrenadeDetonate);
|
|
define_event!(InfernoStartBurn, GameEvent::InfernoStartBurn);
|
|
define_event!(InfernoExpire, GameEvent::InfernoExpire);
|
|
define_event!(FlashbangDetonate, GameEvent::FlashbangDetonate);
|
|
define_event!(DecoyStarted, GameEvent::DecoyStarted);
|
|
define_event!(DecoyDetonate, GameEvent::DecoyDetonate);
|
|
|
|
define_event!(
|
|
PlayerConnect,
|
|
GameEvent::PlayerConnect,
|
|
(address, RawValue),
|
|
(bot, RawValue),
|
|
(name, RawValue),
|
|
(userid, i32),
|
|
(networkid, RawValue),
|
|
(xuid, RawValue)
|
|
);
|
|
define_event!(
|
|
PlayerConnectFull,
|
|
GameEvent::PlayerConnectFull,
|
|
(userid, UserId)
|
|
);
|
|
define_event!(
|
|
PlayerDisconnect,
|
|
GameEvent::PlayerDisconnect,
|
|
(userid, UserId),
|
|
(reason, RawValue),
|
|
(name, RawValue),
|
|
(networkid, RawValue),
|
|
(xuid, RawValue)
|
|
);
|
|
define_event!(
|
|
PlayerFootstep,
|
|
GameEvent::PlayerFootstep,
|
|
(userid, UserId),
|
|
(userid_pawn, RawValue)
|
|
);
|
|
define_event!(PlayerJump, GameEvent::PlayerJump, (userid, i32));
|
|
define_event!(
|
|
PlayerHurt,
|
|
GameEvent::PlayerHurt,
|
|
(userid, UserId),
|
|
(attacker, UserId),
|
|
(health, RawValue),
|
|
(armor, RawValue),
|
|
(weapon, String),
|
|
(dmg_health, RawValue),
|
|
(dmg_armor, RawValue),
|
|
(hitgroup, RawValue),
|
|
(userid_pawn, RawValue),
|
|
(attacker_pawn, RawValue)
|
|
);
|
|
define_event!(
|
|
PlayerDeath,
|
|
GameEvent::PlayerDeath,
|
|
(userid, UserId),
|
|
(attacker, UserId),
|
|
(assister, UserId),
|
|
(assistedflash, bool),
|
|
(weapon, String),
|
|
(weapon_itemid, String),
|
|
(weapon_fauxitemid, String),
|
|
(weapon_originalowner_xuid, String),
|
|
(headshot, bool),
|
|
(dominated, RawValue),
|
|
(revenge, RawValue),
|
|
(wipe, RawValue),
|
|
(penetrated, RawValue),
|
|
(noreplay, bool),
|
|
(noscope, bool),
|
|
(thrusmoke, bool),
|
|
(attackerblind, bool),
|
|
(distance, RawValue),
|
|
(userid_pawn, RawValue),
|
|
(attacker_pawn, RawValue),
|
|
(assister_pawn, RawValue),
|
|
(dmg_health, RawValue),
|
|
(dmg_armor, RawValue),
|
|
(hitgroup, RawValue),
|
|
(attackerinair, bool)
|
|
);
|
|
define_event!(
|
|
PlayerSpawn,
|
|
GameEvent::PlayerSpawn,
|
|
(userid, UserId),
|
|
(inrestart, RawValue),
|
|
(userid_pawn, RawValue)
|
|
);
|
|
define_event!(
|
|
PlayerBlind,
|
|
GameEvent::PlayerBlind,
|
|
(userid, UserId),
|
|
(attacker, RawValue),
|
|
(entityid, RawValue),
|
|
(blind_duration, RawValue)
|
|
);
|
|
define_event!(
|
|
PlayerTeam,
|
|
GameEvent::PlayerTeam,
|
|
(userid, UserId),
|
|
(team, RawValue),
|
|
(oldteam, RawValue),
|
|
(disconnect, RawValue),
|
|
(silent, RawValue),
|
|
(isbot, RawValue),
|
|
(userid_pawn, RawValue)
|
|
);
|
|
|
|
define_event!(BulletDamage, GameEvent::BulletDamage);
|
|
|
|
define_event!(OtherDeath, GameEvent::OtherDeath);
|
|
|
|
define_event!(BombPickup, GameEvent::BombPickup);
|
|
define_event!(BombDropped, GameEvent::BombDropped);
|
|
define_event!(BombBeginPlant, GameEvent::BombBeginPlant);
|
|
define_event!(BombPlanted, GameEvent::BombPlanted);
|
|
define_event!(BombExploded, GameEvent::BombExploded);
|
|
define_event!(BombBeginDefuse, GameEvent::BombBeginDefuse);
|
|
define_event!(BombDefused, GameEvent::BombDefused);
|
|
|
|
define_event!(BeginNewMatch, GameEvent::BeginNewMatch);
|
|
define_event!(RoundAnnounceMatchStart, GameEvent::RoundAnnounceMatchStart);
|
|
define_event!(RoundFreezeEnd, GameEvent::RoundFreezeEnd);
|
|
define_event!(RoundPreStart, GameEvent::RoundPreStart);
|
|
define_event!(RoundPostStart, GameEvent::RoundPostStart);
|
|
define_event!(RoundOfficiallyEnded, GameEvent::RoundOfficiallyEnded);
|
|
define_event!(RoundStartBeep, GameEvent::RoundStartBeep);
|
|
define_event!(RoundAnnounceMatchpoint, GameEvent::RoundAnnounceMatchpoint);
|
|
define_event!(RoundPreRestart, GameEvent::RoundPreRestart);
|
|
define_event!(RoundTimeWarning, GameEvent::RoundTimeWarning);
|
|
define_event!(RoundFinalBeep, GameEvent::RoundFinalBeep);
|
|
define_event!(BuyTimeEnded, GameEvent::BuyTimeEnded);
|
|
define_event!(
|
|
RoundAnnounceLastRoundHalf,
|
|
GameEvent::RoundAnnounceLastRoundHalf
|
|
);
|
|
define_event!(AnnouncePhaseEnd, GameEvent::AnnouncePhaseEnd);
|
|
|
|
define_event!(WinPanelMatch, GameEvent::WinPanelMatch);
|
|
|
|
type ParseFn = fn(
|
|
keys: &[csgo_proto::csvc_msg_game_event_list::KeyT],
|
|
event: csgo_proto::CMsgSource1LegacyGameEvent,
|
|
) -> Result<GameEvent, ParseGameEventError>;
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
#[allow(dead_code)]
|
|
pub enum GameEvent {
|
|
HltvVersionInfo(HltvVersionInfo),
|
|
//
|
|
ItemEquip(ItemEquip),
|
|
ItemPickup(ItemPickup),
|
|
//
|
|
WeaponReload(WeaponReload),
|
|
WeaponZoom(WeaponZoom),
|
|
WeaponFire(WeaponFire),
|
|
//
|
|
SmokeGrenadeDetonate(SmokeGrenadeDetonate),
|
|
SmokeGrenadeExpired(SmokeGrenadeExpired),
|
|
HEGrenadeDetonate(HEGrenadeDetonate),
|
|
InfernoStartBurn(InfernoStartBurn),
|
|
InfernoExpire(InfernoExpire),
|
|
FlashbangDetonate(FlashbangDetonate),
|
|
DecoyStarted(DecoyStarted),
|
|
DecoyDetonate(DecoyDetonate),
|
|
//
|
|
PlayerConnect(PlayerConnect),
|
|
PlayerConnectFull(PlayerConnectFull),
|
|
PlayerDisconnect(PlayerDisconnect),
|
|
PlayerFootstep(PlayerFootstep),
|
|
PlayerJump(PlayerJump),
|
|
PlayerHurt(PlayerHurt),
|
|
PlayerDeath(PlayerDeath),
|
|
PlayerSpawn(PlayerSpawn),
|
|
PlayerBlind(PlayerBlind),
|
|
PlayerTeam(PlayerTeam),
|
|
//
|
|
BulletDamage(BulletDamage),
|
|
//
|
|
OtherDeath(OtherDeath),
|
|
//
|
|
BombPickup(BombPickup),
|
|
BombDropped(BombDropped),
|
|
BombBeginPlant(BombBeginPlant),
|
|
BombPlanted(BombPlanted),
|
|
BombExploded(BombExploded),
|
|
BombBeginDefuse(BombBeginDefuse),
|
|
BombDefused(BombDefused),
|
|
//
|
|
BeginNewMatch(BeginNewMatch),
|
|
RoundAnnounceMatchStart(RoundAnnounceMatchStart),
|
|
RoundFreezeEnd(RoundFreezeEnd),
|
|
RoundPreStart(RoundPreStart),
|
|
RoundPostStart(RoundPostStart),
|
|
RoundOfficiallyEnded(RoundOfficiallyEnded),
|
|
RoundStartBeep(RoundStartBeep),
|
|
RoundAnnounceMatchpoint(RoundAnnounceMatchpoint),
|
|
RoundPreRestart(RoundPreRestart),
|
|
RoundTimeWarning(RoundTimeWarning),
|
|
RoundFinalBeep(RoundFinalBeep),
|
|
BuyTimeEnded(BuyTimeEnded),
|
|
RoundAnnounceLastRoundHalf(RoundAnnounceLastRoundHalf),
|
|
AnnouncePhaseEnd(AnnouncePhaseEnd),
|
|
|
|
WinPanelMatch(WinPanelMatch),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ParseGameEventError {
|
|
MismatchedKeysFields,
|
|
}
|
|
|
|
pub static EVENT_PARSERS: phf::Map<&'static str, GameEventParser> = phf::phf_map! {
|
|
"hltv_versioninfo" => GameEventParser::new(HltvVersionInfo::parse),
|
|
|
|
"item_equip" => GameEventParser::new(ItemEquip::parse),
|
|
"item_pickup" => GameEventParser::new(ItemPickup::parse),
|
|
|
|
"weapon_reload" => GameEventParser::new(WeaponReload::parse),
|
|
"weapon_zoom" => GameEventParser::new(WeaponZoom::parse),
|
|
"weapon_fire" => GameEventParser::new(WeaponFire::parse),
|
|
|
|
"smokegrenade_detonate" => GameEventParser::new(SmokeGrenadeDetonate::parse),
|
|
"smokegrenade_expired" => GameEventParser::new(SmokeGrenadeExpired::parse),
|
|
"hegrenade_detonate" => GameEventParser::new(HEGrenadeDetonate::parse),
|
|
"inferno_startburn" => GameEventParser::new(InfernoStartBurn::parse),
|
|
"inferno_expire" => GameEventParser::new(InfernoExpire::parse),
|
|
"flashbang_detonate" => GameEventParser::new(FlashbangDetonate::parse),
|
|
"decoy_started" => GameEventParser::new(DecoyStarted::parse),
|
|
"decoy_detonate" => GameEventParser::new(DecoyDetonate::parse),
|
|
|
|
"player_connect" => GameEventParser::new(PlayerConnect::parse),
|
|
"player_connect_full" => GameEventParser::new(PlayerConnectFull::parse),
|
|
"player_disconnect" => GameEventParser::new(PlayerDisconnect::parse),
|
|
"player_footstep" => GameEventParser::new(PlayerFootstep::parse),
|
|
"player_jump" => GameEventParser::new(PlayerJump::parse),
|
|
"player_hurt" => GameEventParser::new(PlayerHurt::parse),
|
|
"player_death" => GameEventParser::new(PlayerDeath::parse),
|
|
"player_spawn" => GameEventParser::new(PlayerSpawn::parse),
|
|
"player_blind" => GameEventParser::new(PlayerBlind::parse),
|
|
"player_team" => GameEventParser::new(PlayerTeam::parse),
|
|
|
|
"bullet_damage" => GameEventParser::new(BulletDamage::parse),
|
|
|
|
"other_death" => GameEventParser::new(OtherDeath::parse),
|
|
|
|
"bomb_pickup" => GameEventParser::new(BombPickup::parse),
|
|
"bomb_dropped" => GameEventParser::new(BombDropped::parse),
|
|
"bomb_beginplant" => GameEventParser::new(BombBeginPlant::parse),
|
|
"bomb_planted" => GameEventParser::new(BombPlanted::parse),
|
|
"bomb_exploded" => GameEventParser::new(BombExploded::parse),
|
|
"bomb_begindefuse" => GameEventParser::new(BombBeginDefuse::parse),
|
|
"bomb_defused" => GameEventParser::new(BombDefused::parse),
|
|
|
|
"begin_new_match" => GameEventParser::new(BeginNewMatch::parse),
|
|
"round_announce_match_start" => GameEventParser::new(RoundAnnounceMatchStart::parse),
|
|
"round_freeze_end" => GameEventParser::new(RoundFreezeEnd::parse),
|
|
"round_prestart" => GameEventParser::new(RoundPreStart::parse),
|
|
"round_poststart" => GameEventParser::new(RoundPostStart::parse),
|
|
"round_officially_ended" => GameEventParser::new(RoundOfficiallyEnded::parse),
|
|
"cs_round_start_beep" => GameEventParser::new(RoundStartBeep::parse),
|
|
"round_announce_match_point" => GameEventParser::new(RoundAnnounceMatchpoint::parse),
|
|
"cs_pre_restart" => GameEventParser::new(RoundPreRestart::parse),
|
|
"round_time_warning" => GameEventParser::new(RoundTimeWarning::parse),
|
|
"cs_round_final_beep" => GameEventParser::new(RoundFinalBeep::parse),
|
|
"buytime_ended" => GameEventParser::new(BuyTimeEnded::parse),
|
|
"round_announce_last_round_half" => GameEventParser::new(RoundAnnounceLastRoundHalf::parse),
|
|
"announce_phase_end" => GameEventParser::new(AnnouncePhaseEnd::parse),
|
|
|
|
"cs_win_panel_match" => GameEventParser::new(WinPanelMatch::parse),
|
|
};
|
|
|
|
pub struct GameEventParser {
|
|
inner: fn(
|
|
keys: &[csgo_proto::csvc_msg_game_event_list::KeyT],
|
|
event: csgo_proto::CMsgSource1LegacyGameEvent,
|
|
) -> Result<GameEvent, ParseGameEventError>,
|
|
}
|
|
|
|
impl GameEventParser {
|
|
pub const fn new(func: ParseFn) -> Self {
|
|
Self { inner: func }
|
|
}
|
|
|
|
pub fn parse(
|
|
&self,
|
|
keys: &[csgo_proto::csvc_msg_game_event_list::KeyT],
|
|
event: csgo_proto::CMsgSource1LegacyGameEvent,
|
|
) -> Result<GameEvent, ParseGameEventError> {
|
|
if keys.len() != event.keys.len() {
|
|
return Err(ParseGameEventError::MismatchedKeysFields);
|
|
}
|
|
|
|
(self.inner)(keys, event)
|
|
}
|
|
}
|