Help us shape the future of Go! Take the 2026 Go Developer Survey

s2replay

package module
v0.0.0-...-aa8bed7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: MIT Imports: 14 Imported by: 0

README

s2replay

A modern Source 2 (PBDEMS2) replay parser. The first release targets Deadlock (Citadel) only and is tuned to emit a clean, attributed event stream for downstream analysis.

Status

The Deadlock parser is usable end to end. It decodes PBDEMS2 containers, dispatches generated Source 2 and Citadel messages, tracks entity and modifier state, emits attributed JSONL events, and derives replay-local analysis data. The public event stream remains at schema version 1. The separate analysis output is at schema version 2 and may evolve as more replay evidence becomes available.

What it emits

s2replay decodes a .dem replay into a stream of events keyed by entity index and game time:

  • Damage events with full context (attacker, victim, inflictor, ability, pre/post health and shield, absorbed, crit, effectiveness).
  • Modifier add/remove/refresh with duration, caster, parent entity, and stack count.
  • Item purchase events tying an item to its owner at a game time.
  • Per-tick entity samples for hero health, shield, position, and facing. Every hero pawn is sampled at each tick it is updated, which in a ranked replay is every tick for all 12 heroes. Facing is the eye angles in degrees (facing_x pitch, facing_y yaw, facing_z roll, with has_facing); when a component is zero, the JSON omits it.
  • Ability casts (ability_cast): one event each time an ability or item entity starts a new cooldown. entity is the ability entity and owner_entity the hero pawn that holds it, with the class name, slot, subclass id and the cooldown window in server seconds. Movement abilities (jump, dash, slide, zipline, mantle, melee) cast too; filter by class name. It covers every important_ability_used the game sends and many more.
  • Lane trooper samples (trooper_sample): the same entity sample payload for CNPC_Trooper entities, at most four a second per trooper, so a consumer can draw each wave. A trooper that stops sampling has died or left sight.

These streams let a consumer cluster events into fights and build per-item behavior profiles.

Usage

s2replay version
s2replay parse <demo.dem>
s2replay emit --format jsonl <demo.dem>
s2replay analyze --format json [--combat-gap seconds] [--combat-events damage,modifier] <demo.dem>

parse opens a PBDEMS2 demo, walks its outer command stream, and prints the file header plus a monotonic tick / game-time stream.

emit --format jsonl writes one JSON event per line to stdout. Each event has the common keys below:

key type meaning
schema_version integer Event schema version. Current value: 1.
type string One of damage, modifier, purchase, entity_sample, trooper_sample, ability_cast, and the other game-authored types.
tick integer Demo tick, with pre-game sentinel ticks normalized to 0.
game_time number Parser game time in seconds.
entity integer Primary entity index, or -1 when the event is not entity-owned.
player_slot integer Deadlock player slot, or -1 when not attributed.
owned_items integer array Current item ability IDs for the attributed player. Present on attributed attacker-side events and purchase events.

Type-specific payloads live under a key matching the event type:

  • damage: full Deadlock damage context from CCitadelUserMessage_Damage.
  • modifier: modifier lifecycle details from ActiveModifiers.
  • purchase: player_slot, user_id, ability_id, change, sell, quickbuy, and source.
  • entity_sample: hero health, shield, position, and facing sample fields.
  • trooper_sample: an entity_sample payload for a lane trooper.
  • ability_cast: the ability use described above.

Analysis primitives

The Go package github.com/paralin/s2replay/analysis derives replay-local timelines from the typed event stream:

  • analysis.Build(events) returns inventory ownership intervals, entity health samples, modifier lifecycle intervals, and quality counters.
  • analysis.BuildCombatWindows(events, options) groups caller-selected events with caller-owned timing policy.

These primitives use only replay events. They do not fetch Deadlock API data, read static item catalogs, score item behavior, or assign item-profile policy. Modifier source ids remain raw replay source identity; they are not promoted to parser-owned item ids without fixture-backed proof. Entity samples in analysis output include item context derived from parser-owned inventory state without changing the default emit --format jsonl event stream.

analyze --format json is the process boundary for non-Go consumers. It emits a separate analysis schema:

  • schema_version: analysis schema version. Current value: 2.
  • analysis: the Go analysis result.
  • combat_windows: present only when --combat-gap is non-negative.

Combat windows have no parser default fight policy. Callers must pass --combat-gap and may restrict selected events with --combat-events. Each combat window reports the selected event count, per-type counts, first and last selected event indexes, player slots observed in the window, primary entity ids, damage attacker/victim ids, and modifier parent ids. Player slots include damage victims when an earlier entity sample established the victim entity's slot. The default emit --format jsonl event stream is unchanged by analysis output.

Entity samples retain both the index and serial from owner and controller pawn handles (owner_entity_serial and pawn_entity_serial). Runback equipment and controller attribution require both to match the current pawn. Regenerate saved entity samples from the replay if they predate these fields; an index alone cannot establish ownership after an entity is replaced. Runback facts keep their existing schema and record the parser revision used for extraction.

Runback tick provenance also includes the last observed CNETMsg_Tick as server_tick, with its source demo tick and freshness. Cooldown deadlines use the server clock; neither demo time nor the header's server_start_tick is an observed substitute. Older facts without server_tick must be re-extracted before restoring absolute cooldown deadlines.

Runback alive verdicts prefer recorded native life state when available, then fall back to health or entity activity. Dead NPCs can retain positive health. Re-extract older facts before restoring a replay world so those remnants are not recreated as live actors.

Protocol generation

The Deadlock protocol Go package under protocol/ is generated, never hand-edited. generator/update_protos.bash copies a minimal Deadlock allow-list from the pinned generator/Protobufs submodule, flattens it into one protocol package, and strips proto2 extensions, custom options, and the heavy Steam-GC / descriptor imports the wire decode path does not need. Generation runs through the aperturerobotics/common (aptre) reflect-free protobuf-go-lite pipeline with Go outputs only:

make gen

This is reproducible: a clean re-run produces no diff. Do not edit *.pb.go.

Development

make lint
make test

License

MIT. See LICENSE.

Documentation

Overview

Package s2replay parses Source 2 PBDEMS2 replays and emits a clean, attributed event stream for downstream analysis.

The first release targets Deadlock (Citadel) only. The parser decodes the demo container, network and Citadel user messages, the entity framework, and the active-modifier table, then exposes damage, modifier, purchase, and entity-sample events keyed by entity index and game time.

Index

Constants

View Source
const DefaultTickInterval = 1.0 / 64.0

DefaultTickInterval is the seconds-per-tick used until the server's CSVCMsg_ServerInfo tick_interval is decoded from the packet stream. Source 2 servers record at 1/64 s by default; SetInterval overwrites this with the exact value once message dispatch is in place.

View Source
const ParserSourceDigest = "ffccb7c19cf6214733cbacfad41b234e6b95637a61ae3133b6150e5c63224934"

ParserSourceDigest identifies the canonical parser source and module inputs.

View Source
const PreGameTick = ^uint32(0)

PreGameTick is the sentinel tick Source 2 stamps on pre-game commands; it is ignored for clock advancement so game time stays monotonic.

View Source
const Version = "0.0.0-dev"

Version is the current s2replay release identifier.

Variables

View Source
var ErrMatchClockNotReached = errors.New("the replay ends before that match time")

ErrMatchClockNotReached reports a replay that ends before the requested match time.

Functions

func BuildRevision

func BuildRevision() (string, bool)

BuildRevision identifies the parser source in the running binary. Standalone builds require clean VCS metadata; dependencies require a checksummed module pseudo-version. Local replacements and unknown revisions are refused.

func MatchClock

func MatchClock(rules *Entity, clock *Clock) (float64, error)

MatchClock reads elapsed match seconds from the recorded rules and network clock. Missing fields remain an error; demo duration is not match time.

Types

type AbilityCastEvent

type AbilityCastEvent struct {
	Tick              uint32  `json:"tick"`
	GameTime          float64 `json:"game_time"`
	ClassName         string  `json:"class_name"`
	OwnerEntity       int32   `json:"owner_entity"`
	CooldownStart     float32 `json:"cooldown_start"`
	CooldownEnd       float32 `json:"cooldown_end"`
	CastCompletedTime float32 `json:"cast_completed_time"`
	AbilitySlot       uint32  `json:"ability_slot,omitempty"`
	HasAbilitySlot    bool    `json:"has_ability_slot,omitempty"`
	SubclassID        uint32  `json:"subclass_id,omitempty"`
	HasSubclassID     bool    `json:"has_subclass_id,omitempty"`
}

AbilityCastEvent is one use of an ability or item, observed as a new cooldown start on the ability entity. Entity on the enclosing Event is the ability entity; OwnerEntity is the hero pawn that holds it, or -1 when the owner is not resolved. Times are game seconds.

type AbilityChargesEvent

type AbilityChargesEvent struct {
	Tick             uint32  `json:"tick"`
	GameTime         float64 `json:"game_time"`
	ClassName        string  `json:"class_name"`
	RemainingCharges int32   `json:"remaining_charges"`
}

AbilityChargesEvent is one ability charge-count change observed on an ability entity (dash charges are the primary consumer). Emitted only when the count differs from the previously seen value for that entity.

type AbilityNotifyEvent

type AbilityNotifyEvent struct {
	Tick                uint32  `json:"tick"`
	GameTime            float64 `json:"game_time"`
	EntindexVictim      int32   `json:"entindex_victim"`
	EntindexAttacker    int32   `json:"entindex_attacker"`
	AbilityID           uint32  `json:"ability_id"`
	StatusImpact        uint32  `json:"status_impact"`
	HasEntindexVictim   bool    `json:"has_entindex_victim"`
	HasEntindexAttacker bool    `json:"has_entindex_attacker"`
	HasAbilityID        bool    `json:"has_ability_id"`
	HasStatusImpact     bool    `json:"has_status_impact"`
}

AbilityNotifyEvent is one game-authored ability contact notification.

type Clock

type Clock struct {
	// contains filtered or unexported fields
}

Clock converts demo ticks to game-time seconds. The interval starts at DefaultTickInterval and is replaced by the exact server value when known; callers read GameTime and never depend on the placeholder interval.

func (*Clock) GameTime

func (c *Clock) GameTime() float64

GameTime returns the current tick expressed in seconds.

func (*Clock) ServerTick

func (c *Clock) ServerTick() (tick, sourceDemoTick uint32, known bool)

ServerTick returns the last observed network tick and its enclosing demo tick. It is absent until a CNETMsg_Tick with an explicit tick is decoded outside signon. No header offset, extrapolation, or demo-clock value is substituted.

func (*Clock) SetInterval

func (c *Clock) SetInterval(seconds float64)

SetInterval replaces the seconds-per-tick with the exact server value.

func (*Clock) Tick

func (c *Clock) Tick() uint32

Tick returns the most recent real demo tick.

func (*Clock) TickInterval

func (c *Clock) TickInterval() float64

TickInterval returns the current seconds-per-tick.

func (*Clock) TickIntervalKnown

func (c *Clock) TickIntervalKnown() bool

TickIntervalKnown reports whether ServerInfo provided the exact interval.

type Command

type Command struct {
	Kind    protocol.EDemoCommands
	Tick    uint32
	Payload []byte
}

Command is one outer demo record: its kind, the tick it applies to, and the decompressed payload bytes awaiting message decode.

type ControllerSample

type ControllerSample struct {
	Tick               uint32  `json:"tick"`
	GameTime           float64 `json:"game_time"`
	Entity             int32   `json:"entity"`
	ClassID            int32   `json:"class_id"`
	ClassName          string  `json:"class_name"`
	SteamID            uint64  `json:"steam_id"`
	PlayerName         string  `json:"player_name"`
	NetWorth           int32   `json:"net_worth"`
	HeroDamage         int32   `json:"hero_damage"`
	HeroHealing        int32   `json:"hero_healing"`
	CreepGold          int32   `json:"creep_gold"`
	CreepGoldKill      int32   `json:"creep_gold_kill"`
	CreepGoldNeutral   int32   `json:"creep_gold_neutral"`
	CreepGoldAirOrb    int32   `json:"creep_gold_air_orb"`
	CreepGoldGroundOrb int32   `json:"creep_gold_ground_orb"`
	CreepGoldDeny      int32   `json:"creep_gold_deny"`
	CreepGoldSoloBonus int32   `json:"creep_gold_solo_bonus"`
}

ControllerSample is one periodic snapshot of a player controller entity: the live scoreboard the game itself maintains.

type DamageEvent

type DamageEvent struct {
	Tick                     uint32  `json:"tick"`
	GameTime                 float64 `json:"game_time"`
	Damage                   int32   `json:"damage"`
	PreDamage                float32 `json:"pre_damage"`
	VictimHealthNew          int32   `json:"victim_health_new"`
	VictimHealthMax          int32   `json:"victim_health_max"`
	DamageAbsorbed           float32 `json:"damage_absorbed"`
	Effectiveness            float32 `json:"effectiveness"`
	CritDamage               float32 `json:"crit_damage"`
	Hits                     int32   `json:"hits"`
	Attacker                 int32   `json:"attacker"`
	Victim                   int32   `json:"victim"`
	Inflictor                int32   `json:"inflictor"`
	AbilityEntity            int32   `json:"ability_entity"`
	AbilityID                uint32  `json:"ability_id"`
	DamageType               int32   `json:"damage_type"`
	CitadelDamageType        int32   `json:"citadel_damage_type"`
	AttackingObject          int32   `json:"attacking_object"`
	VictimShieldNew          int32   `json:"victim_shield_new"`
	VictimShieldMax          int32   `json:"victim_shield_max"`
	HealthLost               int32   `json:"health_lost"`
	HitgroupID               int32   `json:"hitgroup_id"`
	IsSecondaryStat          bool    `json:"is_secondary_stat"`
	OriginX                  float32 `json:"origin_x"`
	OriginY                  float32 `json:"origin_y"`
	OriginZ                  float32 `json:"origin_z"`
	DamageDirectionX         float32 `json:"damage_direction_x"`
	DamageDirectionY         float32 `json:"damage_direction_y"`
	DamageDirectionZ         float32 `json:"damage_direction_z"`
	ServerTick               int32   `json:"server_tick"`
	Flags                    uint64  `json:"flags"`
	AttackerClass            uint32  `json:"attacker_class"`
	VictimClass              uint32  `json:"victim_class"`
	PreDamageDeprecated      int32   `json:"pre_damage_deprecated"`
	DamageAbsorbedDeprecated int32   `json:"damage_absorbed_deprecated"`
}

DamageEvent is the Phase 3 product projection of a Deadlock damage user message. Later phases add entity class and item attribution around it.

type DamageSummaryEvent

type DamageSummaryEvent struct {
	Tick            uint32                        `json:"tick"`
	GameTime        float64                       `json:"game_time"`
	PlayerSlot      int32                         `json:"player_slot"`
	TotalDamage     int32                         `json:"total_damage"`
	LostGold        int32                         `json:"lost_gold"`
	StartTime       float32                       `json:"start_time"`
	EndTime         float32                       `json:"end_time"`
	DamageRecords   []DamageSummaryRecord         `json:"damage_records"`
	ModifierRecords []DamageSummaryModifierRecord `json:"modifier_records"`
}

DamageSummaryEvent is the engine-compiled recent-damage summary the game itself uses for death recap and scoreboard attribution.

type DamageSummaryModifierRecord

type DamageSummaryModifierRecord struct {
	AbilityID      uint32  `json:"ability_id"`
	ModifierTypeID uint32  `json:"modifier_type_id"`
	EntindexCaster int32   `json:"entindex_caster"`
	StartTime      float32 `json:"start_time"`
	EndTime        float32 `json:"end_time"`
	Debuff         bool    `json:"debuff"`
}

DamageSummaryModifierRecord is one engine-attributed modifier entry in a RecentDamageSummary user message.

type DamageSummaryRecord

type DamageSummaryRecord struct {
	Damage         int32   `json:"damage"`
	Hits           int32   `json:"hits"`
	DamageType     uint32  `json:"damage_type"`
	HeroID         uint32  `json:"hero_id"`
	AbilityID      uint32  `json:"ability_id"`
	AttackerClass  uint32  `json:"attacker_class"`
	DamageAbsorbed float32 `json:"damage_absorbed"`
	IsKillingBlow  bool    `json:"is_killing_blow"`
	VictimHeroID   uint32  `json:"victim_hero_id"`
	PreDamage      float32 `json:"pre_damage"`
	CritDamage     float32 `json:"crit_damage"`
}

DamageSummaryRecord is one engine-attributed damage entry in a RecentDamageSummary user message.

type Entity

type Entity struct {
	// contains filtered or unexported fields
}

Entity is the parser-owned current state for one networked entity.

func (*Entity) Bool

func (e *Entity) Bool(name string) (bool, bool)

Bool returns the current field value as a bool when present.

func (*Entity) ClassID

func (e *Entity) ClassID() int32

ClassID returns the entity class id.

func (*Entity) ClassName

func (e *Entity) ClassName() string

ClassName returns the entity class name.

func (*Entity) Float32

func (e *Entity) Float32(name string) (float32, bool)

Float32 returns the current field value as a float32 when possible.

func (*Entity) Get

func (e *Entity) Get(name string) any

Get returns the current decoded field value for name.

func (*Entity) Index

func (e *Entity) Index() int32

Index returns the networked entity index.

func (*Entity) Int32

func (e *Entity) Int32(name string) (int32, bool)

Int32 returns the current field value as an int32 when it can be represented without changing sign.

func (*Entity) String

func (e *Entity) String(name string) (string, bool)

String returns the current field value as a string when present.

func (*Entity) UInt32

func (e *Entity) UInt32(name string) (uint32, bool)

UInt32 returns the current field value as a uint32 when it fits.

type EntitySample

type EntitySample struct {
	// CameraAngles is the client camera orientation, distinct from aim-facing angles.
	CameraAngles [3]float32 `json:"camera_angles"`
	// CameraAnglesTicks identifies the last update of each camera component.
	CameraAnglesTicks [3]uint32 `json:"camera_angles_ticks"`
	// CameraAnglesSourceFields names the recorded fields for each component.
	CameraAnglesSourceFields [3]string `json:"camera_angles_source_fields"`
	// HasCameraAngles distinguishes observed zero components from missing evidence.
	HasCameraAngles [3]bool `json:"has_camera_angles"`

	// LifeState is the native lifecycle state; zero means alive, independently of health.
	LifeState uint32 `json:"life_state,omitempty"`
	// LifeStateTick identifies the last recorded lifecycle update.
	LifeStateTick uint32 `json:"life_state_tick,omitempty"`
	// HasLifeState distinguishes a recorded alive state from absent evidence.
	HasLifeState bool `json:"has_life_state,omitempty"`

	Tick                 uint32   `json:"tick"`
	GameTime             float64  `json:"game_time"`
	Entity               int32    `json:"entity"`
	EntitySerial         int32    `json:"entity_serial,omitempty"`
	ClassID              int32    `json:"class_id"`
	ClassName            string   `json:"class_name"`
	Health               float32  `json:"health"`
	MaxHealth            float32  `json:"max_health"`
	Shield               float32  `json:"shield"`
	MaxShield            float32  `json:"max_shield"`
	HealthTick           uint32   `json:"health_tick,omitempty"`
	MaxHealthTick        uint32   `json:"max_health_tick,omitempty"`
	ShieldTick           uint32   `json:"shield_tick,omitempty"`
	MaxShieldTick        uint32   `json:"max_shield_tick,omitempty"`
	PositionX            float32  `json:"position_x"`
	PositionY            float32  `json:"position_y"`
	PositionZ            float32  `json:"position_z"`
	PositionXTick        uint32   `json:"position_x_tick,omitempty"`
	PositionYTick        uint32   `json:"position_y_tick,omitempty"`
	PositionZTick        uint32   `json:"position_z_tick,omitempty"`
	PositionXSourceField string   `json:"position_x_source_field,omitempty"`
	PositionYSourceField string   `json:"position_y_source_field,omitempty"`
	PositionZSourceField string   `json:"position_z_source_field,omitempty"`
	FacingXSourceField   string   `json:"facing_x_source_field,omitempty"`
	FacingYSourceField   string   `json:"facing_y_source_field,omitempty"`
	FacingZSourceField   string   `json:"facing_z_source_field,omitempty"`
	VelocityXSourceField string   `json:"velocity_x_source_field,omitempty"`
	VelocityYSourceField string   `json:"velocity_y_source_field,omitempty"`
	VelocityZSourceField string   `json:"velocity_z_source_field,omitempty"`
	FacingX              float32  `json:"facing_x,omitempty"`
	FacingY              float32  `json:"facing_y,omitempty"`
	FacingZ              float32  `json:"facing_z,omitempty"`
	VelocityX            float32  `json:"velocity_x,omitempty"`
	VelocityY            float32  `json:"velocity_y,omitempty"`
	VelocityZ            float32  `json:"velocity_z,omitempty"`
	FacingXTick          uint32   `json:"facing_x_tick,omitempty"`
	FacingYTick          uint32   `json:"facing_y_tick,omitempty"`
	FacingZTick          uint32   `json:"facing_z_tick,omitempty"`
	VelocityXTick        uint32   `json:"velocity_x_tick,omitempty"`
	VelocityYTick        uint32   `json:"velocity_y_tick,omitempty"`
	VelocityZTick        uint32   `json:"velocity_z_tick,omitempty"`
	HasFacing            bool     `json:"has_facing,omitempty"`
	HasFacingX           bool     `json:"has_facing_x,omitempty"`
	HasFacingY           bool     `json:"has_facing_y,omitempty"`
	HasFacingZ           bool     `json:"has_facing_z,omitempty"`
	HasVelocity          bool     `json:"has_velocity,omitempty"`
	HasVelocityX         bool     `json:"has_velocity_x,omitempty"`
	HasVelocityY         bool     `json:"has_velocity_y,omitempty"`
	HasVelocityZ         bool     `json:"has_velocity_z,omitempty"`
	HeroID               uint32   `json:"hero_id,omitempty"`
	HeroIDTick           uint32   `json:"hero_id_tick,omitempty"`
	Team                 int32    `json:"team,omitempty"`
	TeamTick             uint32   `json:"team_tick,omitempty"`
	HasHealth            bool     `json:"has_health"`
	HasShield            bool     `json:"has_shield"`
	HasPosition          bool     `json:"has_position"`
	HasHeroID            bool     `json:"has_hero_id"`
	HasTeam              bool     `json:"has_team"`
	InvalidFields        []string `json:"invalid_fields,omitempty"`

	// PlayerSlot is the replay-local lobby slot attributed by the parser when
	// known; negative means unattributed.
	PlayerSlot int32 `json:"player_slot,omitempty"`

	// Level is the observed hero level with its source tick.
	Level     uint32 `json:"level,omitempty"`
	LevelTick uint32 `json:"level_tick,omitempty"`
	HasLevel  bool   `json:"has_level,omitempty"`

	// SubclassID identifies the concrete VData definition behind generic classes.
	SubclassID     uint32 `json:"subclass_id,omitempty"`
	SubclassIDTick uint32 `json:"subclass_id_tick,omitempty"`
	HasSubclassID  bool   `json:"has_subclass_id,omitempty"`

	// AbilitySlot and UpgradeInfo retain equipment placement and the packed engine upgrade state.
	AbilitySlot     uint32 `json:"ability_slot,omitempty"`
	AbilitySlotTick uint32 `json:"ability_slot_tick,omitempty"`
	HasAbilitySlot  bool   `json:"has_ability_slot,omitempty"`
	UpgradeInfo     uint32 `json:"upgrade_info,omitempty"`
	UpgradeInfoTick uint32 `json:"upgrade_info_tick,omitempty"`
	HasUpgradeInfo  bool   `json:"has_upgrade_info,omitempty"`

	// OwnerEntity and OwnerEntitySerial identify the generation in m_hOwnerEntity.
	OwnerEntitySerial int32  `json:"owner_entity_serial,omitempty"`
	OwnerEntity       int32  `json:"owner_entity,omitempty"`
	OwnerEntityTick   uint32 `json:"owner_entity_tick,omitempty"`
	HasOwnerEntity    bool   `json:"has_owner_entity,omitempty"`

	// PawnEntity and PawnEntitySerial identify the controller hero pawn generation.
	PawnEntitySerial int32  `json:"pawn_entity_serial,omitempty"`
	PawnEntity       int32  `json:"pawn_entity,omitempty"`
	PawnEntityTick   uint32 `json:"pawn_entity_tick,omitempty"`
	HasPawnEntity    bool   `json:"has_pawn_entity,omitempty"`

	// NetWorth is the controller scoreboard net worth.
	NetWorth     int32  `json:"net_worth,omitempty"`
	NetWorthTick uint32 `json:"net_worth_tick,omitempty"`
	HasNetWorth  bool   `json:"has_net_worth,omitempty"`

	// Souls is the controller's unspent souls.
	Souls     int32  `json:"souls,omitempty"`
	SoulsTick uint32 `json:"souls_tick,omitempty"`
	HasSouls  bool   `json:"has_souls,omitempty"`

	// Lane is the controller's original lane assignment, numbered as
	// troopers' m_iLane.
	Lane     int32  `json:"lane,omitempty"`
	LaneTick uint32 `json:"lane_tick,omitempty"`
	HasLane  bool   `json:"has_lane,omitempty"`

	// RemainingCharges is the ability charge count.
	RemainingCharges     int32  `json:"remaining_charges,omitempty"`
	RemainingChargesTick uint32 `json:"remaining_charges_tick,omitempty"`
	HasRemainingCharges  bool   `json:"has_remaining_charges,omitempty"`

	// StaminaLatchTime is the observed stamina interpolation anchor in server seconds.
	StaminaLatchTime float32 `json:"stamina_latch_time,omitempty"`
	// StaminaLatchTimeTick identifies the source update for StaminaLatchTime.
	StaminaLatchTimeTick uint32 `json:"stamina_latch_time_tick,omitempty"`
	// HasStaminaLatchTime distinguishes observed zero from unavailable evidence.
	HasStaminaLatchTime bool `json:"has_stamina_latch_time,omitempty"`
	// StaminaLatchValue is the stamina value at StaminaLatchTime, not current stamina.
	StaminaLatchValue float32 `json:"stamina_latch_value,omitempty"`
	// StaminaLatchValueTick identifies the source update for StaminaLatchValue.
	StaminaLatchValueTick uint32 `json:"stamina_latch_value_tick,omitempty"`
	// HasStaminaLatchValue distinguishes observed zero from unavailable evidence.
	HasStaminaLatchValue bool `json:"has_stamina_latch_value,omitempty"`

	// CooldownStart records the start of the active cooldown interval in server seconds.
	CooldownStart float32 `json:"cooldown_start,omitempty"`
	// CooldownStartTick identifies the last replay update for CooldownStart.
	CooldownStartTick uint32 `json:"cooldown_start_tick,omitempty"`
	// HasCooldownStart distinguishes an observed zero from an absent timer.
	HasCooldownStart bool `json:"has_cooldown_start,omitempty"`

	// ChargeRechargeStart records the start of the charge recovery interval in server seconds.
	ChargeRechargeStart float32 `json:"charge_recharge_start,omitempty"`
	// ChargeRechargeStartTick identifies the last replay update for ChargeRechargeStart.
	ChargeRechargeStartTick uint32 `json:"charge_recharge_start_tick,omitempty"`
	// HasChargeRechargeStart distinguishes an observed zero from an absent timer.
	HasChargeRechargeStart bool `json:"has_charge_recharge_start,omitempty"`

	// ChargeRechargeEnd records the end of the charge recovery interval in server seconds.
	ChargeRechargeEnd float32 `json:"charge_recharge_end,omitempty"`
	// ChargeRechargeEndTick identifies the last replay update for ChargeRechargeEnd.
	ChargeRechargeEndTick uint32 `json:"charge_recharge_end_tick,omitempty"`
	// HasChargeRechargeEnd distinguishes an observed zero from an absent timer.
	HasChargeRechargeEnd bool `json:"has_charge_recharge_end,omitempty"`

	// CooldownEnd is the ability cooldown end time in game seconds.
	CooldownEnd     float32 `json:"cooldown_end,omitempty"`
	CooldownEndTick uint32  `json:"cooldown_end_tick,omitempty"`
	HasCooldownEnd  bool    `json:"has_cooldown_end,omitempty"`

	// Scoreboard components observed on the controller at sample time.
	Deaths         int32  `json:"deaths,omitempty"`
	DeathsTick     uint32 `json:"deaths_tick,omitempty"`
	HasDeaths      bool   `json:"has_deaths,omitempty"`
	LastHits       int32  `json:"last_hits,omitempty"`
	LastHitsTick   uint32 `json:"last_hits_tick,omitempty"`
	HasLastHits    bool   `json:"has_last_hits,omitempty"`
	Denies         int32  `json:"denies,omitempty"`
	DeniesTick     uint32 `json:"denies_tick,omitempty"`
	HasDenies      bool   `json:"has_denies,omitempty"`
	KillStreak     int32  `json:"kill_streak,omitempty"`
	KillStreakTick uint32 `json:"kill_streak_tick,omitempty"`
	HasKillStreak  bool   `json:"has_kill_streak,omitempty"`
	HeroDamage     int32  `json:"hero_damage,omitempty"`
	HeroDamageTick uint32 `json:"hero_damage_tick,omitempty"`
	HasHeroDamage  bool   `json:"has_hero_damage,omitempty"`
	// Grounded and Crouching retain observed movement state, including false.
	Grounded  bool `json:"grounded"`
	Crouching bool `json:"crouching"`
	// HasGrounded and HasCrouching distinguish observations from absent fields.
	HasGrounded  bool `json:"has_grounded,omitempty"`
	HasCrouching bool `json:"has_crouching,omitempty"`
}

EntitySample is a typed snapshot of one entity's state at a tick, consumed by downstream event code.

func (EntitySample) MatchesHandle

func (s EntitySample) MatchesHandle(handle uint32) bool

MatchesHandle reports whether a valid full handle names this exact snapshot entity incarnation, including its serial rather than only its index.

type Event

type Event struct {
	SchemaVersion int       `json:"schema_version"`
	Type          EventType `json:"type"`
	Tick          uint32    `json:"tick"`
	GameTime      float64   `json:"game_time"`
	Entity        int32     `json:"entity"`
	EntitySerial  int32     `json:"entity_serial,omitempty"`
	PlayerSlot    int32     `json:"player_slot"`
	// OwnedItems is the player item set at event time when attribution is
	// available.
	OwnedItems           []uint32                   `json:"owned_items,omitempty"`
	Damage               *DamageEvent               `json:"damage,omitempty"`
	Modifier             *ModifierEvent             `json:"modifier,omitempty"`
	Purchase             *PurchaseEvent             `json:"purchase,omitempty"`
	EntitySample         *EntitySample              `json:"entity_sample,omitempty"`
	DamageSummary        *DamageSummaryEvent        `json:"damage_summary,omitempty"`
	PostMatch            *PostMatchEvent            `json:"post_match,omitempty"`
	ControllerSample     *ControllerSample          `json:"controller_sample,omitempty"`
	KillStreak           *KillStreakEvent           `json:"kill_streak,omitempty"`
	StaminaConsumed      *StaminaConsumedEvent      `json:"stamina_consumed,omitempty"`
	ImportantAbilityUsed *ImportantAbilityUsedEvent `json:"important_ability_used,omitempty"`
	AbilityNotify        *AbilityNotifyEvent        `json:"ability_notify,omitempty"`
	JumpState            *JumpStateEvent            `json:"jump_state,omitempty"`
	AbilityCharges       *AbilityChargesEvent       `json:"ability_charges,omitempty"`
	AbilityCast          *AbilityCastEvent          `json:"ability_cast,omitempty"`
	Objective            *ObjectiveEvent            `json:"objective_event,omitempty"`
}

Event is the unified typed stream used by downstream Deadlock analysis.

func (*Event) EmitProto

func (e *Event) EmitProto(dst []byte) ([]byte, error)

EmitProto marshals the event as a length-delimited protobuf frame appended to dst.

func (*Event) ToProto

func (e *Event) ToProto() *protocol.ReplayEvent

ToProto converts the unified event to its framed binary representation.

type EventType

type EventType string

EventType identifies a record in the unified replay event stream.

const (
	// EventSchemaVersion is the schema version emitted with each replay event.
	EventSchemaVersion = 1

	// EventDamage identifies a damage event.
	EventDamage EventType = "damage"
	// EventModifier identifies a modifier lifecycle event.
	EventModifier EventType = "modifier"
	// EventPurchase identifies an item or ability ownership transition.
	EventPurchase EventType = "purchase"
	// EventEntitySample identifies a sampled entity state.
	EventEntitySample EventType = "entity_sample"
	// EventDamageSummary identifies an engine-compiled recent-damage summary.
	EventDamageSummary EventType = "damage_summary"
	// EventPostMatch identifies the engine-compiled post-match summary.
	EventPostMatch EventType = "post_match"
	// EventControllerSample identifies a player controller scoreboard sample.
	EventControllerSample EventType = "controller_sample"
	// EventKillStreak identifies a kill streak update from the user message
	// stream.
	EventKillStreak EventType = "kill_streak"
	// EventStaminaConsumed identifies a stamina spend from the user message
	// stream.
	EventStaminaConsumed EventType = "stamina_consumed"
	// EventImportantAbilityUsed identifies the game-authored ability-use
	// notification from the user message stream.
	EventImportantAbilityUsed EventType = "important_ability_used"
	// EventAbilityNotify identifies the game-authored ability contact
	// notification from the user message stream.
	EventAbilityNotify EventType = "ability_notify"
	// EventJumpState identifies an observed jump-ability state transition.
	EventJumpState EventType = "jump_state"
	// EventAbilityCharges identifies an ability charge-count change from the
	// entity stream.
	EventAbilityCharges EventType = "ability_charges"
	// EventTrooperSample identifies a throttled lane trooper state sample; it
	// carries an EntitySample.
	EventTrooperSample EventType = "trooper_sample"
	// EventAbilityCast identifies an ability or item use, observed as a new
	// cooldown start on the ability entity.
	EventAbilityCast EventType = "ability_cast"
	// EventObjective identifies a map objective lifecycle observation.
	EventObjective EventType = "objective_event"
)

type ImportantAbilityUsedEvent

type ImportantAbilityUsedEvent struct {
	Tick           uint32  `json:"tick"`
	GameTime       float64 `json:"game_time"`
	Player         uint32  `json:"player"`
	Caster         uint32  `json:"caster"`
	AbilityName    string  `json:"ability_name"`
	HasPlayer      bool    `json:"has_player"`
	HasCaster      bool    `json:"has_caster"`
	HasAbilityName bool    `json:"has_ability_name"`
}

ImportantAbilityUsedEvent is one game-authored important ability-use notification. Player and Caster retain the message's complete entity handles.

type JumpStateEvent

type JumpStateEvent struct {
	Tick                        uint32  `json:"tick"`
	GameTime                    float64 `json:"game_time"`
	ClassName                   string  `json:"class_name"`
	InitialObservation          bool    `json:"initial_observation"`
	Jumped                      bool    `json:"jumped"`
	DesiredAirJumpCount         int32   `json:"desired_air_jump_count"`
	ExecutedAirJumpCount        int32   `json:"executed_air_jump_count"`
	ConsecutiveAirJumps         int32   `json:"consecutive_air_jumps"`
	ConsecutiveWallJumps        int32   `json:"consecutive_wall_jumps"`
	CanDashJump                 bool    `json:"can_dash_jump"`
	InSlideJump                 bool    `json:"in_slide_jump"`
	HasJumped                   bool    `json:"has_jumped"`
	HasDesiredAirJumpCount      bool    `json:"has_desired_air_jump_count"`
	HasExecutedAirJumpCount     bool    `json:"has_executed_air_jump_count"`
	HasConsecutiveAirJumps      bool    `json:"has_consecutive_air_jumps"`
	HasConsecutiveWallJumps     bool    `json:"has_consecutive_wall_jumps"`
	HasCanDashJump              bool    `json:"has_can_dash_jump"`
	HasInSlideJump              bool    `json:"has_in_slide_jump"`
	ChangedJumped               bool    `json:"changed_jumped"`
	ChangedDesiredAirJumpCount  bool    `json:"changed_desired_air_jump_count"`
	ChangedExecutedAirJumpCount bool    `json:"changed_executed_air_jump_count"`
	ChangedConsecutiveAirJumps  bool    `json:"changed_consecutive_air_jumps"`
	ChangedConsecutiveWallJumps bool    `json:"changed_consecutive_wall_jumps"`
	ChangedCanDashJump          bool    `json:"changed_can_dash_jump"`
	ChangedInSlideJump          bool    `json:"changed_in_slide_jump"`
}

JumpStateEvent is one initial observation or changed sample from CCitadel_Ability_Jump. Field names retain the measured game-state semantics.

type KillStreakEvent

type KillStreakEvent struct {
	Tick         uint32  `json:"tick"`
	GameTime     float64 `json:"game_time"`
	PlayerPawn   uint32  `json:"player_pawn"`
	NumKills     int32   `json:"num_kills"`
	IsFirstBlood bool    `json:"is_first_blood"`
	StreakEnded  bool    `json:"streak_ended"`
	Duration     float32 `json:"duration"`
}

KillStreakEvent is one kill streak update: a player pawn reached num_kills consecutive kills. First blood and streak ends are flagged.

type Message

type Message struct {
	Kind     int32
	Name     string
	Tick     uint32
	GameTime float64
	Payload  decodedProto
	// contains filtered or unexported fields
}

Message is a decoded packet or user message with demo tick and game time.

func (*Message) DamageEvent

func (m *Message) DamageEvent() (DamageEvent, bool)

DamageEvent returns the full-context damage projection for this message.

type ModifierEvent

type ModifierEvent struct {
	// PayloadProto is a serialized CModifierTableEntry after same-instance merging.
	// JSON base64 preserves unknown fields and source float bits without interpretation.
	PayloadProto             []byte             `json:"payload_proto,omitempty"`
	Tick                     uint32             `json:"tick"`
	GameTime                 float64            `json:"game_time"`
	Transition               ModifierTransition `json:"transition"`
	TableIndex               int32              `json:"table_index"`
	Parent                   uint32             `json:"parent"`
	HasSerialNumber          bool               `json:"has_serial_number"`
	SerialNumber             uint32             `json:"serial_number"`
	ModifierSubclass         uint32             `json:"modifier_subclass"`
	StackCount               int32              `json:"stack_count"`
	MaxStackCount            int32              `json:"max_stack_count"`
	HasLastAppliedTime       bool               `json:"has_last_applied_time"`
	LastAppliedTime          float32            `json:"last_applied_time"`
	HasDuration              bool               `json:"has_duration"`
	Duration                 float32            `json:"duration"`
	Caster                   uint32             `json:"caster"`
	Ability                  uint32             `json:"ability"`
	AuraProviderSerialNumber int32              `json:"aura_provider_serial_number"`
	AuraProviderEHandle      uint32             `json:"aura_provider_ehandle"`
	AbilitySubclass          uint32             `json:"ability_subclass"`
	InAuraRange              bool               `json:"in_aura_range"`
	MatchedPrior             bool               `json:"matched_prior"`
}

ModifierEvent records an instance transition and the last observed payload. Presence distinguishes unknown serial/timing from explicit zero or indefinite duration.

type ModifierTransition

type ModifierTransition string
const (
	ModifierAdd     ModifierTransition = "add"
	ModifierRemove  ModifierTransition = "remove"
	ModifierRefresh ModifierTransition = "refresh"
)

type ObjectiveEvent

type ObjectiveEvent struct {
	Kind            string  `json:"kind"`
	ObjectiveTeam   int32   `json:"objective_team"`
	ObjectiveID     int32   `json:"objective_id"`
	KillingTeam     int32   `json:"killing_team"`
	EntityType      int32   `json:"entity_type"`
	BossesRemaining int32   `json:"bosses_remaining"`
	GameTimeF       float32 `json:"game_time_f"`
}

ObjectiveEvent is one map-objective lifecycle observation: mid boss spawn, boss damage, boss kill, or rejuvenator pickup.

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser walks a Source 2 PBDEMS2 demo container. It validates the header, yields the outer command stream one record at a time, and owns the Clock. Packet unpacking, message dispatch, and entity decoding layer on top of this container.

func NewParser

func NewParser(demo []byte) (*Parser, error)

NewParser validates the PBDEMS2 header and returns a Parser positioned at the first command. It reads demo as a stream; see NewParserFromReader.

func NewParserFromReader

func NewParserFromReader(ctx context.Context, src io.Reader) (*Parser, error)

NewParserFromReader validates the PBDEMS2 header and returns a Parser that reads the command stream from src one command at a time, so memory use does not grow with the size of the replay. The parser consumes src until it reports io.EOF and does not close it.

func NewParserWithContext

func NewParserWithContext(ctx context.Context, demo []byte) (*Parser, error)

NewParserWithContext constructs a parser whose command reads stop on context cancellation. It retains ctx for its lifetime. A command already being decoded finishes before the next read observes cancellation.

func (*Parser) Clock

func (p *Parser) Clock() *Clock

Clock returns the game-time clock advanced by Next.

func (*Parser) CollectDamage

func (p *Parser) CollectDamage(limit int) ([]DamageEvent, error)

CollectDamage reads up to limit damage events. A non-positive limit reads the whole demo.

func (*Parser) CollectEvents

func (p *Parser) CollectEvents(limit int) ([]Event, error)

CollectEvents reads up to limit unified events. A non-positive limit reads the whole demo.

func (*Parser) CollectModifierEvents

func (p *Parser) CollectModifierEvents(limit int) ([]ModifierEvent, error)

func (*Parser) FindEntity

func (p *Parser) FindEntity(index int32) *Entity

FindEntity returns the current parser state for an entity index, or nil when absent.

func (*Parser) FindEntityByHandle

func (p *Parser) FindEntityByHandle(handle uint64) *Entity

FindEntityByHandle returns the current entity for a Source 2 entity handle.

func (*Parser) Next

func (p *Parser) Next() (*Command, error)

Next reads the next outer command, decompressing its payload when the compression bit is set, and advances the clock. It returns io.EOF once the stream is exhausted or after Stop. A context-bound parser returns its context error before reading another command when canceled.

func (*Parser) NextDamage

func (p *Parser) NextDamage() (DamageEvent, error)

NextDamage returns the next decoded Deadlock damage event.

func (*Parser) NextEntitySample

func (p *Parser) NextEntitySample() (EntitySample, error)

NextEntitySample returns the next typed hero/entity sample from packet entity updates.

func (*Parser) NextEvent

func (p *Parser) NextEvent() (Event, error)

NextEvent returns the next unified typed event produced while walking the demo stream. Calling it declares an event consumer: entity samples then arrive only as events and are not also queued for NextEntitySample, so the parser holds no sample nobody reads.

func (*Parser) NextMessage

func (p *Parser) NextMessage() (*Message, error)

NextMessage returns the next decoded packet or user message. It unwraps DEM_Packet, DEM_SignonPacket, and DEM_FullPacket command payloads, routes inner ids through generated dispatch, and updates the clock from ServerInfo.

func (*Parser) NextModifierEvent

func (p *Parser) NextModifierEvent() (ModifierEvent, error)

func (*Parser) ReleasePendingQueues

func (p *Parser) ReleasePendingQueues()

ReleasePendingQueues releases decoded queues while preserving entity delta state.

func (*Parser) SampleEntity

func (p *Parser) SampleEntity(e *Entity) (EntitySample, bool)

SampleEntity samples e at the parser's current tick, with the same fields and world coordinates as an entity sample event. Event streams emit samples only for heroes; this reads any entity, such as an objective, on demand. It reports false when e is nil, inactive, or has no class.

func (*Parser) ServerWorld

func (p *Parser) ServerWorld() (game, mapName string)

ServerWorld identifies the active root world at the current parser tick. A loaded root world supersedes the server's bootstrap map. Multiple distinct non-bootstrap roots are ambiguous and return an empty map name.

func (*Parser) SetEventMode

func (p *Parser) SetEventMode(enabled bool)

SetEventMode configures unified event consumption without retaining duplicate samples.

func (*Parser) Stop

func (p *Parser) Stop()

Stop makes the next Next call report io.EOF.

func (*Parser) TickAtMatchClock

func (p *Parser) TickAtMatchClock(seconds float64) (uint32, error)

TickAtMatchClock advances the parser to the first tick whose match clock reaches seconds and returns it. The parser is consumed: a snapshot of that tick needs a fresh parser. A replay that ends first returns ErrMatchClockNotReached.

func (*Parser) WorldEntitySnapshot

func (p *Parser) WorldEntitySnapshot(tick uint32) ([]EntitySample, error)

WorldEntitySnapshot advances the parser through tick and samples every active entity once. A request older than the parser position is refused. It includes ability, projectile, and other ephemeral entities still active at the boundary; entities deleted before the boundary are absent. It consumes the parser and does not retain event records.

type PostMatchEvent

type PostMatchEvent struct {
	MatchID      uint64               `json:"match_id"`
	DurationS    uint32               `json:"duration_s"`
	MatchOutcome int32                `json:"match_outcome"`
	WinningTeam  int32                `json:"winning_team"`
	GameMode     int32                `json:"game_mode"`
	MatchMode    int32                `json:"match_mode"`
	Players      []PostMatchPlayer    `json:"players"`
	Objectives   []PostMatchObjective `json:"objectives"`
}

PostMatchEvent is the engine-compiled post-match summary embedded in the demo. It is the authoritative record for final builds, buy and sell times, and scoreboard totals.

type PostMatchObjective

type PostMatchObjective struct {
	LegacyObjectiveID     int32  `json:"legacy_objective_id"`
	TeamObjectiveID       int32  `json:"team_objective_id"`
	Team                  int32  `json:"team"`
	DestroyedTimeS        uint32 `json:"destroyed_time_s"`
	FirstDamageTimeS      uint32 `json:"first_damage_time_s"`
	CreepDamage           uint32 `json:"creep_damage"`
	CreepDamageMitigated  uint32 `json:"creep_damage_mitigated"`
	PlayerDamage          uint32 `json:"player_damage"`
	PlayerDamageMitigated uint32 `json:"player_damage_mitigated"`
	PlayerSpiritDamage    uint32 `json:"player_spirit_damage"`
}

PostMatchObjective is one map objective in the post-match record.

type PostMatchPlayer

type PostMatchPlayer struct {
	AccountID     uint32                `json:"account_id"`
	PlayerSlot    uint32                `json:"player_slot"`
	Team          int32                 `json:"team"`
	Kills         uint32                `json:"kills"`
	Deaths        uint32                `json:"deaths"`
	Assists       uint32                `json:"assists"`
	NetWorth      uint32                `json:"net_worth"`
	HeroID        uint32                `json:"hero_id"`
	LastHits      uint32                `json:"last_hits"`
	Denies        uint32                `json:"denies"`
	AbilityPoints uint32                `json:"ability_points"`
	Level         uint32                `json:"level"`
	Items         []PostMatchPlayerItem `json:"items"`
	Stats         []PostMatchPlayerStat `json:"stats"`
}

PostMatchPlayer is one player in the engine-compiled post-match summary.

type PostMatchPlayerItem

type PostMatchPlayerItem struct {
	ItemID          uint32 `json:"item_id"`
	GameTimeS       uint32 `json:"game_time_s"`
	SoldTimeS       uint32 `json:"sold_time_s"`
	UpgradeID       uint32 `json:"upgrade_id"`
	Flags           uint32 `json:"flags"`
	ImbuedAbilityID uint32 `json:"imbued_ability_id"`
	UpgradeInfo     uint32 `json:"upgrade_info"`
}

PostMatchPlayerItem is one item or ability purchase in the post-match record, with buy and sell times.

type PostMatchPlayerStat

type PostMatchPlayerStat struct {
	TimeStampS        uint32 `json:"time_stamp_s"`
	NetWorth          uint32 `json:"net_worth"`
	Kills             uint32 `json:"kills"`
	Deaths            uint32 `json:"deaths"`
	Assists           uint32 `json:"assists"`
	Level             uint32 `json:"level"`
	LastHits          uint32 `json:"last_hits"`
	Denies            uint32 `json:"denies"`
	PlayerDamage      uint32 `json:"player_damage"`
	PlayerDamageTaken uint32 `json:"player_damage_taken"`
	PlayerHealing     uint32 `json:"player_healing"`
	CreepDamage       uint32 `json:"creep_damage"`
	NeutralDamage     uint32 `json:"neutral_damage"`
	BossDamage        uint32 `json:"boss_damage"`
	DamageAbsorbed    uint32 `json:"damage_absorbed"`
	DamageMitigated   uint32 `json:"damage_mitigated"`
	ShotsHit          uint32 `json:"shots_hit"`
	ShotsMissed       uint32 `json:"shots_missed"`
	WeaponPower       uint32 `json:"weapon_power"`
	TechPower         uint32 `json:"tech_power"`
}

PostMatchPlayerStat is one timed snapshot of a player's scoreboard state.

type PurchaseEvent

type PurchaseEvent struct {
	Tick       uint32  `json:"tick"`
	GameTime   float64 `json:"game_time"`
	PlayerSlot int32   `json:"player_slot"`
	UserID     int32   `json:"user_id"`
	AbilityID  uint32  `json:"ability_id"`
	Change     string  `json:"change"`
	Sell       bool    `json:"sell"`
	Quickbuy   bool    `json:"quickbuy"`
	Source     string  `json:"source"`
}

PurchaseEvent is an item/ability ownership transition observed in the user message stream.

type StaminaConsumedEvent

type StaminaConsumedEvent struct {
	Tick               uint32  `json:"tick"`
	GameTime           float64 `json:"game_time"`
	EntindexTarget     int32   `json:"entindex_target"`
	StaminaBefore      float32 `json:"stamina_before"`
	StaminaAfter       float32 `json:"stamina_after"`
	Drained            bool    `json:"drained"`
	StaminaMax         float32 `json:"stamina_max"`
	MessageGameTime    float32 `json:"message_game_time"`
	HasEntindexTarget  bool    `json:"has_entindex_target"`
	HasStaminaBefore   bool    `json:"has_stamina_before"`
	HasStaminaAfter    bool    `json:"has_stamina_after"`
	HasDrained         bool    `json:"has_drained"`
	HasStaminaMax      bool    `json:"has_stamina_max"`
	HasMessageGameTime bool    `json:"has_message_game_time"`
}

StaminaConsumedEvent is one stamina spend observed in the user message stream. Before/After/Max are the player's stamina bars at the spend.

type WorldEntitySampleError

type WorldEntitySampleError struct {
	// EntityID identifies the replay entity with malformed evidence.
	EntityID int32
	// EntitySerial distinguishes generations that reuse EntityID.
	EntitySerial int32
	// Field names the non-finite sample field.
	Field string
}

WorldEntitySampleError reports malformed direct entity evidence.

func (*WorldEntitySampleError) Error

func (e *WorldEntitySampleError) Error() string

Error identifies the malformed field; entity identity remains in the record.

type WorldSnapshotError

type WorldSnapshotError struct {
	// RequestedTick is the snapshot tick requested by the caller.
	RequestedTick uint32
	// FinalTick is the last tick observed before the replay ended.
	FinalTick uint32
}

WorldSnapshotError reports that a requested snapshot tick was not observed.

func (*WorldSnapshotError) Error

func (e *WorldSnapshotError) Error() string

Error describes the missing snapshot without changing its structured evidence.

Directories

Path Synopsis
Package analysis derives replay-local timelines from s2replay events.
Package analysis derives replay-local timelines from s2replay events.
cmd
runback-facts command
Command runback-facts extracts the replay state at one exact tick, with parser provenance, and writes it as JSON on stdout.
Command runback-facts extracts the replay state at one exact tick, with parser provenance, and writes it as JSON on stdout.
s2replay command
Command s2replay parses Source 2 / Deadlock replays.
Command s2replay parses Source 2 / Deadlock replays.
generator
dispatchgen command
scripts
calibration command
Command calibration extracts a pro-calibration movement dataset from s2replay analyze JSON dumps.
Command calibration extracts a pro-calibration movement dataset from s2replay analyze JSON dumps.
parserdigest command
Command parserdigest computes the canonical parser source digest.
Command parserdigest computes the canonical parser source digest.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL