Home

Awesome

Demo parser for Counter-Strike 2

We now have a discord channel: Discord

Demoparser is a tool for analysing CS2 replay files "demos". All the heavy lifting is done in Rust, but you use it from the comfort of Python/JavaScript. The parser takes a slightly different approach to exposing the information in the demo. Rather than letting you hook onto events in a streaming fashion, it lets you "query" the demo similarly to how you would interact with a database.

Install

Python: pip install demoparser2 (python >= 3.8)

NodeJS: npm i @laihoe/demoparser2

WASM: npm i demoparser2

Getting started

Python

from demoparser2 import DemoParser

parser = DemoParser("path_to_demo.dem")
event_df = parser.parse_event("player_death", player=["X", "Y"], other=["total_rounds_played"])
ticks_df = parser.parse_ticks(["X", "Y"])

NodeJS

var {parseEvent, parseTicks} = require('@laihoe/demoparser2');

let event_json = parseEvent("path_to_demo.dem", "player_death", ["X", "Y"], ["total_rounds_played"])
let ticks_json = parseTicks("path_to_demo.dem", ["X", "Y"])

Examples in Python and JavaScript

Scuffed Documentaion

Performance

The benchmark finds the coordinates of all player deaths. Code: Benchmark

Benchmarked 50 demos containing a mix of (MM, Faceit, HLTV) demos. Total size: 4.6GB

deviceCPU corestotal timeMB/s
gaming PC126.14s749
ThinkPad T14 gen 2414.00s328

Gaming pc specs: CPU: Ryzen 5900x, SSD: Samsung 980 pro NVME
Thinkpad specs: CPU: i5-1335g7, SSD: Toshiba XG6 NVME
Both devices run Ubuntu 20.04
Python/JS are rougly as fast.

List of fields the parser supports:

Player data

Name"Real" name
Xm_vec + m_cell
Ym_vec + m_cell
Zm_vec + m_cell
healthm_iHealth
scorem_iScore
mvpsm_iMVPs
is_alivem_bPawnIsAlive
balancem_iAccount
inventory_
inventory_as_ids-
life_statem_lifeState
pitchm_angEyeAngles[0]
yawm_angEyeAngles[1]
is_auto_mutedm_bHasCommunicationAbuseMute
crosshair_codem_szCrosshairCodes
pending_team_numm_iPendingTeamNum
player_colorm_iCompTeammateColor
ever_played_on_teamm_bEverPlayedOnTeam
is_coach_teamm_iCoachingTeam
rankm_iCompetitiveRanking
rank_if_winm_iCompetitiveRankingPredicted_Win
rank_if_lossm_iCompetitiveRankingPredicted_Loss
rank_if_tiem_iCompetitiveRankingPredicted_Tie
comp_winsm_iCompetitiveWins
comp_rank_typem_iCompetitiveRankType
is_controlling_botm_bControllingBot
has_controlled_bot_this_roundm_bHasControlledBotThisRound
can_control_botm_bCanControlObservedBot
has_defuserm_bPawnHasDefuser
has_helmetm_bPawnHasHelmet
spawn_timem_iPawnLifetimeStart
death_timem_iPawnLifetimeEnd
game_timenet_tick
is_connectedm_iConnected
player_namem_iszPlayerName
player_steamidm_steamID
fovm_iDesiredFOV
start_balancem_iStartAccount
total_cash_spentm_iTotalCashSpent
cash_spent_this_roundm_iCashSpentThisRound
music_kit_idm_unMusicID
leader_honorsm_nPersonaDataPublicCommendsLeader
teacher_honorsm_nPersonaDataPublicCommendsTeacher
friendly_honorsm_nPersonaDataPublicCommendsFriendly
pingm_iPing
move_collidem_MoveCollide
move_typem_MoveType
team_numm_iTeamNum
active_weaponm_hActiveWeapon
looking_at_weaponm_bIsLookingAtWeapon
holding_look_at_weaponm_bIsHoldingLookAtWeapon
next_attack_timem_flNextAttack
duck_time_msm_nDuckTimeMsecs
max_speedm_flMaxspeed
max_fall_velom_flMaxFallVelocity
duck_amountm_flDuckAmount
duck_speedm_flDuckSpeed
duck_overrdiem_bDuckOverride
old_jump_pressedm_bOldJumpPressed
jump_untilm_flJumpUntil
jump_velom_flJumpVel
fall_velom_flFallVelocity
in_crouchm_bInCrouch
crouch_statem_nCrouchState
duckedm_bDucked
duckingm_bDucking
in_duck_jumpm_bInDuckJump
allow_auto_movementm_bAllowAutoMovement
jump_time_msm_nJumpTimeMsecs
last_duck_timem_flLastDuckTime
is_rescuingm_bIsRescuing
weapon_purchases_this_matchm_iWeaponPurchasesThisMatch
weapon_purchases_this_roundm_iWeaponPurchasesThisRound
spottedm_bSpotted
approximate_spotted_bym_bSpottedByMask
time_last_injurym_flTimeOfLastInjury
direction_last_injurym_nRelativeDirectionOfLastInjury
player_statem_iPlayerState
passive_itemsm_passiveItems
is_scopedm_bIsScoped
is_walkingm_bIsWalking
resume_zoomm_bResumeZoom
is_defusingm_bIsDefusing
is_grabbing_hostagem_bIsGrabbingHostage
blocking_use_in_progessm_iBlockingUseActionInProgress
molotov_damage_timem_fMolotovDamageTime
moved_since_spawnm_bHasMovedSinceSpawn
in_bomb_zonem_bInBombZone
in_buy_zonem_bInBuyZone
in_no_defuse_aream_bInNoDefuseArea
killed_by_taserm_bKilledByTaser
move_statem_iMoveState
which_bomb_zonem_nWhichBombZone
in_hostage_rescue_zonem_bInHostageRescueZone
staminam_flStamina
directionm_iDirection
shots_firedm_iShotsFired
armor_valuem_ArmorValue
velo_modifierm_flVelocityModifier
ground_accel_linear_frac_last_timem_flGroundAccelLinearFracLastTime
flash_durationm_flFlashDuration
flash_max_alpham_flFlashMaxAlpha
wait_for_no_attackm_bWaitForNoAttack
last_place_namem_szLastPlaceName
is_strafingm_bStrafing
round_start_equip_valuem_unRoundStartEquipmentValue
current_equip_valuem_unCurrentEquipmentValue
velocity-
velocity_X-
velocity_Y-
velocity_Z-
agent_skin-
user_id-
entity_id-
is_airbornem_hGroundEntity
aim_punch_angleCCSPlayerPawn.m_aimPunchAngle
aim_punch_angle_velCCSPlayerPawn.m_aimPunchAngleVel

Buttons

True/Flase if player is pressing button.

NameReal name
FORWARDm_nButtonDownMaskPrev
LEFTm_nButtonDownMaskPrev
RIGHTm_nButtonDownMaskPrev
BACKm_nButtonDownMaskPrev
FIREm_nButtonDownMaskPrev
RIGHTCLICKm_nButtonDownMaskPrev
RELOADm_nButtonDownMaskPrev
INSPECTm_nButtonDownMaskPrev
USEm_nButtonDownMaskPrev
ZOOMm_nButtonDownMaskPrev
SCOREBOARDm_nButtonDownMaskPrev
WALKm_nButtonDownMaskPrev
buttonm_nButtonDownMaskPrev

(buttons is the real value of m_nButtonDownMaskPrev and the others are derived from it)

Game State

NameReal name
team_rounds_totalm_iScore
team_surrenderedm_bSurrendered
team_namem_szTeamname
team_score_overtimem_scoreOvertime
team_match_statm_szTeamMatchStat
team_num_map_victoriesm_numMapVictories
team_score_first_halfm_scoreFirstHalf
team_score_second_halfm_scoreSecondHalf
team_clan_namem_szClanTeamname
is_freeze_periodm_bFreezePeriod
is_warmup_periodm_bWarmupPeriod
warmup_period_endm_fWarmupPeriodEnd
warmup_period_startm_fWarmupPeriodStart
is_terrorist_timeoutm_bTerroristTimeOutActive
is_ct_timeoutm_bCTTimeOutActive
terrorist_timeout_remainingm_flTerroristTimeOutRemaining
ct_timeout_remainingm_flCTTimeOutRemaining
num_terrorist_timeoutsm_nTerroristTimeOuts
num_ct_timeoutsm_nCTTimeOuts
is_technical_timeoutm_bTechnicalTimeOut
is_waiting_for_resumem_bMatchWaitingForResume
match_start_timem_fMatchStartTime
round_start_timem_fRoundStartTime
restart_round_timem_flRestartRoundTime
is_game_restartm_bGameRestart
game_start_timem_flGameStartTime
time_until_next_phase_startm_timeUntilNextPhaseStarts
game_phasem_gamePhase
total_rounds_playedm_totalRoundsPlayed
rounds_played_this_phasem_nRoundsPlayedThisPhase
hostages_remainingm_iHostagesRemaining
any_hostages_reachedm_bAnyHostageReached
has_bombitesm_bMapHasBombTarget
has_rescue_zonem_bMapHasRescueZone
has_buy_zonem_bMapHasBuyZone
is_matchmakingm_bIsQueuedMatchmaking
match_making_modem_nQueuedMatchmakingMode
is_valve_dedicated_serverm_bIsValveDS
gungame_prog_weap_ctm_iNumGunGameProgressiveWeaponsCT
gungame_prog_weap_tm_iNumGunGameProgressiveWeaponsT
spectator_slot_countm_iSpectatorSlotCount
is_match_startedm_bHasMatchStarted
n_best_of_mapsm_numBestOfMaps
is_bomb_droppedm_bBombDropped
is_bomb_plantedm_bBombPlanted
round_win_statusm_iRoundWinStatus
round_win_reasonm_eRoundWinReason
terrorist_cant_buym_bTCantBuy
ct_cant_buym_bCTCantBuy
ct_losing_streakm_iNumConsecutiveCTLoses
t_losing_streakm_iNumConsecutiveTerroristLoses
survival_start_timem_flSurvivalStartTime
round_in_progressm_bRoundInProgress

Weapon

NameReal name
active_weapon_namem_iItemDefinitionIndex + lookup
active_weapon_skinm_iRawValue32 + lookup
active_weapon_ammom_iClip1
active_weapon_original_ownerm_OriginalOwnerXuidLow + m_OriginalOwnerXuidHigh
total_ammo_leftm_pReserveAmmo
item_def_idxm_iItemDefinitionIndex
weapon_qualitym_iEntityQuality
entity_lvlm_iEntityLevel
item_id_highm_iItemIDHigh
item_id_lowm_iItemIDLow
item_account_idm_iAccountID
inventory_positionm_iInventoryPosition
is_initializedm_bInitialized
econ_item_attribute_def_idxm_iAttributeDefinitionIndex
initial_valuem_flInitialValue
refundable_currencym_nRefundableCurrency
set_bonusm_bSetBonus
custom_namem_szCustomName
orig_owner_xuid_lowm_OriginalOwnerXuidLow
orig_owner_xuid_highm_OriginalOwnerXuidHigh
fall_back_paint_kitm_nFallbackPaintKit
fall_back_seedm_nFallbackSeed
fall_back_wearm_flFallbackWear
fall_back_stat_trackm_nFallbackStatTrak
m_iStatem_iState
fire_seq_start_timem_flFireSequenceStartTime
fire_seq_start_time_changem_nFireSequenceStartTimeChange
is_player_fire_event_primarym_bPlayerFireEventIsPrimary
weapon_modem_weaponMode
accuracy_penaltym_fAccuracyPenalty
i_recoil_idxm_iRecoilIndex
fl_recoil_idxm_flRecoilIndex
is_burst_modem_bBurstMode
post_pone_fire_ready_timem_flPostponeFireReadyTime
is_in_reloadm_bInReload
reload_visually_completem_bReloadVisuallyComplete
dropped_at_timem_flDroppedAtTime
is_hauled_backm_bIsHauledBack
is_silencer_onm_bSilencerOn
time_silencer_switch_completem_flTimeSilencerSwitchComplete
orig_team_numberm_iOriginalTeamNumber
prev_ownerm_hPrevOwner
last_shot_timem_fLastShotTime
iron_sight_modem_iIronSightMode
num_empty_attacksm_iNumEmptyAttacks
zoom_lvlm_zoomLevel
burst_shots_remainingm_iBurstShotsRemaining
needs_bolt_actionm_bNeedsBoltAction
next_primary_attack_tickm_nNextPrimaryAttackTick
next_primary_attack_tick_ratiom_flNextPrimaryAttackTickRatio
next_secondary_attack_tickm_nNextSecondaryAttackTick
next_secondary_attack_tick_ratiom_flNextSecondaryAttackTickRatio
weapon_floatm_iRawValue32
weapon_paint_seedm_iRawValue32
weapon_stickersm_iRawValue32

usercommands

NameReal name
usercmd_viewangle_x-
usercmd_viewangle_y-
usercmd_viewangle_z-
usercmd_buttonstate_1-
usercmd_buttonstate_2-
usercmd_buttonstate_3-
usercmd_consumed_server_angle_changes-
usercmd_forward_move-
usercmd_left_move-
usercmd_impulse-
usercmd_mouse_dx-
usercmd_mouse_dy-
usercmd_left_hand_desired-
usercmd_weapon_select-
usercmd_input_history-

Aggregate stats (updates one time per round)

NameReal name
kills_totalm_iKills
deaths_totalm_iDeaths
assists_totalm_iAssists
alive_time_totalm_iLiveTime
headshot_kills_totalm_iHeadShotKills
ace_rounds_totalm_iEnemy5Ks
4k_rounds_totalm_iEnemy4Ks
3k_rounds_totalm_iEnemy3Ks
damage_totalm_iDamage
objective_totalm_iObjective
utility_damage_totalm_iUtilityDamage
enemies_flashed_totalm_iEnemiesFlashed
equipment_value_totalm_iEquipmentValue
money_saved_totalm_iMoneySaved
kill_reward_totalm_iKillReward
cash_earned_totalm_iCashEarned

Other parsers

Go: https://github.com/markus-wa/demoinfocs-golang
C#: https://github.com/saul/demofile-net
Python: https://github.com/pnxenopoulos/awpy
Java: https://github.com/skadistats/clarity

Acknowledgements

Without Dotabuff's dota 2 parser "manta" this would not have been possible. Check it out: https://github.com/dotabuff/manta

The dota 2 demo format is very similar to CS2 demo format with only a few minor changes.

Thank you to Clarity for an elegant implementation of sendtables

Thank you to Dandrews for figuring out voice data extraction