Table of Contents

Shared (World-State) Views

One View instance, refreshed and diffed once per tick, fanned out to every subscriber.

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

๐ŸŽฏ What it solves

World state โ€” NPCs, terrain objects, the player roster โ€” is the same for every client watching it. Computing that delta independently per subscriber wastes CPU proportional to subscriber count for data that doesn't vary by who's asking. Shared Views let many clients subscribe to the exact same query result: the engine refreshes and diffs it once per tick no matter how many clients are subscribed.

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

PublishView(name, view) registers a single ViewBase instance as the published target. During the Output phase, the engine refreshes that one View, computes its Added/Removed/Modified delta once, and serializes the resulting TickDeltaMessage once for steady-state clients โ€” the same byte buffer is then memcpy'd into every subscriber's send buffer. Subscriber count only affects the fan-out step, not the delta computation or serialization cost. The View instance must be dedicated to subscriptions โ€” it cannot also be wired as a system's query input (see Published/System-Input View Separation).

๐Ÿ’ป Usage

[Component("Game.Npc", 1)]
public struct Npc
{
    [Index] public int Health;
}

[Archetype]
public class NpcArch : Archetype<NpcArch>
{
    public static readonly Comp<Npc> Data = Register<Npc>();
}

using var tx = dbe.CreateQuickTransaction();

// Dedicated View instance for subscriptions โ€” do not reuse a system's input View here.
var worldNpcsView = tx.Query<NpcArch>()
    .WhereField<Npc>(n => n.Health > 0)
    .ToView();

var worldNpcs = runtime.PublishView("world_npcs", worldNpcsView, SubscriptionPriority.Normal);

// Subscribe a client to it; the runtime diffs against their previous subscription set.
runtime.SetSubscriptions(clientContext, worldNpcs);
Option Default Effect
priority SubscriptionPriority.Normal Critical always pushed; Normal throttled at overload Level 1+; Low throttled aggressively, paused at Level 4

โš ๏ธ Guarantees & limits

  • One delta, one serialization, N memcpys โ€” cost scales with View size and change volume, not with subscriber count, for the steady-state (no pending events, no sync) case.
  • Must be a dedicated View โ€” using the same instance as a system's query input throws InvalidOperationException; the View's change-tracking ring buffer has a single consumer.
  • New subscribers receive the View's current entity set via incremental sync (batched across ticks for large Views), not a free pass to the live delta stream โ€” see Incremental Sync in the design doc.
  • All subscribers to a shared View see identical data; there is no per-client filtering within a shared View (use a per-client View factory for that).

๐Ÿงช Tests

  • SubscriptionStressTests โ€” MultiClient_50Clients_SharedView_AllReceiveDeltas: one shared View fanned out to 50 concurrent subscribers
  • ViewDeltaTests โ€” DeltaBuilder_SharedView_ProducesCorrectDelta/DeltaBuilder_SecondCall_OnlyNewEntities: delta correctness across ticks for a shared PublishedView