Go Get
go get -u github.com/markus-wa/demoinfocs-golang/v6/pkg/demoinfocs@v6.0.0-rc.0
To upgrade from v5, this may be useful to update import paths (use at your own risk):
sed -i 's/demoinfocs-golang\/v5/demoinfocs-golang\/v6/' **/*.go
This is the first release candidate of v6. These notes are self-contained: they list every change since v5.2.0, so you don't need to read the alpha release notes to migrate from v5. If you are on an alpha, the per-alpha notes are linked at the end.
The v5 line continues to be maintained on the v5 branch for users not ready to upgrade. Fixes marked "also on the v5 branch" below are there too (unreleased as of this rc).
Upgrading from v5
Most of the API changes are mechanical. This table maps v5.2.0 code to v6; the sections below have the details and the PR links.
| v5.2.0 | v6.0.0-rc.0 |
|---|---|
github.com/markus-wa/demoinfocs-golang/v5/...
| github.com/markus-wa/demoinfocs-golang/v6/...
|
| Go 1.24 | Go 1.27 |
NewParserWithConfig(r, cfg), ParseWithConfig(r, cfg, cb)
| NewParser(r, option.X(...), ...) — one option function per former ParserConfig field: option.MsgQueueBufferSize(n), option.AdditionalNetMessageCreators(m), option.IgnoreErrBombsiteIndexNotFound(true), option.DisableMimicSource1Events(true), option.Source2FallbackGameEventListBin(b), option.IgnorePacketEntitiesPanic(true)
|
DefaultParserConfig, ParserConfig.Format
| gone — no options = the same defaults as before, the format is detected |
Parse(r, cb)
| p := NewParser(r) + p.ParseToEnd(ctx)
|
ParseFile(path, cb), ParseFileWithConfig(path, cfg, cb)
| ParseFile(ctx, path, cb, option.X(...))
|
NewCSTVBroadcastParser(url), ParseCSTVBroadcast(url, cb), cfg.CSTVTimeout = d
| r, err := cstv.NewReader(ctx, url, cstv.WithTimeout(d), cstv.WithHTTPClient(c)) + NewParser(r)
|
p.ParseToEnd()
| p.ParseToEnd(ctx)
|
p.Cancel(), err == ErrCancelled
| cancel the ctx, errors.Is(err, context.Canceled)
|
panic(...) inside a handler to stop parsing
| cancel the ctx (context.WithCancelCause) — a panic in a handler is now re-raised on the caller as *PanicError
|
demoinfocs.Parser, GameState, GameRules, Participants (interfaces)
| *demoinfocs.Parser, *GameState, *GameRules, Participants (value)
|
sendtables.Entity, Property, ServerClass (interfaces), sendtablescs2.X
| *sendtables.Entity, *sendtables.Property, *sendtables.ServerClass, sendtables.X
|
fake.NewParser(), sendtables/fake
| your own small interface + fake, see examples/mocking
|
Equipment.UniqueID2() (ULID), GrenadeProjectile.UniqueID() / Inferno.UniqueID() (int64)
| UniqueID() returning common.UniqueID (uint64) on all three; .Uint64() for the number. IDs stored from v5 won't match
|
entity.Property("m_vecX") and other bare names that exist in several send-nodes of a class
| the send-node-prefixed name: m_vecVelocity.m_vecX, m_vecViewOffset.m_vecX, … (unique names like m_iHealth are unaffected)
|
Equipment.AmmoInMagazine() + 1 workarounds
| remove them, the off-by-one is fixed |
PlayerHurt.HealthDamageTaken to reproduce HLTV ADR
| PlayerHurt.HealthDamage (HealthDamageTaken is now the HP actually removed, see below)
|
BombDropped.EntityID (was the player's entity ID)
| the C4's entity ID; the player's is BombDropped.Player.EntityID
|
protoregistry / anypb lookups of CSVCMsg_ServerInfo etc., msg.File_<x>_proto
| com.github.markus_wa.demoinfocs_golang.v6.CSVCMsg_ServerInfo, msg.File_v6_<x>_proto (Go types unchanged)
|
go run ./examples/print-events
| cd examples && go run ./print-events (own module)
|
events.BomsiteUnknown
| events.BombsiteUnknown (the misspelled alias still works)
|
Observable output also changes without any code change on your side: PlayerHurt.Health() & HealthDamageTaken, PlayerFlashed pairing, BombExplode on server shutdown, GrenadeEvent.Grenade attribution, InfernoStart timing, duplicate POV grenade events, and the order of Player.Weapons() and round-end projectile destruction are all different. The "Breaking Changes" section says how.
Breaking Changes
- Upgraded version from v5 to v6, changing import paths from
github.com/markus-wa/demoinfocs-golang/v5/...togithub.com/markus-wa/demoinfocs-golang/v6/...in #686 - Now requires Go 1.27 (up from 1.24) by @markus-wa in #681
- Single
NewParserconstructor with functional options &context.Context,Cancel()removed by @markus-wa in #724NewParser(r io.Reader, opts ...option.Option)is the only constructor.ParserConfig,DefaultParserConfigand all*WithConfigvariants are replaced by the newoptionpackage (option.NetMessageDecryptionKey(key),option.UserCmdParsing(option.UserCmdParsingFull), …), with the same defaults as beforeParseToEnd(ctx)&ParseFile(ctx, path, cb, opts...)take a context: cancel it instead of callingCancel(), checkerrors.Is(err, context.Canceled)instead ofErrCancelled. A cancellation that happens while the message queue drains is now returned too, instead of silently giving incomplete data- CSTV broadcasts are just an
io.Reader:r, err := cstv.NewReader(ctx, url, cstv.WithTimeout(d), cstv.WithHTTPClient(c)), thenNewParser(r).NewCSTVBroadcastParser/ParseCSTVBroadcastare gone. A non-200 response to the initial requests now returnscstv.ErrUnexpectedStatusinstead of a confusing JSON error or an error page in the demo stream
- Concrete types instead of interfaces ("accept interfaces, return structs") by @markus-wa in #725
*demoinfocs.Parser,*GameState,Participants(value) and a live*GameRules(a value fetched before theCCSGameRulesProxyentity exists no longer keeps a nilEntity()forever)sendtablescs2is merged intosendtables;Entity,Property,ServerClassandServerClassesare concrete types (*sendtables.Entityetc.,Property()returns nil if not found)- The ifacemaker-generated interfaces and both
fakepackages are removed (no more testify in your build);examples/mockingshows how to define your own small interface + fake Entity.Destroy,Entity.Get,Exists, theGet*getters andMapare unexported or removed;Entity.String(),ServerClasses.String()andEntity.Replaced()are newly exported
- Errors instead of panics by @markus-wa in #729, #730 and #732
- The library's own failures are returned from
ParseToEnd/ParseNextFrame:ErrUnexpectedEndOfDemofor truncated demos,*DecodeError/ErrDecodeFailed(withFrame,Cause&Stack) for corrupt data, unsupported schemas and missing properties (incl.PropertyValueMustcalls inside your handlers), andreading demo: …for errors from yourio.Reader - A panic in one of your handlers is re-raised on the caller's goroutine as
*PanicError(original stack kept, recoverable) instead of being returned as an error string. To stop parsing from a handler, cancel the context (context.WithCancelCause) instead of panicking. Calling a parse method again afterwards returnsErrParserStopped - Hardened against corrupt input: frames are capped at 16 MiB (64 MiB for
DEM_AnimationData), field indices & collection sizes at 65536, plus per-parse budgets for entity state. Exceeding a limit returnsErrDecodeFailedinstead of allocating gigabytes or hanging forever. BrokenDEM_FullPacketframes are skipped with aParserWarn(WarnTypeBrokenFullPacket), as the game does (#409); a corrupt frame of any other type is nowErrDecodeFailedinstead of a warning + empty message. Fuzz targets and a corrupt-demo corpus are part of the test suite now option.IgnorePacketEntitiesPanicnow actually dispatchesParserWarn{Type: WarnTypePacketEntitiesPanic, Err}(it used to print to stderr) and no longer swallows user panics;events.ParserWarnhas a newErrfieldNewParserno longer reads from the stream, read errors surface fromParseToEnd/ParseNextFrame;Close()closes the stream even before parsing.PropertyValueMustpanics with*sendtables.PropertyNotFoundErrorinstead of a string
- The library's own failures are returned from
Player.Name&Player.SteamID64now come from the CS2 player controller (CCSPlayerController.m_iszPlayerName/m_steamID, i.e. the server-enforced scoreboard name) by @markus-wa in #710 — fixes names being reverted after a reconnect (#692, FACEIT names onbroken_1.dem), keepsplayer_changenamerenames and plugin prefixes like"COACH E"- The Steam / connect name stays available via
PlayerInfo&PlayerConnect - A slot taken over by a different person gets a new
*Player; the previous occupant getsPlayerDisconnected,IsConnected = false&Entity = nil. An existing player'sSteamID64is never changed.Playing()no longer returns the same*Playertwice when a bot name is re-used (#693) PlayerInfo&StringTablePlayerUpdateAppliedonly fire when a slot's info actually changes;PlayerNameChangecomes from controller updates and is not dispatched for bots,unknown→ name, or placeholder names (empty / SteamID64)Player.IsBotism_steamID == 0,Player.Teamis set on creation, raw userinfo is kept after disconnect- New
sendtables.Entity.OnUpdateFinished(func()): the parser applies controller changes there, so inside your ownCCSPlayerControllerprop handlers the boundPlayerisn't updated yet, read it from anOnUpdateFinisheddelegate instead - Also fixes string-table entry index decoding (indices are relative, not absolute): 18 of 72 demos had userinfo entries attributed to the wrong slot (up to ~25k per demo)
- Verified on 60 demos: name mismatches vs. the controller 1.67M → 0, duplicate
*Players inPlaying()0 → 0, kills identical
- The Steam / connect name stays available via
common.UniqueIDreplacesEquipment.UniqueID2()and theint64IDs ofGrenadeProjectile/Infernoby @markus-wa in #712 (with @AdamMagued, #711) — oneuint64counter type for all three, comparable with<and sorted in creation order.String()/ JSON is the decimal number instead of a 26-character ULID, so IDs stored from earlier versions won't match.oklog/ulidis no longer a dependency- Entities leaving & re-entering the PVS by @markus-wa in #740 and #746 (#728, #741, #742)
- New
Entity.InPVS(),Entity.OnLeavePVS(func())&Entity.OnEnterPVS(func()). A weapon that left the PVS stays inGameState().Weapons()with itsOwneruntil it is destroyed or comes back - A same-serial re-create (PVS re-entry) keeps the same
*Entity: entity handlers seeEntityOpUpdated|EntityOpEnteredinstead ofCreated|Entered,ServerClass.OnEntityCreateddoesn't fire again, and property handlers fire only for values that changed - POV demos no longer emit duplicate
GrenadeProjectileThrow, grenadeWeaponFire&InfernoStartevents for projectiles / infernos that re-enter the PVS (pov.dem: 904 → 315 throws, 253 → 86 inferno starts; GOTV output is byte-identical)
- New
- Ambiguous bare sendtable field names no longer resolve by @akiver in #674 and @markus-wa in #706 — e.g.
m_vecXon a player pawn silently returned the view offset, not the velocity. Use the send-node-prefixed name (m_vecVelocity.m_vecX,m_vecViewOffset.m_vecX,m_AttributeManager.m_Item.m_NetworkedDynamicAttributes.m_Attributes, …). Names that are unique within their class (m_iHealthetc.) are unaffected PlayerHurt.HealthDamageTakenis now the HP actually removed (health before the hit minus reported post-hit health) by @markus-wa in #700 (and #663 for fatal hits)- On many demos a hit removes
dmg_healthordmg_health + 1HP. This depends on the server build: it shows up on demos from 2023 to 2026, but not on all of them.HealthDamageTakennow matches the game's own damage stats (m_pActionTrackingServices.m_iDamage,round_statsinserver.log) in 7,223 of 7,230 player-rounds over a 41-demo corpus (the remaining 7 areplayer_hurtevents the GOTV recording dropped) - ADR computed from
HealthDamageTakenwill be ~1% higher than HLTV's on affected demos. UseHealthDamage(the rawdmg_health) to reproduce HLTV numbers
- On many demos a hit removes
PlayerHurt.Player.Health()reports the HP before the event (the event is dispatched before the entity update is applied) by @akiver in #675PlayerFlashedvictims are paired with their flashbang by detonation rather than in FIFO order by @markus-wa in #663BombExplodeis no longer dispatched when the server shuts down a planted bomb without detonating it (e.g. a few ticks after a round is decided at half-time, overtime start or map end) by @markus-wa in #701 — the event now keys offCPlantedC4.m_bHasExploded, matching the nativebomb_explodedcount exactly in all 41 corpus demos (previously 17 spurious explosions across 12 demos)BombDropped.EntityIDis now the C4's entity ID on both the synthesized and the game-event path (it was the dropping player's entity ID, or 0 with mimicking disabled); the player's ID is still atBombDropped.Player.EntityIDby @markus-wa in #737Equipment.AmmoInMagazine()returns one more than before by @markus-wa in #718 — them_iClip1+1 offset was subtracted twice on CS2 demos, so every magazine read one round low and an empty one underflowed to 4294967295. Drop any+1you added downstream. Also on thev5branch (#719)- Grenade events carry the actual projectile:
GrenadeEvent.Grenadeis the projectile'sWeaponInstanceinstead of a "last grenade of that type the player threw" guess, andInfernoStartis dispatched at the end of the frame the inferno was created in, see "New Features" (#708) - Deterministic ordering by @markus-wa in #739 and #663 —
Player.Weapons(),GameState.Hostages()andParticipants.SpottersOf()/SpottedBy()are sorted by entity ID, grenade / infernoUniqueIDs are deterministic, and leftover projectiles / infernos atround_officially_endedare destroyed in ascending entity-ID order (not throw order). 12 of 49 demos produced a different event order from run to run before - Examples are a separate Go module (
examples/go.mod) by @markus-wa in #717 — the heatmap / draw2d / vdf dependencies no longer end up in yourgo.sum;cd examplesbeforego run ./print-events -demo … - The v6 protos can coexist with v4's and v5's by @markus-wa in #748 — a program importing both v4 and v6 used to panic at init (
extension number 61000 is already registered on message google.protobuf.MessageOptions). This is what lets CS Demo Analyzer parse CS:GO demos with v4 and CS2 demos with v6 (#749)- Proto full names in package
msguse the packagecom.github.markus_wa.demoinfocs_golang.v6and files are registered asv6/*.proto, e.g.com.github.markus_wa.demoinfocs_golang.v6.CSVCMsg_ServerInfoinv6/netmessages.proto. This affectsprotoregistrylookups,anypbtype URLs and logged descriptor names - The generated file descriptor variables are renamed:
File_<x>_proto→File_v6_<x>_proto(17 of them; message types likemsg.CSVCMsg_ServerInfokeep their names) - Valve's custom option extensions (
E_CodegenDeltaEncoder,E_MaximumSizeBytes, … 20 in total) are removed. demoinfocs never read them, they were only descriptor metadata - Go types, fields, the wire format and protojson field names are unchanged, so code that only uses the message types isn't affected
- Proto full names in package
events.UserCmd.Commandmust not be modified by @markus-wa in #756 — the dispatched command is the decoder's baseline for the player's next delta (it is no longer deep-copied, see "Performance"). Useproto.Clone()to get a copy that can be modified. (events.UserCmdis new in v6, so this only affects alpha users)
New Features
- Full user-command decoding for CS2 by @WangChuDi in #669
- new
events.UserCmdwith the fully reconstructed command (buttons, mouse deltas, view angles, movement, subtick moves, input history, …), incl. delta-encoded commands - new
option.UserCmdParsingmodes:option.UserCmdParsingFull,option.UserCmdParsingButtonsOnly(default, low overhead) &option.UserCmdParsingDisabled - Decode the user commands of players whose first live commands are deltas by @l0rem in #753 (#751) — every
DEM_FullPacketcarries a full command per player, which now seeds the decoder of a player that has no command yet, instead of rejecting every delta withWarnTypeUserCommandBaselineMissinguntil the live stream sends a full command (in one demo, two players had no decoded commands for ~2.4 min)- on 41 CS2 demos: 58.2M → 69.5M
UserCmdevents (+19%),WarnTypeUserCommandBaselineMissing11.27M → 123. 15 demos were affected (recent HLTV demos), in the worst one only 26% of the commands were decoded before - applies to
UserCmdParsingButtonsOnly(the default) too, so you'll see morePlayerButtonsStateUpdateevents on affected demos. Seeds are never dispatched themselves and never replace a live baseline; every command dispatched before is still dispatched unchanged
- on 41 CS2 demos: 58.2M → 69.5M
- new
- Decrypt encrypted net-messages in CS2 demos (chat messages in Valve matchmaking demos are encrypted since the Sept 2026 CS2 update) by @blinkzz in #696 and @akiver in #697, ported to v6 in #699. Also on the
v5branch- new
option.NetMessageDecryptionKey(key)— set it to the key from thematch730_*.dem.infofile, whichMatchInfoDecryptionKey()extracts for you - new
ParserWarntypesWarnTypeMissingNetMessageDecryptionKey(encrypted message seen, no key configured) &WarnTypeCantReadEncryptedNetMessage CSVCMsg_EncryptedDatais also exposed as a regular net-message forRegisterNetMessageHandler- Empty, truncated or corrupt payloads (e.g. a wrong key) give a
WarnTypeCantReadEncryptedNetMessagewarning instead of aborting the parse by @markus-wa in #705. The path is covered by unit tests with ICE-encrypted fixtures; there is still no real encrypted demo in the test suite, so reports are welcome
- new
- Track individual grenades:
GrenadeEvent.Projectileby @markus-wa in #708 (#361, #601)HeExplode,FlashExplode,SmokeStart/SmokeExpired,DecoyStart/DecoyExpired,FireGrenadeStart/FireGrenadeExpiredandInfernoStart/InfernoExpiredcarry the*GrenadeProjectile, resolved from the event's entity ID, soProjectile.UniqueID()identifies one throwGrenadeis the projectile'sWeaponInstanceinstead of a "last grenade of that type the player threw" guess (was wrong for 578 of 8,313 flashes and 512 of 4,107 smoke expiries in a 50-demo corpus),Throweris filled from the projectile for CS2's thrower-lessinferno_*events- Infernos are linked to their molotov / incendiary projectile (all inferno events in the corpus, GOTV and POV).
InfernoStartis now dispatched at the end of the frame the inferno was created in
Player.Velocity()by @akiver in #674, and it works on demos recorded before AnimGraph 2 (protocol < 14089, wherem_vecVelocityisn't networked on GOTV) by @markus-wa in #709 — derived from position deltas in snapshot server-ticks (v4 had a similar fallback, but under-reported by ~3× on demos with 3 ticks per snapshot and spiked on round restarts)- Tracking starts lazily on the first
Velocity()call per player (oneWarnTypeVelocityTrackingStartedLazilyper parse);option.EagerVelocityTracking(true)tracks everyone from creation. No cost on demos where the velocity is networked - The POV recording player's networked value is used as-is; during an entity snapshot the velocity between the last two snapshots is returned so moving shooters don't read 0 at
WeaponFire
- Tracking starts lazily on the first
- Knife detail:
common.KnifeTypewithMapKnifeType(), user-extensibleKnifeTypes/KnifeTypeIndexMapping/KnifeTypeNamesmaps, andEquipment.OriginalStringcarrying the raw weapon string from game events by @markus-wa in #683 - New events & getters by @markus-wa in #663:
events.InfernoFireOut— dispatched when an inferno's flames actually burn out (~5–7s), as opposed toInfernoExpiredon entity destroy (~20s)events.VoteCastwith library-resolved playerBombDefuseStart.SiteandPlayerJump.PositionParser.FrameCount()— returns-1until the total frame count is known
- New
sendtables.EntityAPI:OnUpdateFinished(func()),InPVS(),OnLeavePVS(func()),OnEnterPVS(func()),Replaced()(destroyed because its index was reused without a delete, e.g. on round restarts / backup restores) andString(), see "Breaking Changes" for the semantics events.BombsiteUnknown(the misspelledBomsiteUnknownstays as a deprecated alias) in #717- Updated proto messages by @akiver in #660 and in #699 (
svc_EncryptedData,CSVCMsg_VoiceData.caster,textallchat,CS_UM_*_CSGOLegacyrenames,CCSUsrMsg_CustomHudClicked)
Fixes
- OOM FIX: fail fast on corrupt bitstreams instead of OOM-killing — production demos that previously OOM'd at ~121 GB live heap (desynced entity state after a handler panic) now abort in ~2s at ~150 MB with a recoverable error:
- stop processing queued messages once a fatal error is recorded by @StefanDorresteijn in #664
- stop all handler work (incl. user-registered handlers) once a fatal error is recorded by @markus-wa in #678
- bound field-state allocations by @StefanDorresteijn in #665
- bound
readBytesallocation before decoding corrupt lengths by @markus-wa in #679
- Proper support for polymorphic fields in the CS2 sendtables parser by @Copilot in #654 — sub-fields of polymorphic pointers (e.g.
m_pGameRules.m_pGameModeRules.*deathmatch fields) now resolve per active type, with deterministic handler rebuilds and nested-table enumeration inProperties() - Fix entity-stream desync (panic: "unable to find existing entity") on demos containing plugin-spawned entities such as CBeam — the quantized-float decoder now rounds to nearest, matching the engine's encoder by @akiver in #672
- Fix entity index reuse without a delete (#601) by @markus-wa in #712 — the server sometimes creates a new entity (new serial) at the index of one it never deleted, e.g. on round restarts or backup restores. The old entity was overwritten without
OnDestroy, sobindWeaponreused the stale*Equipment(and its ID) for the new weapon: 2,239 weapon entities sharing an*Equipmentacross 23 of 49 demos → 0, 158 spuriousItemRefunds across 14 demos gone, and a USP-S no longer shows up as a P2000. Stale projectiles / infernos that lingered inGameState()for 10k+ ticks are destroyed too - Fix grenade attribution by @markus-wa in #712 and #738
- the thrown-grenade bookkeeping removes the grenade that actually went off instead of the last one of its type (6
PlayerHurt.Weapons corrected over 74 demos, tracked-but-never-detonated grenades at demo end 183 → 74) GrenadeProjectile.WeaponInstanceis taken from the weapon entity's own throw state (m_bPinPulled/m_fThrowTime), which fixes the wrong instance when a player throws a grenade on the same tick they pick up a floor grenade of the same type
- the thrown-grenade bookkeeping removes the grenade that actually went off instead of the last one of its type (6
- Fix nil
Entity/OwneronWeaponInstanceof thrown grenades (GrenadeProjectileThrow, mimickedWeaponFire; #580) by @markus-wa in #688 — CS2 destroys the weapon entity before the projectile is created, so the last-known weapon of that type is used as a fallback (nilEntitywent from 9–42% of throws per demo to 0) - Bomb event fixes by @markus-wa in #737
Bomb().Carrier&BombPickupfor a C4 that is created before its owner's pawn (demos starting in freeze time):Bomb().Position()returned (0,0,0) for up to 6s and a drop by the first holder dispatched noBombDropped- no duplicate
BombPickupon repeated owner updates (36 across 49 demos), noBombDroppedfor a pass to an owner that hasn't resolved yet - with
option.DisableMimicSource1Events(true),BombDropped.EntityID/PlayerandBombPickup.Playerare read from CS2'sentindex/userid_pawnkeys (they were always 0 / the GOTV bot)
- Fix
GameRules.RoundTime()always returningErrFailedToRetrieveGameRuleon CS2 demos (it still looked up the CS:GO property name) by @markus-wa in #726. Also on thev5branch (#727) - Fix user command warnings never reaching event handlers by @l0rem in #752 (#751) —
WarnTypeUserCommandDeltaDecodeFailed,WarnTypeUserCommandBaselineMissingandWarnTypeUserCommandBaselineMismatchwent to the net-message dispatcher, soRegisterEventHandler(func(events.ParserWarn))never saw them - Fix
PlayerButtonsStateUpdatenot working since the last CS2 update & resolve usercmd players via controller entity by @WangChuDi in #669 - Fix float decoding differences between amd64 and arm64 (FMA contraction) by @akiver in #676
- Fix
Equipment.Silenced()panicking with a nil pointer dereference on equipment whose class has nom_bSilencerOn(defuse kit, kevlar, helmet, NVG) by @youdie006 in #695 - Always update player user ID in
getOrCreatePlayerby @akiver in #659 - Broadcasts: fix player user-id mappings when starting parsing mid-match by @markus-wa in #655
- Further correctness fixes by @markus-wa in #663:
- Dispatch
OtherDeathfor CS2entity_killed - Resolve
PlayerHurt.Attackerviaattacker_pawnfallback - Decode 32-bit QAngle components as noscale floats (fixes e.g.
m_aimPunchAngle) - Stop
GrenadeProjectile.Velocity()panicking on CS2 demos - Grenade type fallback by server class +
ParserWarninstead of stderr for unknown models
- Dispatch
- Docs: typos by @antonkesy in #667,
IsWalking()semantics in #684, game events /AmmoReserve()/FlashDurationcorrections in #663,HealthDamageTaken's +1 depends on the server build in #707, stale API docs (ParseHeader(),NewCustomParser(), CS:GO references,ErrInvalidFileTypemessage) in #717, new README "Error Handling" section in #730
Performance
-
v5.2.0 → v6.0.0-rc.0: ~42% less memory and ~13% fewer allocations on
BenchmarkDemoInfoCs(s2.dem, benchstat, 10 interleaved rounds):v5.2.0 v6.0.0-rc.0 memory (B/op) 350 MiB ± 1% 204 MiB ± 0% −42% allocs/op 5.79M ± 0% 5.06M ± 0% −13% Parse time is roughly the same as v5.2.0. Measurements so far are too noisy to say whether it is a few percent faster or slower; this is tracked in #757. Please report regressions you see on your own demos
- fix alloc regressions, pool frame buffers & widen value caches by @markus-wa in #685
- reduce parse hotspots (snappy destination reuse, field-path huffman peek table, less decoder boxing) by @markus-wa in #691
Participants().All()goes from 3 to 1 allocs/op in #739; frame buffers above 4 MiB are reused on POV demos (pov.dempeak RSS 211–220 → 171–178 MB) in #745- User
OnUpdateproperty handlers cost ~9 ns more per call for the panic guard (BenchmarkUserPropertyHandlers+3.6%, worst case with a handler on every pawn property)
-
UserCmdParsingFullis ~15–25% faster by @markus-wa in #756 — decoded commands are stored as the next delta's baseline without a deep copy (6381816.dem: 18.8–19.9 → 13.5–15.7 s,fokus-vs-aurora-m1-cache.dem: 9.15–9.35 → 7.97–8.08 s; dispatchedUserCmdevents identical, see the breaking-change note onUserCmd.Command)
Changes
- Dependencies:
pkg/errors&x/expreplaced with the standard library in #716 (%w,errors.Is,cmp.Ordered);oklog/uliddropped in #712;testify/objxdropped in #725; the example-only deps (draw2d,go-heatmap,freetype,x/image,vdf) moved to the examples module in #717; newice-cipher-gov1.0.0 for net-message decryption (#699, #702);godispatchv1.5.1 (fixes handler order & dropped handlers when registering after the firstDispatch),gobitreadv0.2.5, protobuf 1.36.12 (#666), testify 1.12.1 (#670), x/image & x/text (#658) - Repo: the default branch was renamed
master→mainin #721 (git branch -m master main && git fetch origin && git branch -u origin/main main && git remote set-head origin -a), the GitLab mirror is gone - CI: actions updated by @katsugtgz in #677; least-privilege workflow permissions in #690; OSV-Scanner dependency scanning in #689;
test-csdadisabled by @akiver in #694 and re-enabled against the v6 fork of CS Demo Analyzer in #703, #750 (CS:GO demos as a smoke test of CSDA's combined v4 + v6 build), #754 and #755; push / PR runs deduped in #720; coverage viacodecov-actionin #723; jobs parallelised & sharded in #744 and #747; a flaky trailing-newline reader test fixed in #682 - Upgraded linter config by @akiver in #680
- Protos:
prepare-protos.pyapplies the package, import prefixes and option stripping beforegenerate.sh, and the nightly "Update Protobufs" workflow uses it (#748)
New Contributors
- @antonkesy made their first contribution in #667
- @katsugtgz made their first contribution in #677
- @youdie006 made their first contribution in #695
- @blinkzz made their first contribution in #696
- @l0rem made their first contribution in #752
Full Changelog: v5.2.0...v6.0.0-rc.0
Coming from an alpha? The incremental notes: alpha.0 · alpha.1 · alpha.2 (has an alpha.1 → alpha.2 migration table) · alpha.3. Since alpha.3: #753, #754, #755, #756.