mirror of
https://codeberg.org/icewind/log-normalizer.git
synced 2026-06-03 13:54:11 +02:00
basic parsing
This commit is contained in:
parent
a03771e4b5
commit
0151dfce7d
10 changed files with 6974 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
.env
|
||||||
1468
Cargo.lock
generated
Normal file
1468
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
|
@ -4,6 +4,14 @@ version = "0.1.0"
|
||||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
sqlx = { version = "0.3", default_features = false, features = ["macros", "postgres", "json", "runtime-tokio"] }
|
||||||
|
dotenv = "0.15.0"
|
||||||
|
main_error = "0.1.0"
|
||||||
|
tokio = { version = "0.2.13", features = ["macros", "time"] }
|
||||||
|
reqwest = { version = "0.10.4", features = [] }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
steamid-ng = "0.3.4"
|
||||||
|
test-case = "1.0.0"
|
||||||
395
schema.sql
Normal file
395
schema.sql
Normal file
|
|
@ -0,0 +1,395 @@
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pg_trgm WITH SCHEMA public;
|
||||||
|
|
||||||
|
CREATE TYPE team AS ENUM ('Blue', 'Red');
|
||||||
|
|
||||||
|
CREATE TYPE class_type AS ENUM ('scout', 'soldier', 'pyro', 'demoman', 'heavyweapons', 'engineer', 'medic', 'sniper', 'spy');
|
||||||
|
|
||||||
|
CREATE TYPE game_mode AS ENUM ('ultiduo', '4v4', '6v6', '7v7', '9v9', 'other');
|
||||||
|
|
||||||
|
CREATE TYPE event_type AS ENUM ('charge', 'pointcap', 'medic_death', 'round_win');
|
||||||
|
|
||||||
|
CREATE TYPE medigun AS ENUM ('medigun', 'kritzkrieg', 'quickfix', 'vacinator');
|
||||||
|
|
||||||
|
CREATE TYPE weapon_id AS ENUM (
|
||||||
|
'sniperrifle',
|
||||||
|
'taunt_sniper',
|
||||||
|
'sydney_sleeper',
|
||||||
|
'the_winger',
|
||||||
|
'hot_hand',
|
||||||
|
'deflect_rocket',
|
||||||
|
'scout_sword',
|
||||||
|
'voodoo_pin',
|
||||||
|
'degreaser',
|
||||||
|
'shortstop',
|
||||||
|
'robot_arm',
|
||||||
|
'tf_pumpkin_bomb',
|
||||||
|
'kunai',
|
||||||
|
'wrench',
|
||||||
|
'necro_smasher',
|
||||||
|
'headtaker',
|
||||||
|
'proto_syringe',
|
||||||
|
'eternal_reward',
|
||||||
|
'eureka_effect',
|
||||||
|
'dragons_fury_bonus',
|
||||||
|
'grappling_hook',
|
||||||
|
'pep_pistol',
|
||||||
|
'guillotine',
|
||||||
|
'lava_axe',
|
||||||
|
'wrangler_kill',
|
||||||
|
'panic_attack',
|
||||||
|
'loose_cannon',
|
||||||
|
'thirddegree',
|
||||||
|
'taunt_pyro',
|
||||||
|
'iron_bomber',
|
||||||
|
'persian_persuader',
|
||||||
|
'amputator',
|
||||||
|
'tf_projectile_pipe',
|
||||||
|
'awper_hand',
|
||||||
|
'demokatana',
|
||||||
|
'loose_cannon_reflect',
|
||||||
|
'obj_minisentry',
|
||||||
|
'back_scratcher',
|
||||||
|
'pomson',
|
||||||
|
'the_classic',
|
||||||
|
'sandman',
|
||||||
|
'axtinguisher',
|
||||||
|
'obj_sentrygun2',
|
||||||
|
'jar',
|
||||||
|
'samrevolver',
|
||||||
|
'deflect_flare',
|
||||||
|
'loch_n_load',
|
||||||
|
'tf_projectile_rocket',
|
||||||
|
'annihilator',
|
||||||
|
'rocketpack_stomp',
|
||||||
|
'ullapool_caber_explosion',
|
||||||
|
'minigun',
|
||||||
|
'deflect_promode',
|
||||||
|
'liberty_launcher',
|
||||||
|
'nessieclub',
|
||||||
|
'quickiebomb_launcher',
|
||||||
|
'cow_mangler',
|
||||||
|
'wrap_assassin',
|
||||||
|
'blackbox',
|
||||||
|
'candy_cane',
|
||||||
|
'rocketlauncher_directhit',
|
||||||
|
'ai_flamethrower',
|
||||||
|
'soda_popper',
|
||||||
|
'frontier_justice',
|
||||||
|
'rescue_ranger_reflect',
|
||||||
|
'warrior_spirit',
|
||||||
|
'widowmaker',
|
||||||
|
'quake_rl',
|
||||||
|
'tf_projectile_arrow',
|
||||||
|
'sledgehammer',
|
||||||
|
'deflect_arrow',
|
||||||
|
'market_gardener',
|
||||||
|
'unique_pickaxe_escape',
|
||||||
|
'pistol_scout',
|
||||||
|
'boston_basher',
|
||||||
|
'spellbook_bats',
|
||||||
|
'holymackerel',
|
||||||
|
'club',
|
||||||
|
'ball',
|
||||||
|
'fireaxe',
|
||||||
|
'backburner',
|
||||||
|
'eviction_notice',
|
||||||
|
'holiday_punch',
|
||||||
|
'taunt_soldier',
|
||||||
|
'shotgun_primary',
|
||||||
|
'nonnonviolent_protest',
|
||||||
|
'force_a_nature',
|
||||||
|
'paintrain',
|
||||||
|
'freedom_staff',
|
||||||
|
'shotgun_hwg',
|
||||||
|
'long_heatmaker',
|
||||||
|
'shotgun_soldier',
|
||||||
|
'knife',
|
||||||
|
'batsaber',
|
||||||
|
'mailbox',
|
||||||
|
'compound_bow',
|
||||||
|
'ullapool_caber',
|
||||||
|
'ambassador',
|
||||||
|
'player_penetration',
|
||||||
|
'powerjack',
|
||||||
|
'crusaders_crossbow',
|
||||||
|
'scotland_shard',
|
||||||
|
'wrench_jag',
|
||||||
|
'scattergun',
|
||||||
|
'unknown',
|
||||||
|
'shahanshah',
|
||||||
|
'pistol',
|
||||||
|
'spellbook_boss',
|
||||||
|
'smg',
|
||||||
|
'dragons_fury',
|
||||||
|
'revolver',
|
||||||
|
'player',
|
||||||
|
'the_maul',
|
||||||
|
'skullbat',
|
||||||
|
'ham_shank',
|
||||||
|
'solemn_vow',
|
||||||
|
'iron_curtain',
|
||||||
|
'bonesaw',
|
||||||
|
'dumpster_device',
|
||||||
|
'bushwacka',
|
||||||
|
'builder',
|
||||||
|
'bread_bite',
|
||||||
|
'southern_hospitality',
|
||||||
|
'tribalkukri',
|
||||||
|
'the_capper',
|
||||||
|
'fists',
|
||||||
|
'disciplinary_action',
|
||||||
|
'tf_projectile_flare',
|
||||||
|
'bleed_kill',
|
||||||
|
'black_rose',
|
||||||
|
'letranger',
|
||||||
|
'tomislav',
|
||||||
|
'atomizer',
|
||||||
|
'back_scatter',
|
||||||
|
'pep_brawlerblaster',
|
||||||
|
'tf_projectile_pipe_remote',
|
||||||
|
'battleaxe',
|
||||||
|
'deflect_flare_detonator',
|
||||||
|
'taunt_medic',
|
||||||
|
'telefrag',
|
||||||
|
'stickybomb_defender',
|
||||||
|
'splendid_screen',
|
||||||
|
'claidheamohmor',
|
||||||
|
'airstrike',
|
||||||
|
'righteous_bison',
|
||||||
|
'gloves_running_urgently',
|
||||||
|
'sword',
|
||||||
|
'mantreads',
|
||||||
|
'deflect_sticky',
|
||||||
|
'enforcer',
|
||||||
|
'scorch_shot',
|
||||||
|
'pro_rifle',
|
||||||
|
'spy_cicle',
|
||||||
|
'bat',
|
||||||
|
'sharp_dresser',
|
||||||
|
'spellbook_lightning',
|
||||||
|
'tide_turner',
|
||||||
|
'shotgun_pyro',
|
||||||
|
'lava_bat',
|
||||||
|
'taunt_heavy',
|
||||||
|
'bottle',
|
||||||
|
'unique_pickaxe',
|
||||||
|
'phlogistinator',
|
||||||
|
'crossing_guard',
|
||||||
|
'giger_counter',
|
||||||
|
'charged_smg',
|
||||||
|
'syringegun_medic',
|
||||||
|
'gloves',
|
||||||
|
'bazaar_bargain',
|
||||||
|
'spellbook_mirv',
|
||||||
|
'big_earner',
|
||||||
|
'battleneedle',
|
||||||
|
'warfan',
|
||||||
|
'obj_sentrygun',
|
||||||
|
'manmelter',
|
||||||
|
'family_business',
|
||||||
|
'reserve_shooter',
|
||||||
|
'short_circuit',
|
||||||
|
'flaregun',
|
||||||
|
'spellbook_fireball',
|
||||||
|
'world',
|
||||||
|
'jar_milk',
|
||||||
|
'flamethrower',
|
||||||
|
'shooting_star',
|
||||||
|
'trigger_hurt',
|
||||||
|
'blutsauger',
|
||||||
|
'taunt_spy',
|
||||||
|
'tf_projectile_energy_ball',
|
||||||
|
'jar_gas',
|
||||||
|
'tf_projectile_mechanicalarmorb',
|
||||||
|
'obj_sentrygun3',
|
||||||
|
'diamondback',
|
||||||
|
'shovel',
|
||||||
|
'brass_beast',
|
||||||
|
'loose_cannon_impact',
|
||||||
|
'demoshield',
|
||||||
|
'prinny_machete',
|
||||||
|
'machina',
|
||||||
|
'rocketlauncher_fireball',
|
||||||
|
'sticky_resistance',
|
||||||
|
'detonator',
|
||||||
|
'tf_projectile_sentryrocket',
|
||||||
|
'unarmed_combat',
|
||||||
|
'spellbook_skeleton',
|
||||||
|
'ubersaw',
|
||||||
|
'maxgun',
|
||||||
|
'robot_arm_combo_kill',
|
||||||
|
'rescue_ranger',
|
||||||
|
'apocofists',
|
||||||
|
'natascha',
|
||||||
|
'pro_smg',
|
||||||
|
'steel_fists',
|
||||||
|
'fryingpan',
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE logs (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
red_score INTEGER NOT NULL,
|
||||||
|
blue_score INTEGER NOT NULL,
|
||||||
|
length INTEGER NOT NULL,
|
||||||
|
game_mode game_mode NOT NULL,
|
||||||
|
map TEXT NOT NULL,
|
||||||
|
date TIMESTAMP WITHOUT TIME ZONE NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX logs_map_idx
|
||||||
|
ON logs USING BTREE (map);
|
||||||
|
|
||||||
|
CREATE INDEX logs_mode_idx
|
||||||
|
ON logs USING BTREE (game_mode);
|
||||||
|
|
||||||
|
CREATE TABLE rounds (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
round INTEGER NOT NULL,
|
||||||
|
log_id INTEGER NOT NULL REFERENCES logs(id),
|
||||||
|
length INTEGER NOT NULL,
|
||||||
|
winner team NOT NULL,
|
||||||
|
first_cap team NOT NULL,
|
||||||
|
red_score INTEGER NOT NULL,
|
||||||
|
blue_score INTEGER NOT NULL,
|
||||||
|
red_kills INTEGER NOT NULL,
|
||||||
|
blue_kills INTEGER NOT NULL,
|
||||||
|
red_dmg INTEGER NOT NULL,
|
||||||
|
blue_dmg INTEGER NOT NULL,
|
||||||
|
red_ubers INTEGER NOT NULL,
|
||||||
|
blue_ubers INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX rounds_log_id_idx
|
||||||
|
ON rounds USING BTREE (log_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX rounds_round_log_id_idx
|
||||||
|
ON rounds USING BTREE (round, log_id);
|
||||||
|
|
||||||
|
CREATE INDEX rounds_winner_idx
|
||||||
|
ON rounds USING BTREE (winner);
|
||||||
|
|
||||||
|
CREATE INDEX rounds_first_cap_idx
|
||||||
|
ON rounds USING BTREE (first_cap);
|
||||||
|
|
||||||
|
CREATE TABLE events_charge (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
||||||
|
time INTEGER NOT NULL,
|
||||||
|
team team NOT NULL,
|
||||||
|
steam_id BIGINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX events_charge_round_id_idx
|
||||||
|
ON events_charge USING BTREE (round_id);
|
||||||
|
|
||||||
|
CREATE INDEX events_charge_steam_id_idx
|
||||||
|
ON events_charge USING BTREE (steam_id);
|
||||||
|
|
||||||
|
CREATE TABLE events_point_cap (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
||||||
|
time INTEGER NOT NULL,
|
||||||
|
team team NOT NULL,
|
||||||
|
point INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX events_point_cap_round_id_idx
|
||||||
|
ON events_point_cap USING BTREE (round_id);
|
||||||
|
|
||||||
|
CREATE TABLE events_medic_death (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
||||||
|
time INTEGER NOT NULL,
|
||||||
|
team team NOT NULL,
|
||||||
|
steam_id BIGINT NOT NULL,
|
||||||
|
killer BIGINT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX events_medic_death_round_id_idx
|
||||||
|
ON events_medic_death USING BTREE (round_id);
|
||||||
|
|
||||||
|
CREATE TABLE events_round_win (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
round_id INTEGER NOT NULL REFERENCES rounds(id),
|
||||||
|
time INTEGER NOT NULL,
|
||||||
|
team team NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX events_round_win_round_id_idx
|
||||||
|
ON events_round_win USING BTREE (round_id);
|
||||||
|
|
||||||
|
CREATE TABLE players (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
log_id INTEGER NOT NULL REFERENCES logs(id),
|
||||||
|
steam_id BIGINT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
kills INTEGER NOT NULL,
|
||||||
|
deaths INTEGER NOT NULL,
|
||||||
|
assists INTEGER NOT NULL,
|
||||||
|
suicides INTEGER NOT NULL,
|
||||||
|
dmg INTEGER NOT NULL,
|
||||||
|
damage_taken INTEGER NOT NULL,
|
||||||
|
ubers INTEGER NOT NULL,
|
||||||
|
medigun_ubers INTEGER NOT NULL,
|
||||||
|
kritzkrieg_ubers INTEGER NOT NULL,
|
||||||
|
quickfix_ubers INTEGER NOT NULL,
|
||||||
|
vacinator_ubers INTEGER NOT NULL,
|
||||||
|
drops INTEGER NOT NULL,
|
||||||
|
medkits INTEGER NOT NULL,
|
||||||
|
medkits_hp INTEGER NOT NULL,
|
||||||
|
backstabs INTEGER NOT NULL,
|
||||||
|
headshots INTEGER NOT NULL,
|
||||||
|
heal INTEGER NOT NULL,
|
||||||
|
heals_received INTEGER NOT NULL,
|
||||||
|
scout_kills INTEGER NOT NULL,
|
||||||
|
soldier_kills INTEGER NOT NULL,
|
||||||
|
pyro_kills INTEGER NOT NULL,
|
||||||
|
demoman_kills INTEGER NOT NULL,
|
||||||
|
heavy_kills INTEGER NOT NULL,
|
||||||
|
engineer_kills INTEGER NOT NULL,
|
||||||
|
medic_kills INTEGER NOT NULL,
|
||||||
|
sniper_kills INTEGER NOT NULL,
|
||||||
|
spy_kills INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX players_log_id_idx
|
||||||
|
ON players USING BTREE (log_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX players_log_steam_id_idx
|
||||||
|
ON players USING BTREE (log_id, steam_id);
|
||||||
|
|
||||||
|
CREATE INDEX players_steam_id_idx
|
||||||
|
ON players USING BTREE (steam_id);
|
||||||
|
|
||||||
|
CREATE TABLE class_stats (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
player_id INTEGER NOT NULL REFERENCES players(id),
|
||||||
|
type class_type NOT NULL,
|
||||||
|
time INTEGER NOT NULL,
|
||||||
|
kills INTEGER NOT NULL,
|
||||||
|
deaths INTEGER NOT NULL,
|
||||||
|
assists INTEGER NOT NULL,
|
||||||
|
dmg INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX class_stats_player_id_idx
|
||||||
|
ON class_stats USING BTREE (player_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX class_stats_player_id_type_idx
|
||||||
|
ON class_stats USING BTREE (player_id, type);
|
||||||
|
|
||||||
|
CREATE TABLE player_weapon_stats (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
class_stat_id INTEGER NOT NULL REFERENCES class_stats(id),
|
||||||
|
weapon weapon_id NOT NULL,
|
||||||
|
kills INTEGER NOT NULL,
|
||||||
|
shots INTEGER NOT NULL,
|
||||||
|
hits INTEGER NOT NULL,
|
||||||
|
dmg INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX player_weapon_stats_class_stat_id_idx
|
||||||
|
ON player_weapon_stats USING BTREE (class_stat_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX player_weapon_stats_class_stat_id_weapon_idx
|
||||||
|
ON player_weapon_stats USING BTREE (class_stat_id, weapon);
|
||||||
272
src/data.rs
Normal file
272
src/data.rs
Normal file
|
|
@ -0,0 +1,272 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, sqlx::Type, Deserialize)]
|
||||||
|
pub enum Team {
|
||||||
|
Blue,
|
||||||
|
Red,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, sqlx::Type, Deserialize)]
|
||||||
|
pub enum Class {
|
||||||
|
Scout,
|
||||||
|
Soldier,
|
||||||
|
Pyro,
|
||||||
|
Demoman,
|
||||||
|
HeavyWeapons,
|
||||||
|
Engineer,
|
||||||
|
Medic,
|
||||||
|
Sniper,
|
||||||
|
Spy,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, sqlx::Type, Deserialize)]
|
||||||
|
pub enum GameMode {
|
||||||
|
UltiDuo,
|
||||||
|
Fours,
|
||||||
|
Sixes,
|
||||||
|
Sevens,
|
||||||
|
Highlander,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, sqlx::Type, Deserialize)]
|
||||||
|
pub enum EventType {
|
||||||
|
Charge,
|
||||||
|
PointCap,
|
||||||
|
MedicDeath,
|
||||||
|
RoundWin,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, sqlx::Type, Deserialize, Hash, Eq, PartialEq)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum Medigun {
|
||||||
|
Medigun,
|
||||||
|
KritzKrieg,
|
||||||
|
QuickFix,
|
||||||
|
Vacinator
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Medigun {
|
||||||
|
fn default() -> Self {
|
||||||
|
Medigun::Medigun
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, sqlx::Type, Deserialize, Hash, Eq, PartialEq)]
|
||||||
|
// #[sqlx(rename_all = "snake_case")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Weapon {
|
||||||
|
Sniperrifle,
|
||||||
|
TauntSniper,
|
||||||
|
SydneySleeper,
|
||||||
|
TheWinger,
|
||||||
|
HotHand,
|
||||||
|
DeflectRocket,
|
||||||
|
ScoutSword,
|
||||||
|
VoodooPin,
|
||||||
|
Degreaser,
|
||||||
|
Shortstop,
|
||||||
|
RobotArm,
|
||||||
|
TfPumpkinBomb,
|
||||||
|
Kunai,
|
||||||
|
Wrench,
|
||||||
|
NecroSmasher,
|
||||||
|
Headtaker,
|
||||||
|
ProtoSyringe,
|
||||||
|
EternalReward,
|
||||||
|
EurekaEffect,
|
||||||
|
DragonsFuryBonus,
|
||||||
|
GrapplingHook,
|
||||||
|
PepPistol,
|
||||||
|
Guillotine,
|
||||||
|
LavaAxe,
|
||||||
|
WranglerKill,
|
||||||
|
PanicAttack,
|
||||||
|
LooseCannon,
|
||||||
|
Thirddegree,
|
||||||
|
TauntPyro,
|
||||||
|
IronBomber,
|
||||||
|
PersianPersuader,
|
||||||
|
Amputator,
|
||||||
|
TfProjectilePipe,
|
||||||
|
AwperHand,
|
||||||
|
Demokatana,
|
||||||
|
LooseCannonReflect,
|
||||||
|
ObjMinisentry,
|
||||||
|
BackScratcher,
|
||||||
|
Pomson,
|
||||||
|
TheClassic,
|
||||||
|
Sandman,
|
||||||
|
Axtinguisher,
|
||||||
|
ObjSentrygun2,
|
||||||
|
Jar,
|
||||||
|
Samrevolver,
|
||||||
|
DeflectFlare,
|
||||||
|
LochNLoad,
|
||||||
|
TfProjectileRocket,
|
||||||
|
Annihilator,
|
||||||
|
RocketpackStomp,
|
||||||
|
UllapoolCaberExplosion,
|
||||||
|
Minigun,
|
||||||
|
DeflectPromode,
|
||||||
|
LibertyLauncher,
|
||||||
|
Nessieclub,
|
||||||
|
QuickiebombLauncher,
|
||||||
|
CowMangler,
|
||||||
|
WrapAssassin,
|
||||||
|
Blackbox,
|
||||||
|
CandyCane,
|
||||||
|
RocketlauncherDirecthit,
|
||||||
|
AiFlamethrower,
|
||||||
|
SodaPopper,
|
||||||
|
FrontierJustice,
|
||||||
|
RescueRangerReflect,
|
||||||
|
WarriorSpirit,
|
||||||
|
Widowmaker,
|
||||||
|
QuakeRl,
|
||||||
|
TfProjectileArrow,
|
||||||
|
Sledgehammer,
|
||||||
|
DeflectArrow,
|
||||||
|
MarketGardener,
|
||||||
|
UniquePickaxeEscape,
|
||||||
|
PistolScout,
|
||||||
|
BostonBasher,
|
||||||
|
SpellbookBats,
|
||||||
|
Holymackerel,
|
||||||
|
Club,
|
||||||
|
Ball,
|
||||||
|
Fireaxe,
|
||||||
|
Backburner,
|
||||||
|
EvictionNotice,
|
||||||
|
HolidayPunch,
|
||||||
|
TauntSoldier,
|
||||||
|
ShotgunPrimary,
|
||||||
|
NonnonviolentProtest,
|
||||||
|
ForceANature,
|
||||||
|
Paintrain,
|
||||||
|
FreedomStaff,
|
||||||
|
ShotgunHwg,
|
||||||
|
LongHeatmaker,
|
||||||
|
ShotgunSoldier,
|
||||||
|
Knife,
|
||||||
|
Batsaber,
|
||||||
|
Mailbox,
|
||||||
|
CompoundBow,
|
||||||
|
UllapoolCaber,
|
||||||
|
Ambassador,
|
||||||
|
PlayerPenetration,
|
||||||
|
Powerjack,
|
||||||
|
CrusadersCrossbow,
|
||||||
|
ScotlandShard,
|
||||||
|
WrenchJag,
|
||||||
|
Scattergun,
|
||||||
|
Unknown,
|
||||||
|
Shahanshah,
|
||||||
|
Pistol,
|
||||||
|
SpellbookBoss,
|
||||||
|
Smg,
|
||||||
|
DragonsFury,
|
||||||
|
Revolver,
|
||||||
|
Player,
|
||||||
|
TheMaul,
|
||||||
|
Skullbat,
|
||||||
|
HamShank,
|
||||||
|
SolemnVow,
|
||||||
|
IronCurtain,
|
||||||
|
Bonesaw,
|
||||||
|
DumpsterDevice,
|
||||||
|
Bushwacka,
|
||||||
|
Builder,
|
||||||
|
BreadBite,
|
||||||
|
SouthernHospitality,
|
||||||
|
Tribalkukri,
|
||||||
|
TheCapper,
|
||||||
|
Fists,
|
||||||
|
DisciplinaryAction,
|
||||||
|
TfProjectileFlare,
|
||||||
|
BleedKill,
|
||||||
|
BlackRose,
|
||||||
|
Letranger,
|
||||||
|
Tomislav,
|
||||||
|
Atomizer,
|
||||||
|
BackScatter,
|
||||||
|
PepBrawlerblaster,
|
||||||
|
TfProjectilePipeRemote,
|
||||||
|
Battleaxe,
|
||||||
|
DeflectFlareDetonator,
|
||||||
|
TauntMedic,
|
||||||
|
Telefrag,
|
||||||
|
StickybombDefender,
|
||||||
|
SplendidScreen,
|
||||||
|
Claidheamohmor,
|
||||||
|
Airstrike,
|
||||||
|
RighteousBison,
|
||||||
|
GlovesRunningUrgently,
|
||||||
|
Sword,
|
||||||
|
Mantreads,
|
||||||
|
DeflectSticky,
|
||||||
|
Enforcer,
|
||||||
|
ScorchShot,
|
||||||
|
ProRifle,
|
||||||
|
SpyCicle,
|
||||||
|
Bat,
|
||||||
|
SharpDresser,
|
||||||
|
SpellbookLightning,
|
||||||
|
TideTurner,
|
||||||
|
ShotgunPyro,
|
||||||
|
LavaBat,
|
||||||
|
TauntHeavy,
|
||||||
|
Bottle,
|
||||||
|
UniquePickaxe,
|
||||||
|
Phlogistinator,
|
||||||
|
CrossingGuard,
|
||||||
|
GigerCounter,
|
||||||
|
ChargedSmg,
|
||||||
|
SyringegunMedic,
|
||||||
|
Gloves,
|
||||||
|
BazaarBargain,
|
||||||
|
SpellbookMirv,
|
||||||
|
BigEarner,
|
||||||
|
Battleneedle,
|
||||||
|
Warfan,
|
||||||
|
ObjSentrygun,
|
||||||
|
Manmelter,
|
||||||
|
FamilyBusiness,
|
||||||
|
ReserveShooter,
|
||||||
|
ShortCircuit,
|
||||||
|
Flaregun,
|
||||||
|
SpellbookFireball,
|
||||||
|
World,
|
||||||
|
JarMilk,
|
||||||
|
Flamethrower,
|
||||||
|
ShootingStar,
|
||||||
|
TriggerHurt,
|
||||||
|
Blutsauger,
|
||||||
|
TauntSpy,
|
||||||
|
TfProjectileEnergyBall,
|
||||||
|
JarGas,
|
||||||
|
TfProjectileMechanicalarmorb,
|
||||||
|
ObjSentrygun3,
|
||||||
|
Diamondback,
|
||||||
|
Shovel,
|
||||||
|
BrassBeast,
|
||||||
|
LooseCannonImpact,
|
||||||
|
Demoshield,
|
||||||
|
PrinnyMachete,
|
||||||
|
Machina,
|
||||||
|
RocketlauncherFireball,
|
||||||
|
StickyResistance,
|
||||||
|
Detonator,
|
||||||
|
TfProjectileSentryrocket,
|
||||||
|
UnarmedCombat,
|
||||||
|
SpellbookSkeleton,
|
||||||
|
Ubersaw,
|
||||||
|
Maxgun,
|
||||||
|
RobotArmComboKill,
|
||||||
|
RescueRanger,
|
||||||
|
Apocofists,
|
||||||
|
Natascha,
|
||||||
|
ProSmg,
|
||||||
|
SteelFists,
|
||||||
|
Fryingpan,
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
mod data;
|
||||||
|
mod raw;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
294
src/raw.rs
Normal file
294
src/raw.rs
Normal file
|
|
@ -0,0 +1,294 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use steamid_ng::SteamID;
|
||||||
|
use crate::data::{Medigun, Class, Weapon, Team as TeamId};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::export::TryFrom;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct RawLog {
|
||||||
|
#[serde(default)]
|
||||||
|
pub version: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub length: u32,
|
||||||
|
pub teams: Option<Teams>,
|
||||||
|
pub players: HashMap<SteamID, Player>,
|
||||||
|
pub names: HashMap<SteamID, String>,
|
||||||
|
pub rounds: Option<Vec<Round>>,
|
||||||
|
pub healspread: HashMap<SteamID, HashMap<SteamID, u32>>,
|
||||||
|
pub classkills: HashMap<SteamID, ClassNumbers>,
|
||||||
|
pub classdeaths: HashMap<SteamID, ClassNumbers>,
|
||||||
|
pub classkillassists: Option<HashMap<SteamID, ClassNumbers>>,
|
||||||
|
pub chat: Vec<ChatMessage>,
|
||||||
|
pub info: Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub struct Teams {
|
||||||
|
pub red: Team,
|
||||||
|
pub blue: Team,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Team {
|
||||||
|
pub score: u32,
|
||||||
|
pub kills: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub deaths: u32,
|
||||||
|
pub dmg: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub charges: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub drops: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub firstcaps: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub caps: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Player {
|
||||||
|
pub team: TeamId,
|
||||||
|
pub kills: u16,
|
||||||
|
pub deaths: u16,
|
||||||
|
pub assists: u16,
|
||||||
|
#[serde(default)]
|
||||||
|
pub suicides: u16,
|
||||||
|
#[serde(default)]
|
||||||
|
pub dmg: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub dmg_real: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub dt: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub dt_real: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub hr: u16,
|
||||||
|
#[serde(default)]
|
||||||
|
pub lks: u16,
|
||||||
|
pub ubers: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub ubertypes: HashMap<Medigun, u8>,
|
||||||
|
pub drops: u8,
|
||||||
|
pub medkits: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub medkits_hp: u16,
|
||||||
|
pub backstabs: u8,
|
||||||
|
pub headshots: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub headshots_hit: u8,
|
||||||
|
pub heal: u32,
|
||||||
|
pub cpc: u8,
|
||||||
|
pub ic: u8,
|
||||||
|
pub medicstat: Option<MedicStats>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct MedicStats {
|
||||||
|
pub advantages_lost: u8,
|
||||||
|
pub biggest_advantage_list: u16,
|
||||||
|
pub deaths_within_20s_after_uber: u8,
|
||||||
|
pub deaths_with_95_99_uber: u8,
|
||||||
|
pub avg_time_before_healing: f32,
|
||||||
|
pub avg_time_to_build: f32,
|
||||||
|
pub avg_time_before_using: f32,
|
||||||
|
pub avg_uber_length: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ClassStat {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub class: Class,
|
||||||
|
pub kills: u16,
|
||||||
|
pub dmg: u32,
|
||||||
|
pub total_time: u32,
|
||||||
|
pub weapon: HashMap<Weapon, WeaponStat>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct WeaponStat {
|
||||||
|
pub kills: u32,
|
||||||
|
pub dmg: u32,
|
||||||
|
pub avg_dmg: u32,
|
||||||
|
pub shots: u32,
|
||||||
|
pub hits: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Round {
|
||||||
|
#[serde(default)]
|
||||||
|
pub start_time: u64,
|
||||||
|
pub winner: TeamId,
|
||||||
|
#[serde(rename = "firstcap")]
|
||||||
|
pub first_cap: Option<TeamId>,
|
||||||
|
pub length: u32,
|
||||||
|
pub team: Option<Teams>,
|
||||||
|
pub players: HashMap<SteamID, RoundPlayer>,
|
||||||
|
pub events: Vec<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct RoundPlayer {
|
||||||
|
pub kills: u32,
|
||||||
|
pub dmg: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum Event {
|
||||||
|
Charge {
|
||||||
|
#[serde(default)]
|
||||||
|
medigun: Medigun,
|
||||||
|
time: u32,
|
||||||
|
steamid: SteamID,
|
||||||
|
team: TeamId,
|
||||||
|
},
|
||||||
|
#[serde(rename = "pointcap")]
|
||||||
|
PointCap {
|
||||||
|
time: u32,
|
||||||
|
team: TeamId,
|
||||||
|
point: u8,
|
||||||
|
},
|
||||||
|
MedicDeath {
|
||||||
|
time: u32,
|
||||||
|
team: TeamId,
|
||||||
|
steamid: SteamID,
|
||||||
|
killer: SteamID,
|
||||||
|
},
|
||||||
|
RoundWin {
|
||||||
|
time: u32,
|
||||||
|
team: TeamId,
|
||||||
|
},
|
||||||
|
Drop {
|
||||||
|
time: u32,
|
||||||
|
steamid: SteamID,
|
||||||
|
team: TeamId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ClassNumbers {
|
||||||
|
#[serde(default)]
|
||||||
|
pub scout: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub soldier: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub pyro: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub demoman: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub heavyweapons: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub engineer: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub medic: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub sniper: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
pub spy: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ChatMessage {
|
||||||
|
pub steamid: ChatFrom,
|
||||||
|
pub name: String,
|
||||||
|
pub msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[serde(try_from = "String")]
|
||||||
|
pub enum ChatFrom {
|
||||||
|
Player(SteamID),
|
||||||
|
Console,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for ChatFrom {
|
||||||
|
type Error = steamid_ng::SteamIDParseError;
|
||||||
|
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
if value == "Console" {
|
||||||
|
Ok(ChatFrom::Console)
|
||||||
|
} else {
|
||||||
|
value.parse().map(ChatFrom::Player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Info {
|
||||||
|
pub map: String,
|
||||||
|
pub total_length: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub supplemental: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_real_damage: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_weapon_damage: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_accuracy: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_hp: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_hp_real: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_hs: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_hs_hit: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_bs: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_cp: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_sb: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_dt: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_as: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_hr: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_intel: bool,
|
||||||
|
pub title: String,
|
||||||
|
pub date: u64,
|
||||||
|
pub uploader: Uploader,
|
||||||
|
pub rounds: Option<Vec<Round>>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub teams: Option<InfoTeams>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub struct InfoTeams {
|
||||||
|
pub red: InfoTeam,
|
||||||
|
pub blue: InfoTeam,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct InfoTeam {
|
||||||
|
pub score: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct Uploader {
|
||||||
|
pub id: SteamID,
|
||||||
|
pub name: String,
|
||||||
|
pub info: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::fs;
|
||||||
|
use test_case::test_case;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test_case("1.json")]
|
||||||
|
#[test_case("550237.json")]
|
||||||
|
fn test_parse(file: &str) {
|
||||||
|
let content = fs::read_to_string(format!("tests/data/{}", file)).unwrap();
|
||||||
|
let parsed: RawLog = serde_json::from_str(&content).unwrap();
|
||||||
|
assert!(parsed.teams.is_some() || parsed.info.teams.is_some());
|
||||||
|
assert!(parsed.rounds.is_some() || parsed.info.rounds.is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
1989
tests/data/1.json
Normal file
1989
tests/data/1.json
Normal file
File diff suppressed because it is too large
Load diff
2542
tests/data/550237.json
Normal file
2542
tests/data/550237.json
Normal file
File diff suppressed because it is too large
Load diff
0
tests/raw.rs
Normal file
0
tests/raw.rs
Normal file
Loading…
Add table
Add a link
Reference in a new issue