Table of Contents

Per-Tick Delta Computation & Encoding

Each tick, the engine figures out exactly what changed in a published View and encodes only that.

Status: โœ… Implemented ยท Visibility: Public ยท Level: ๐Ÿ”ต Core ยท Category: Subscriptions

๐ŸŽฏ What it solves

A client cache only needs three things per tick: what entered, what left, and what changed on entities that stayed. Computing that diff against the previous tick โ€” across every component, including changes that never touch an indexed field โ€” is exactly the kind of bookkeeping a game developer should never write by hand. Per-Tick Delta Computation & Encoding is the step that turns a published View's raw state into that Added/Removed/Modified triple and puts only the changed bytes on the wire, with zero query code from the developer.

โš™๏ธ How it works (in brief)

After WriteTickFence, the Output phase refreshes every published View and reads two sources of change: the View's own ring buffer (entities whose [Index]-marked fields changed) and PreviousTickDirtyBitmap (every SV/Transient component chunk written this tick, indexed or not). The union becomes the View's Modified set โ€” this is what makes "any field changed" detection work, not just indexed ones. For each Modified entity, only the components whose chunk was actually dirty are read and encoded; untouched components on the same entity are omitted entirely. Added entities get a full component snapshot; Removed entities are just an ID. The result is handed to the wire-transport layer for serialization (see TCP Transport & Wire Format) โ€” this feature stops at "what bytes represent this change," not how they travel.

Sub-features

Sub-feature Status Use it for
Component-Level Dirty Encoding (v1) โœ… Implemented Default behavior today โ€” every Modified entity sends full bytes for each dirty component
Per-Field Dirty Encoding (v1.1) ๐Ÿ“‹ Planned Future bandwidth optimization โ€” send only the fields that actually changed within a dirty component

โš ๏ธ Guarantees & limits

  • Modified detection covers all field changes, not just indexed ones โ€” the PreviousTickDirtyBitmap supplement exists specifically because the View ring buffer only fires for [Index]-marked fields.
  • Per-component filtering, not per-entity โ€” a Modified entity only carries the components whose chunk was dirty this tick; components that didn't change are never serialized even if the entity itself did change.
  • Delta computation happens once per View, not once per client โ€” every subscriber to a shared View reuses the same Added/Removed/Modified result; cost scales with View size and change volume, not subscriber count.
  • Versioned components are read unconditionally for Modified entities โ€” they have no DirtyBitmap, so the encoder includes them whenever the entity is in the Modified set rather than risk omitting a real change.
  • This feature only determines what changed and produces the wire structs โ€” see TCP Transport & Wire Format for serialization, framing, and delivery.

๐Ÿงช Tests

  • ViewDeltaTests โ€” DeltaBuilder_SharedView_ProducesCorrectDelta/DeltaBuilder_SecondCall_OnlyNewEntities: Added/Modified/Removed computed once per View, not once per client
  • SubscriptionStressTests โ€” HighEntityChurn_SpawnDestroy_DeltasConsistent: sustained spawn/destroy churn under an active subscription