NetworkUpdateFlags¶
The Thing.NetworkUpdateFlags 16-bit bitmask, how vanilla serialization uses the low 12 bits, and what the apparently free band above them is actually worth to a mod. The short answer, recorded at 0.2.6428.27798 after two mods tried it: the free band is free only at the Thing level, the payload it announces carries no framing at all, and a mod is better served by its own message type. The rest of the page is the evidence for that.
Bitmask semantics¶
Thing.NetworkUpdateFlags is a bitmask. Setting a bit causes the game's next network tick to include that object in the update broadcast.
Spray Paint Plus used bit 12 (0x1000, GenericFlag2) for spray can colour updates, piggybacking on the existing Consumable.BuildUpdate / ProcessUpdate serialization, up to and including the published v1.12.1. It no longer does. See "Spray Paint Plus stopped riding these bits" below for what replaced it and why; the bit itself is described under the bit map.
Vanilla bit usage¶
16-bit bitmask. Values through 0x0800 are used by Thing/DynamicThing/Item for standard state (position, rotation, damage, color, access, etc.). EquipmentPlus uses 0x4000 for active-sensor sync. BuildUpdate and ProcessUpdate are called by the network layer; each flag bit causes the corresponding data block to be written/read.
Thing-level bit map and the transform bit (bit 1)¶
The Thing base class owns the low bits; subclasses (Structure, Cable/logic units, Item, DynamicThing, devices, etc.) add their own bits above the Thing range, each handled in that subclass's BuildUpdate/ProcessUpdate override (which calls base.BuildUpdate/base.ProcessUpdate first). From Assembly-CSharp (0.2.6228.27061): Thing.BuildUpdate (:303247), Thing.ProcessUpdate (:303329), Thing.SerializeDeltaState (:303187), and the various property setters:
| Bit (decimal) | Owner | Meaning / serialized block | Set by |
|---|---|---|---|
1 |
Thing |
transform delta: BuildUpdateTransform (:303381) writes WriteVector3(Position); WriteQuaternion(Rotation) (the Position/Rotation mirror fields, kept synced by the ThingTransformPosition/ThingTransformRotation setters at :298397/:298426); client ProcessUpdateTransform (:303387) reads them and does if (!HasAuthority) ThingTransform.SetPositionAndRotation(...) |
never set automatically by a transform write. Set explicitly by Slot.Take for DynamicThing slot occupants (~Slot.cs:292640/:292644) and a handful of other special cases (a CursorManager-area site :56523, vehicle/rocket classes :141801/:202841); also SerializeOnJoin sets NetworkUpdateFlags |= ushort.MaxValue (:303033) so the join package includes everything |
2 |
Thing |
interactable-state delta: BuildInteractableUpdate (:303291) writes the dirty Interactables |
Interactable.IsDirty machinery |
4 |
Thing |
indestructable DamageState |
damage code |
8 |
Thing |
ReagentMixture |
reagent code (VerifyUpdateType at :303230 clears this bit if ReagentMixture == null) |
16 |
Thing |
IsBurning flag |
IsBurning setter (:298530) |
32 |
Thing |
CustomColor index + CustomName |
CustomName setter (:298559) and color setters |
64 |
Structure |
CurrentBuildStateIndex (sbyte): Structure.BuildUpdate (:295569) |
CurrentBuildStateIndex setter (:295484, server only) |
128 |
Thing |
EnergyConvected + EnergyRadiated |
thermal code |
256 (Setting) |
named Setting in NetworkUpdateType.Thing (286632); not written by Thing itself |
DynamicThing writes DragOffset under it, before its base call (307118-307121); a scan of the decompile finds 63 classes testing it in BuildUpdate |
per class |
512 (Processing) |
named Processing in NetworkUpdateType.Thing (286634); not written by Thing itself |
per class; a scan of the decompile finds 49 classes testing it in BuildUpdate |
per class |
0x1000 (4096 / GenericFlag2) |
free at the Thing level; free on the whole SprayCan chain; other classes use it for their own block |
none on Thing, DynamicThing, Item or Consumable |
nothing in this repo. Spray Paint Plus piggybacked here on Consumable.BuildUpdate through v1.12.1 and no longer does |
0x2000 (8192 / GenericFlag3) |
free at the Thing level; 21 subclasses use it for their own block |
none on Thing, DynamicThing or Item |
nothing in this repo. Spray Paint Plus glow sync used it on Thing.BuildUpdate through v1.12.1 and no longer does |
0x4000 (16384 / GenericFlag4) |
free at the Thing level; 24 subclasses use it for their own block (listed below) |
none on Thing, DynamicThing or Item |
EquipmentPlus active-sensor sync |
(Subclasses such as CircuitHousing/logic units reuse bits 512/1024-32768 for their own device lists; those overlap the "free" range above only for prefab families that don't carry that subclass's serialization, which is exactly why SprayPaintPlus / EquipmentPlus picked bits unused by Consumable / their target type.)
At 0.2.6428.27798 the game names the upper bits privately in NetworkUpdateType.Thing as GenericFlag0 to GenericFlag5 (1024 to 32768, 286636-286646). 16384 (GenericFlag4) is tested in the BuildUpdate and ProcessUpdate of 24 classes: RoboticArmDock (174319), LandingPadCenter (187437), SurvivalToolbelt (322522), RocketMotherboard (343300), AdvancedTablet (345067), GasMask (352697), StepUnit (362027), Plant (364039), Centrifuge (373137), CombustionCentrifuge (374839), CombustionDeepMiner (375300), ElectricDeepMiner (384251), RocketEngineBase (393856), AirConditioner (397073), CircuitHousing (399726), LfoVolume (405568), LogicBatchSlotReader (406546), LogicPidController (409173), LogicSlotReader (410068), LogicUnitProcessor (412696), PassiveSpeaker (414583), SimpleFabricatorBase (426961), AdvancedSuit (434260) and SuitBase (436605). Their subclasses inherit it, and Logicable (387627) and InternalCombustion (41857) raise it on a parent Thing. The 0.2.6228.27061 reading called the bit free in vanilla.
Two distinct transform-serialization paths exist and use different fields:
- Per-tick delta (bit 1): BuildUpdateTransform → Position / Rotation (the live-transform mirror fields). Neither Structure nor Cable overrides BuildUpdateTransform/ProcessUpdateTransform.
- Join package (full state): Thing.SerializeOnJoin (:303031) calls WriteTransform(writer). Thing.WriteTransform (:303025) writes ThingTransformPosition / ThingTransform.rotation; Structure.WriteTransform (:295588) overrides it to write RegisteredPosition / RegisteredRotation instead. The joining client applies these via Thing.DeserializeNew (:303168) → Create<Thing>(...) + SnapTransform(transformPosition, transformRotation) (:303176/:303179). Structure.SerializeOnJoin (:295594) additionally writes RegisteredLocalGrid + WriteQuaternion(Direction), read back by Structure.DeserializeOnJoin (:295602).
Consequence for a mod that moves/rotates a placed Structure server-side: the visible mesh follows ThingTransform.rotation immediately, but for the change to (a) save it must update Structure.RegisteredRotation (the save reads RegisteredRotation, not the live transform; Structure.InitialiseSaveData :295797), (b) reach already-connected clients it must set NetworkUpdateFlags |= 1 server-side, and © reach late-joiners correctly it relies on RegisteredRotation (the join-package transform), and Structure.Direction should be set too for the join-package Direction field. See ../GameSystems/PlacementOrientation.md ("Changing a placed Structure's rotation at runtime") for the full recipe and the option comparison; MoveToWorld is not usable for this (it is DynamicThing's inventory-eject op, a no-op for a non-slotted Structure).
GenericFlag2 (bit 12) and GenericFlag3 (bit 13): what the game does with them¶
The game names the upper bits privately in NetworkUpdateType.Thing: GenericFlag2 = 4096 and GenericFlag3 = 8192 (286640-286642), beside Setting = 256, Processing = 512 and GenericFlag0, 1, 4 and 5 (286632-286646). Neither is used by Thing, DynamicThing or Item: Thing.BuildUpdate uses 1, 2 (through BuildInteractableUpdate), 4, 8, 16, 32 and 128 (328286-328316), DynamicThing adds 256 (307118) and Item 2048 (311489).
"Free at the Thing level" is the whole of the guarantee, and it is a weak one:
0x1000is free on the entireSprayCanchain.Thing,DynamicThingandItemas above,Consumable1024 (180146), andSprayCandoes not overrideBuildUpdate. Other classes do use 4096 for their own block, among themPipe(390610),Plant(363980),Human(369415),AtmosphericItem(346098),Waypoint(121833) and several logic and device classes. A mod writing under this bit for one concrete type therefore collides with nothing, and a mod writing under it for everyThingcollides with all of those.0x2000is free onThingbut not on 21 subclasses.RoboticArmDock(174315),AudioSequencer(183739),LandingPadCenter(187433),SurvivalToolbelt(322518),AdvancedTablet(345063),FertilizedEgg(350792),GasMask(352693),Plant(364034),CombustionCentrifuge(374835),CombustionDeepMiner(375296),RocketEngineBase(393852),AirConditioner(397077),CircuitHousing(399722),LfoVolume(405580),LogicBatchReader(406145),LogicBatchSlotReader(406538),LogicPidController(409169),LogicUnitProcessor(412692),LogicWriterBase(413371),AdvancedSuit(434256) andSuitBase(436601) write their own block under 8192 inBuildUpdateand read it inProcessUpdate, and their subclasses inherit that.
A mod byte written at the end of Thing.BuildUpdate and read at the end of Thing.ProcessUpdate stays aligned across that overlap, because it sits at the same point on both sides. What the overlap costs is traffic and re-applied state in both directions: when the game raises 8192 on one of those classes for its own reason, the mod's trailing byte rides along and every client re-applies the authority's state for that Thing; when the mod raises 8192 to announce its own change on one of them, that class's own 8192 block is sent and re-applied on the clients too.
Spray Paint Plus stopped riding these bits¶
Spray Paint Plus piggybacked bit 12 for spray can colour and bit 13 for glow through the published v1.12.1. It does not any more, and the reason generalises to any mod considering the same trick, so it is recorded here rather than only in the mod.
Not in a release yet. The move off both bits landed in commit
0b1c05a5(2026-09-24) and is targeted at Spray Paint Plus v1.13.0. The published Workshop build is v1.12.1 and still carries both bits, andPluginVersionin the working tree still reads1.12.1, so the bump has not happened either. Anything below describing the new arrangement describes code no player has.
What it used to do, for the record and because the shipped build still does it:
| Bit | Feature | Raised by | Delta update | Join and new-Thing record |
|---|---|---|---|---|
0x1000 (4096, GenericFlag2) |
spray can colour | SprayPaintHelpers.UpdateSprayCanServer (PaintColorNetworkFlag) |
one Int32 colour index from postfixes on Consumable.BuildUpdate / ProcessUpdate (ConsumableSyncPatch.cs), only for a SprayCan and only when the bit is set |
one Int32 for every SprayCan, always, from postfixes on Consumable.SerializeOnJoin / DeserializeOnJoin |
0x2000 (8192, GenericFlag3) |
glow | the glow apply path in GlowPaintPatches.cs when a glow stroke adds or removes glow (GlowNetworkFlag in GlowPaintHelpers.cs) |
one byte from postfixes on Thing.BuildUpdate / ProcessUpdate (ThingGlowSyncPatches.cs), for every Thing whose update carries the bit |
one byte for every Thing, always, from postfixes on Thing.SerializeOnJoin / DeserializeOnJoin |
StreamSync.cs settled both features once per session: each ran only if its stream code compiled against that game build and all four of its patch classes applied, and a feature that was off never raised its bit and wrote and read nothing. The mod relied on every peer reaching the same verdict because the mod build is enforced equal (MOD.Networking.Required) and so is the game version; its own comments named the remaining gap, a dedicated server's separately compiled Assembly-CSharp. All three of ConsumableSyncPatch.cs, ThingGlowSyncPatches.cs and StreamSync.cs were deleted in 0b1c05a5.
Why it moved, which is the part worth keeping. The bits never misaligned anything, and that was not the risk. The risk is that the per-Thing record is {packed id}{ushort flags}{payload} with no length prefix, no checksum and no trailing marker (see "The tick and join payloads carry no framing" below), so a mod's trailing bytes are held in place by nothing but both peers agreeing to write and read the same count at the same point. A game update that gave bit 12 or bit 13 a meaning of its own at the Thing level would shift every later field in the packet, and it would do so with no build error, no exception and no log line: the reference check testrig check-bindings runs resolves members, and both bits resolve fine because they are the mod's own literals. It is a break shape with no instrument pointed at it.
The replacement is a LaunchPadBooster INetworkMessage per feature (GlowStateMessage, SprayCanColorSyncMessage), which carries its own packet type, its own TypeID and its own buffer on channel 169, so a mistake in one cannot move a byte of anyone else's data. The general lesson: a free NetworkUpdateFlags bit is free only until the game wants it, and the byte it announces is protected by nothing. Prefer a framed mod message for any state a mod adds to a Thing. The protocol that replaced it is on ../Protocols/SprayPaintPlusNetworking.md.
Line numbers are the 0.2.6428.27798 decompile; the v1.12.1 mod file references are to files that exist in git history up to commit 0b1c05a5.
Every bit is set on every Thing after every join¶
Thing.SerializeOnJoin raises the whole mask as its first statement (328071-328074):
public virtual void SerializeOnJoin(RocketBinaryWriter writer)
{
NetworkUpdateFlags |= ushort.MaxValue;
DamageState.UpdateType |= DamageUpdateType.All;
Thing.SerializeAllOnJoin (328450) walks OcclusionManager.AllThings.Active() and serializes every live root Thing into the join package, so on the authority, every Thing in the world comes out of a join with all sixteen bits set. Thing.SerializeDeltaState (328227) then picks up every Thing for which IsNetworkUpdate() is true (NetworkUpdateFlags != 0) and clears the mask after writing, so the first delta tick after any join re-sends every Thing's full state to every connected client, and the mask settles back to zero on the tick after that.
Three consequences:
- A join is the most expensive tick in the session by a wide margin, and the cost scales with world size rather than with how much changed.
- Only root Things are in the join package:
SerializeAllOnJoinskips anything wherecurrent.RootParent != current, so slotted and parented Things ride with their parent. - Any mod reading
NetworkUpdateFlagsto decide whether something changed gets a false positive for every Thing on the first tick after a join. A mod that appended data under a flag bit was, by the same mechanism, accidentally self-healing: whatever it missed during the join window was re-sent on that tick. A mod that moves to its own message loses that accident and has to push join state deliberately.
SerializeOnJoin and DeserializeOnJoin also carry mid-session new Things¶
The two join methods are not only the join package. Thing.NewToSend is a SyncList<Thing> (323306) that is serialized inside the ordinary per-tick state stream, not the join stream:
(FragmentHandler.WriteStateImmediate, 218077-218080, with the matching Thing.NewToSend.Deserialize(reader) at 218139.) SyncList<T>.Serialize calls ISyncListable.Serialize on each queued item, and Thing's implementation is a straight delegation (328188):
public void Serialize(RocketBinaryWriter writer)
{
if (ReferenceId == 0L)
{
throw new System.Exception("Tried to send ThingData to client with a referenceId of 0!");
}
SerializeOnJoin(writer);
}
The read side is the SyncList<Thing> factory Thing.DeserializeNew (328208), which reads the packed id, prefab hash, position and rotation, calls Create<Thing>(...), then thing.DeserializeOnJoin(reader) and SnapTransform.
So a Harmony patch on SerializeOnJoin / DeserializeOnJoin fires in two quite different situations: once per Thing when a client joins, and again for every Thing that appears mid-session while anyone is connected. That is easy to misread as join-only, and it is the reason a patch there must not assume it is running during a join. SyncList frames itself with a one-byte encoding tag (Empty / Byte / Ushort / Uint) followed by the count in that width (223252-223270); an empty list costs exactly one byte.
../GameClasses/Thing.md reaches the same two paths from the other direction, tracing the -1 colour sentinel through them, and names the queue sites (Thing.Create<T> at 324904-324907 and 324945-324955) and the server tick that drains them (NetworkServer.NetworkUpdate, 219609-219647). Read that section for the colour specifics; this one is about which methods run when.
The tick and join payloads carry no framing¶
Inside the state payload there is no per-record length prefix, no checksum and no resynchronisation marker. A Thing's delta record is written by Thing.SerializeDeltaState (328227) as Network.WritePackedId, then writer.WriteNetworkUpdateType(networkUpdateType) (the ushort mask), then current.BuildUpdate(writer, networkUpdateType), and read back by the mirror-image DeserializeDeltaState (328248). The only counts on the wire are element counts, back-patched by Network.WriteIndex: how many Things follow, not how many bytes each occupies. FragmentHandler.Mark looks like framing and is not; it appends to a diagnostic _sectionBytes list and writes nothing:
private static void Mark(RocketBinaryWriter writer, string name)
{
_sectionBytes.Add((name, writer.Position - _sectionMark));
_sectionMark = writer.Position;
}
The one length that does exist sits outside the payload, at the transport frame: ApplyState reads a four-byte decompressed size from offset 0 before handing the LZ4-unwrapped buffer to the reader (218234-218240). It bounds the buffer; it says nothing about any record inside it.
Consequence: writer and reader are bound together by agreement alone, over the whole stream and not just one record. A single byte written and not read, anywhere in the sequence, shifts every field after it, and the failure surfaces as a NullReferenceException from DeserializeDeltaState's own guard ("Error: thing number: {i} is null during network update") with a reference id that is simply misparsed garbage, far away from the code that caused it.
The dedicated server never reads any of it. The gate is not inside the serialization methods; it is at the channel dispatch in NetworkManager (279286-279303), where three channels are read only by a client:
case NetworkChannel.StateTick:
if (NetworkRole == NetworkRole.Client)
{
FragmentHandler.ReceiveState(Buffer, num2);
}
break;
case NetworkChannel.PhysicsTick:
if (NetworkRole == NetworkRole.Client)
{
FragmentHandler.ReceivePhysics(Buffer, num2);
}
break;
case NetworkChannel.PlayerJoin:
if (NetworkRole == NetworkRole.Client)
{
NetworkClient.ReceiveJoinFragment(Buffer, num2).Forget();
}
break;
So on a server, ProcessUpdate, DeserializeDeltaState, DeserializeOnJoin and DeserializeNew are never reached through the network at all: the server is a write-only peer on the state and join streams. A patch on the read half is therefore dead code on a dedicated server and on a listen host's server role, which means a mod whose reader is broken shows no symptom until a client runs it. Testing a serialization patch on a server alone proves nothing about the half that matters. See ./NetworkRoles.md.
Verification history¶
- 2026-04-20: page created from the Research migration; verbatim content lifted from F0029a (primary), F0026, F0126a, and F0387.
- 2026-05-12: added "Thing-level bit map and the transform bit (bit 1)" section after a decompile pass on
Thing.NetworkUpdateFlags/BuildUpdate/ProcessUpdate/BuildUpdateTransform/ProcessUpdateTransform/WriteTransform/SerializeDeltaState/SerializeOnJoin/DeserializeNewandStructure.WriteTransform/SerializeOnJoin/DeserializeOnJoin/BuildUpdateinAssembly-CSharp.decompiled.cs(0.2.6228.27061), plus a whole-file grep ofNetworkUpdateFlags |= 1sites. Findings: enumerates the Thing-owned bits (1transform,2interactables,4damage,8reagent,16burning,32colour/name,128thermal),Structure's64(build state); documents that bit 1 is never set automatically by a transform write (only bySlot.Takefor slot occupants and a few special cases) and that two distinct transform-serialization paths exist (per-tick delta viaBuildUpdateTransform→Position/Rotation; join package viaWriteTransform→ forStructure,RegisteredPosition/RegisteredRotation). Additive; cross-linked toPlacementOrientation.md(which carries the "re-roll a placed structure at runtime" recipe and option comparison). Found via investigating runtime re-rolling of a placed straightCable; full dump in.work/2026-05-12-cable-rotation/notes/mutation-findings.md. - 2026-09-21 (game version 0.2.6428.27798): added "Spray Paint Plus bits and their collision risk" and a
0x2000row in the bit map, from the 0.2.6428.27798 decompile and the Spray Paint Plus v1.12.0 source (SprayPaintHelpers.cs,GlowPaintHelpers.cs,GlowPaintPatches.cs,ConsumableSyncPatch.cs,ThingGlowSyncPatches.cs,StreamSync.cs). It records the game's own private names for the bits (NetworkUpdateType.Thing.GenericFlag2 = 4096,GenericFlag3 = 8192, 286640-286642), which classes use 4096 and 8192 for their own blocks, and why neither bit misaligns the stream. Re-verified "GenericFlag2 (bit 12) for SprayPaintPlus color sync" against 0.2.6428.27798 and restamped it; its source quote now matchesSprayPaintHelpers.cslines 11-12 exactly (the trailing period was not in the source) and includes the constant. Additive: no existing claim is contradicted, so no fresh validator. Bumped frontmatter verified_in / verified_at and added sources. - 2026-09-24 (game version 0.2.6428.27798): conflict on "by what mechanism Spray Paint Plus syncs glow and spray can colour". Previous claim, in "Bitmask semantics", the bit map and "Spray Paint Plus bits and their collision risk": the mod piggybacks bits 12 and 13 of
Thing.NetworkUpdateFlags, with the write and read halves inConsumableSyncPatch.cs,ThingGlowSyncPatches.csand the per-session verdict inStreamSync.cs. New finding: neither bit is used, both features moved onto LaunchPadBoosterINetworkMessagetypes, and all three of those files were deleted. Fresh validator verdict: B is correct, quotingPlugin.cs:188-189(MOD.Networking.RegisterMessage<GlowStateMessage>(); MOD.Networking.RegisterMessage<SprayCanColorSyncMessage>();) andGlowPaintHelpers.cs:23-30("GlowNetworkFlag, Thing.NetworkUpdateFlags bit 13 (0x2000, GenericFlag3), used to live here. It is gone as of v1.13.0"). It checked all 14 remainingNetworkUpdateFlags/GenericFlag/0x1000/0x2000hits in the mod folder and found every one to be a historical or warning comment, no live bit-set or bit-test, and confirmed the three deleted files are absent from HEAD. Result: the mod claims are now written in the past tense and quarantined into a new section, "Spray Paint Plus stopped riding these bits", under an unreleased marker, because the change is committed (0b1c05a5) and not published; the shipped v1.12.1 still uses the bits, so the old mechanism table is kept rather than deleted. The game-internals content of the old section (which classes use 4096 and 8192, and why a trailing byte stays aligned across the overlap) was not contradicted and is preserved verbatim under a renamed heading, "GenericFlag2 (bit 12) and GenericFlag3 (bit 13): what the game does with them". The generalisable lesson, that a free bit is free only until the game wants it and the byte it announces is protected by nothing, is recorded there rather than left in the mod. - 2026-09-24 (same pass, additive, game version 0.2.6428.27798): three new sections from a decompile pass on the state-tick and join paths, none of them contradicting anything on the page. "Every bit is set on every Thing after every join" records
Thing.SerializeOnJoinopening withNetworkUpdateFlags |= ushort.MaxValue(328073),SerializeAllOnJoinwalking every live root Thing (328450, skippingRootParent != current), and the consequence that the first delta tick after a join re-sends every Thing's full state. "SerializeOnJoin and DeserializeOnJoin also carry mid-session new Things" recordsThing.NewToSend(323306) being serialized insideFragmentHandler.WriteStateImmediate(218077) throughThing.Serialize(328188), which delegates straight toSerializeOnJoin, withThing.DeserializeNew(328208) as the read side, so a patch on either method fires outside joins too. "The tick and join payloads carry no framing" records that the only counts on the wire are element counts fromNetwork.WriteIndex, thatFragmentHandler.Markis diagnostic and writes nothing, that the sole length prefix is the four-byte decompressed size at the transport frame (218234), and thatNetworkManager's channel dispatch gatesStateTick,PhysicsTickandPlayerJoinonNetworkRole == NetworkRole.Client(279286-279303), making a server a write-only peer on all four serialization methods. Bumped frontmatterverified_atand added two sources lines;verified_inunchanged at 0.2.6428.27798. - 2026-09-21 (second pass, game version 0.2.6428.27798): conflict on "whether
0x4000is free in vanilla". Previous claim, in the bit map: "|0x4000(16384) | free in vanilla | none | EquipmentPlus active-sensor sync |". New finding: free at theThinglevel, but 24 vanilla classes use it for their own block. Fresh validator verdict: B is correct, quotingCircuitHousing.BuildUpdate(399726-399729,writer.WriteInt64(Devices[4]?.ReferenceId ?? 0);under 16384) with the matching read at 399770-399773. It read all 48 tests and confirmed the list of 24 is complete, that subclasses inherit the use, thatRocket's 16384 tests belong to a non-Thingclass with its own flags, and that the dedicated-server decompile agrees. Result: the0x4000row corrected and the 24 classes listed under the table, both under inline 0.2.6428.27798 stamps. Added, as additive rows, the game's names for256(Setting) and512(Processing), withDynamicThing's use of 256 and the class counts from a scan of the decompile. The section stamp stays at 0.2.6228.27061. Added a sources line. - 2026-09-27:
relatedlisted../GameClasses/Consumable.md, a page that has never existed (the entry dates from the 2026-04-20 migration, and no commit ever added that file). Replaced with../Patterns/BinaryStreamSafety.md, the page that documents appending a field after the vanillaConsumabledata inBuildUpdate/ProcessUpdate, the bit-12 path this page records. Link fix only; no stamp changed.
Open questions¶
None.