Repository navigation
v5 backport: all fixes from master without behavioural changes, API breakages or Go version changes - #687
Draft
markus-wa wants to merge 138 commits into
Draft
v5 backport: all fixes from master without behavioural changes, API breakages or Go version changes#687markus-wa wants to merge 138 commits into
markus-wa wants to merge 138 commits into
Conversation
…, model determination Agent-Logs-Url: https://github.com/markus-wa/demoinfocs-golang/sessions/6597a581-e8a9-42e5-b06a-158a6e2452a4 Co-authored-by: markus-wa <5138316+markus-wa@users.noreply.github.com>
… exactly Agent-Logs-Url: https://github.com/markus-wa/demoinfocs-golang/sessions/280cce4b-f470-4e54-bba8-cb12f90a8551 Co-authored-by: markus-wa <5138316+markus-wa@users.noreply.github.com>
Agent-Logs-Url: https://github.com/markus-wa/demoinfocs-golang/sessions/1b70b18f-66ff-404d-bb15-274d4aa6eed5 Co-authored-by: markus-wa <5138316+markus-wa@users.noreply.github.com>
…just 'base' Agent-Logs-Url: https://github.com/markus-wa/demoinfocs-golang/sessions/1b70b18f-66ff-404d-bb15-274d4aa6eed5 Co-authored-by: markus-wa <5138316+markus-wa@users.noreply.github.com>
broadcasts: fix player user-id mappings when starting parsing mid-match
fix: always update player user ID in getOrCreatePlayer
feat: update proto messages
Net-messages are handled on a queued goroutine that can lag thousands of messages behind the frame loop. When a message handler panics, the dispatcher's PanicHandler records the error immediately and the frame loop stops on its next iteration - but the already-queued backlog is still fully processed, both by the consumer and by shutdown's SyncAllQueues(). For a panic inside CSVCMsg_PacketEntities handling this is destructive: the message is half-applied, the entity table no longer matches the stream, and every backlog message decodes against wrong classes. In production we watched ~3,100 queued messages get processed after the first recorded panic, generating ~3,000 follow-on panics and multi-GB allocations from desynced field paths (~121 GB live heap, OOM kill). Skip handler work once a fatal error has been recorded: aborted() checks the recorded error, and every handler that decodes data or invokes user callbacks returns early. The backlog then drains as no-ops and ParseToEnd returns the recorded first error promptly. Cancel() gets the same fast-stop behaviour since it uses the same error mechanism. Validated on the production demo that motivated this: with a panicking user handler, the parse now returns the recorded error in ~2.1s at ~139MB peak instead of being OOM-killed at ~121GB; healthy demos parse with byte-identical results.
fieldState.set trusts decoded field-path components completely; get() bounds-checks but set() does not. On a desynced bitstream a garbage component such as 2142459506 (observed in production on a CS2 demo after a handler panic corrupted entity state) makes set() allocate (z+1) * 16 bytes in a single call - about 32 GB. Live heap reached ~121 GB before the OOM killer intervened; because the memory is reachable, GOMEMLIMIT cannot defend against it. The variable-size collection path in Entity.readFields has the same exposure through make([]any, newLen, newLen*2). Bound both with a shared maxFieldIndex (1<<20). Real components top out in the double digits and the largest networked arrays hold a few thousand entries, so the bound only fires on corrupt or desynced input, converting an OOM kill into a recoverable parse error. Validated against the production demo that motivated this: parse now fails in ~2.3s at ~159MB peak instead of OOM at ~121GB, and healthy demos parse with byte-identical results.
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.11.1 to 1.12.1. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](stretchr/testify@v1.11.1...v1.12.1) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-version: 1.12.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Fixes an entity-stream desync (panic: "unable to find existing entity") on demos containing plugin-spawned entities such as CBeam. The quantized-float decoder truncated in quantize(), but the engine's encoder rounds to nearest. For fields like CBeam m_fWidth (bits=10, low=0, high=102.3, round-up flag), quantize(high) yields 1022.99994: truncation reconstructs a value != high, so the round-up flag was wrongly kept and the decoder consumed a phantom 1-bit special flag where the stream held a plain 10-bit value, desyncing the rest of the message.
fix: round to nearest when quantizing floats
Regression noticed while backporting 74a7360 into the v4 branch. See the CS demo analyzer tests https://github.com/markus-wa/demoinfocs-golang/actions/runs/33257364117/job/99116464310 failure. In PlayerHurt events, event.Player.Health() == event.Health, which should not. event.Health is the new health and event.Player.Health() should be the HP before the event. It happens because entity props are updated after game events and the event was delayed. It's unnecessary, the bomb_exploded game event always precedes the same-frame player_hurt events caused by the explosion.
Add a permanent regression test using a CS2 deathmatch demo, where the polymorphic m_pGameRules.m_pGameModeRules pointer resolves to CCSGameModeRules_Deathmatch - a type with its own sub-fields (unlike the Noop type used by every other demo in the s2 set). The demo is added to the cs-demos submodule as a new small deathmatch.7z LFS archive and extracted into the s2 set, so it also joins the TestDemoSetS2 no-unexpected-warnings sweep. The new test asserts: - the active deathmatch type's sub-fields resolve by name and enumerate via Properties() (regression for the getFieldPaths nested-table fix) - values: m_flDMBonusStartTime, m_flDMBonusTimeLength, m_sDMBonusWeapon - a competitive-only field (m_nHalvesPlayed) does not resolve under the deathmatch type CI: coverage.sh and race-tests.sh now pull deathmatch.7z, and the race run covers TestPolymorphicGameModeRules alongside TestDemoInfoCs.
…nd creation Add an append-only handlerLog to Entity as the single source of truth for update-handler dispatch ordering. handlersByFP rebuilds (on polymorphic type change) and the entity-creation initial dispatch now iterate the log in registration order instead of name-sorted map keys, so handlers sharing an fp key fire in their original registration order, stable across rebuilds. Resolves the pre-existing TODO in OnPacketEntities (introduced by 6a6eb08 'fix random ordered create handlers') which noted the creation dispatch should be ordered by handler registration order. Regenerates test/default.golden: the only change is IsWarmupPeriodChanged now firing in registration order relative to GamePhaseChanged.
…tries - maxPolyID: only cache fully computed results. A back-edge into an in-progress traversal (a serializer cycle, which flattened-serializer data cannot produce) no longer persists an understated value in the memo map, so a later top-level call on such a node recomputes it and a class backed by it gets a correctly sized polySerializers slice. - readFields: panic on a sub-field update under a polymorphic pointer with no active serializer instead of silently skipping the value bits (which would desync the rest of the packet). - PropertyEntries/collectFieldsEntries: enumerate sub-fields of every polymorphic alternative, not just the default serializer. - Tests: maxPolyID cycle no-stale-cache regression, poly PropertyEntries.
…ymorphic-fields Fix polymorphic field support in CS2 sendtables parser
…sorted participants
Various fixes: correctness, new events/getters, and doc corrections
require go 1.27
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.35.0 to 0.41.0. - [Commits](golang/image@v0.35.0...v0.41.0) --- updated-dependencies: - dependency-name: golang.org/x/image dependency-version: 0.41.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com>
…rg/x/image-0.41.0 chore(deps): bump golang.org/x/image from 0.35.0 to 0.41.0
…te churn noscaleDecoder re-boxed the cached float32 into a fresh any on every decode, defeating reader.f32Cache and costing ~786k extra heap allocations per demo parse (+37% allocs, +8.5% wall time vs v5.2.0, confirmed via pprof alloc_objects). Return the pre-boxed cache value directly again, as in v5.2.0. Polymorphic pointer base decoders allocated a polyUpdate per decode even when the active type did not change; return a pointer to a scratch slot on the reader instead. readFields consumes the value synchronously before the next decode on the same reader, so reuse is safe. Benchmarks (s2.dem, benchstat): allocs/op 5.792M -> 5.630M (-2.8% vs v5.2.0) B/op 350.0Mi -> 324.8Mi (-7.2%) CPU parity (pprof -diff_base: -0.03s net)
IsWalking() is backed by m_bIsWalking, which indicates that the walk (sneak) key is held - it is true even while standing still. Document this and point users to Velocity() (or position deltas between ticks, since Velocity() returns a zero vector on GOTV demos) for actual movement detection. Refs #318
docs(common): clarify IsWalking() reflects walk key, not movement
Second optimization pass; allocs another -9% and bytes another -25% below the previous commit, both now well below v5.2.0 levels. - f32Cache 512 -> 4096 entries: fewer conflict misses; working-set misses dominate, so larger sizes don't pay off - quantized float32 decodes route through reader.f32Cache, reusing pre-boxed values (~145k allocs/parse less; stationary players repeat positions across ticks) - QAngle decoders return pre-boxed [3]float32 from a new per-reader direct-mapped v3Cache keyed by the three bit patterns - frame and embedded-message payload buffers are recycled via sync.Pool; proto.Unmarshal always copies bytes fields, so the input buffers are dead after unmarshaling. CSTV broadcasts keep the unpooled path since they retain buffers in messages (m.Data = buf) - compressed frames decode snappy into a per-parser scratch buffer instead of a fresh allocation per frame Benchmarks (s2.dem, benchstat vs v5.2.0): allocs/op 5.792M -> 5.107M (-11.8%) B/op 350.0Mi -> 243.9Mi (-30.2%) CPU differential pprof: ~3% below v5.2.0
perf: fix alloc regressions, pool frame buffers, end below v5.2.0 in allocs & memory
…, exactly" This reverts commit 9285813.
…ID order" This reverts commit 65a13e4.
This reverts commit b6ac932.
…FO order" This reverts commit 4126b7b.
This reverts commit e1e45f5.
Brings in encrypted net-message support (#696, #697) and the v5 lint base fix (#698). Conflict resolution: - protos & generated .pb.go: take v5's (identical to master's), keep usercmd.proto in generate.sh - s2_commands.go: master's resolution from a2dd17a (msgCreatorForType + usercmd handling) - events.go: WarnType order matches master so values are identical on v5 and v6 - ci.yml: keep v5's base_rev for lint-changes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
markus-wa
marked this pull request as draft
October 11, 2026 11:18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Backports everything merged to master since v5.2.0 to the
v5maintenance line — except behavioral changes, API breakages and Go version changes.Branched from master @
b793dd5(the last/v5-module commit, before the v6 module rename).What was reverted (relative to master)
b6ac932+65a13e4InfernoFireOutevent (new event dispatched = behavioral change)4126b7bPlayerFlashedvictims paired by detonation instead of FIFOe1e45f5+9285813HealthDamageTakencapped at remaining HPPlayerHurtdispatched before entity updates (Player.Health()reporting pre-event HP)These are exactly the behavioral changes called out in the v6.0.0-alpha.0 release notes Breaking Changes section, plus the Go version bump.
What's included (kept from master)
KnifeType,Equipment.OriginalString)UserCmdParsingmodes (fixesPlayerButtonsStateUpdatesince the last CS2 update)Player.Velocity()getOrCreatePlayer)OtherDeathforentity_killed,VoteCast,BombDefuseStart.Site,PlayerJump.Position,Parser.FrameCount(),attacker_pawnfallback, deterministic UniqueIDs/accessors, QAngle noscale decode fix (required by perf: fix alloc regressions, pool frame buffers, end below v5.2.0 in allocs & memory #685),GrenadeProjectile.Velocity()panic fix, grenade-type fallback +ParserWarn, doc correctionsVerification
go build ./...,go vet ./..., fullgo test ./pkg/...greentest/default.goldenregenerated: exactly 41 lines removed, 0 added — allInfernoFireOut(Site/Position/event-value fixes kept, so no other golden drift)scripts/check-interfaces-generated.shcleanmodule github.com/markus-wa/demoinfocs-golang/v5,go 1.24.0, x/imagev0.35.0Notes
v5pastb1bc851(v5.2.0) — no force needed.PlayerHurtbehavior from v5.2.0 (bomb-explosion hurts observe post-update entity state), per the no-behavioral-changes rule for this line. The fix remains available on master/v6.