Class SubscriptionsCommands
What an application system reads commands through, and answers them with.
public sealed class SubscriptionsCommands
- Inheritance
-
SubscriptionsCommands
- Inherited Members
Remarks
Everything here describes exactly one tick. The Engine-Pre drain fills the buffers before the application's track runs, so a command is visible for the tick it was drained into and for no other — never zero ticks, never two (SUB-08). A system that wants a command to outlive its tick copies it into state, which is what state is for.
Semantic validation belongs to the caller. What reaches here has passed the wire's own checks, the declaration's role list, its rate limit and its stateless pre-check. Range against the current world, cooldowns and ownership are the system's, because the state they must agree with is the system's (01-model § 7).
Fields
SleepingClusters
Blocks the projection pass read, and blocks it declined because their cluster was dormant, since start.
public int SleepingClusters
Field Value
Remarks
The second number is zero unless the application enabled cluster dormancy, which is what makes the pair readable: a projection cost that did not move means one thing if nothing was declined and the opposite if most of it was.
Properties
AabbChurn
Clusters whose bounds moved per tick, against the active clusters there were — slice 5's gating quantity.
public (long Moved, long Active, long Ticks) AabbChurn { get; }
Property Value
BroadClustersReached
Distinct clusters every cell's broad phase reached, against the entity candidates it collected from them.
public long BroadClustersReached { get; }
Property Value
BroadClustersStructureChanged
Clusters the broad phase reached whose structure changed.
public long BroadClustersStructureChanged { get; }
Property Value
BroadSlots
Live slots the broad phase reached, and those whose structure changed this tick.
public (long Reached, long Changed) BroadSlots { get; }
Property Value
ChangedClusterCensus
The changed-cluster list's census: clusters named, ticks published, ticks that degraded to "all", and the stopwatch ticks spent publishing.
public (long Named, long Ticks, long CoverAll, long StopwatchTicks) ChangedClusterCensus { get; }
Property Value
Remarks
Slice 1 ships a measurement, and this is it. 9.2 % of watched SLOTS change per tick (20 § 3.1); the CLUSTER figure is what decides whether gating the projection pass on it is worth building, and it was unknown. Cumulative, for the reason 18 § 8.4 records.
ClusterContainment
Occupied slots in clusters wholly inside the enter radius, beside those in clusters the disc clips. Cluster-granularity path only.
public (long Interior, long Boundary) ClusterContainment { get; }
Property Value
ClusterSize
Cluster radius (box half-diagonal), mean and largest, against the grid cell hosting clusters and the interest cell.
public (double MeanRadius, double MaxRadius, double GridCellSide, double InterestCellSide, long Samples) ClusterSize { get; }
Property Value
EnterFlow
ENTER and LEAVE records published since start, beside the enters the per-frame budget deferred.
public (long Entered, long Left, long Deferred) EnterFlow { get; }
Property Value
Remarks
The ratio separates a view still filling from one being re-told what it already knew, and the two want opposite fixes. Enters far above leaves is a queue draining, and it drains faster with a larger budget. Enters and leaves in step, at a rate far above what the world actually spawns and moves, is the same entities crossing the interest boundary and crossing back — there a larger budget spends more wire on the same entities arriving again. Read it beside ViewFill, which says whether the client's world is still growing.
EntityCandidatesCollected
Entity candidates every cell's broad phase collected.
public long EntityCandidatesCollected { get; }
Property Value
EntriesMigrated
Replication entries carried from one cluster to another since start, summed over every archetype.
public long EntriesMigrated { get; }
Property Value
Remarks
A relocation is invisible to the world and loud on the wire. The entity did not move and nothing about it changed, but it now sits in a different cluster — so a session that watches the source and not the destination is told the entity LEFT, and one that watches the destination and not the source is told it ENTERED. Only a session holding both suppresses the pair. Read against LeaveCauses: if the two rates track, the churn belongs to whatever relocates entities rather than to observers moving.
EpochEnter
Mean microseconds a subscriptions chunk spends entering its epoch, and how many chunks were measured.
public (double MeanUs, long Chunks) EpochEnter { get; }
Property Value
FrameBalance
The frame stage's effective worker count and parallel efficiency; zero unless phase timing is on.
public (double Effective, double Efficiency, long Ticks) FrameBalance { get; }
Property Value
FramePhases
The frame stage's phases, in ms of CPU summed over workers since start. All zero unless phase timing was enabled.
public (double Gather, double Select, double Sweep, double Sort, double Encode, double Publish) FramePhases { get; }
Property Value
FramePrologueMs
The frame stage's single-threaded prologue, per tick, in ms.
public (double Prologue, double Sweep, double Prepare) FramePrologueMs { get; }
Property Value
FrameSpan
The frame stage's span against the busy time inside it, and the concurrency the two imply.
public (double SpanMs, double BusyMs, double Concurrency, double StartSpreadMs) FrameSpan { get; }
Property Value
FullGatherCauses
Why the incremental path fell back to the full walk: reset, forced, incomplete view, or behind by more than one tick.
public (long Reset, long Forced, long Incomplete, long Behind) FullGatherCauses { get; }
Property Value
GatherRunShape
Interest runs the gather walked, and how many of them had nothing to say.
public (long Walked, long Empty) GatherRunShape { get; }
Property Value
GatherShape
How many frames since start were built as a difference against the session's previous tick, and how many walked the whole view (15 § 3.2).
public (long Difference, long Full) GatherShape { get; }
Property Value
Remarks
Cumulative, as InterestSharing now is. It exists for the same reason: a measurement showing no change means "the difference does not pay" or "the difference never happened", and those call for opposite next steps. A ratio near zero is the second.
GatherSlots
Hit slots the difference read, and hit slots it carried forward without reading, since start (15 § 3.2).
public (long Visited, long Carried) GatherSlots { get; }
Property Value
GroupingDiagnostic
Cluster runs accepted whole against runs the disc clipped, and the slots each accounted for.
public (long Ungroupable, long Keyed, double SpanX, double SpanY, double CellSide) GroupingDiagnostic { get; }
Property Value
IdentityFlow
Network identities minted, released and reused since start — the denominator for LeaveCauses's stale term.
public (long Minted, long Released, long Reused) IdentityFlow { get; }
Property Value
Remarks
Every archetype shares one allocator, so the first non-null state answers for all of them.
InterestBookkeeping
The interest bookkeeping the phases leave out, and the resolved groups' total, in microseconds. Needs MeasureInterestPhases.
public (double InteriorUs, double RetainUs, double CloseUs, double GroupUs) InterestBookkeeping { get; }
Property Value
InterestCoherence
How much of the interest answer has been what the sessions already held, since start.
public (long RunsUnchanged, long RunsTotal, long SessionsCoherent, long SessionsTotal) InterestCoherence { get; }
Property Value
InterestPhases
Where the interest stage's time has gone since start, in microseconds. Needs MeasureInterestPhases.
public (double BroadUs, double NarrowUs, double FlushUs) InterestPhases { get; }
Property Value
InterestSharing
Last tick: how many interest cells the broad phase resolved, and how many sessions were served from a resolution somebody else paid for.
public (long Cells, long SessionsShared) InterestSharing { get; }
Property Value
- (long fileTableOffset, long manifestOffset)
The cells resolved and the sessions shared.
Remarks
A diagnostic, and the one the cell-keyed broad phase is judged by. Its whole benefit is that co-located observers resolve once, so a deployment whose sessions never share a cell pays the grouping's sort and gets nothing back — and no timing comparison can tell that case from a design that does not work, because both look like "no change". Reading it is how an operator, or a measurement, tells them apart.
Cumulative since start, and it was per tick until a zero from it was believed. Sampled after a load generator disconnects, the per-tick
form read "0 cells, 0 sessions" and was written up as the grouping failing at the density where it matters most. There were simply no sessions
left to group. See the note at the reset site in InterestPass.
InterestSpan
The interest stage's parallel shape per tick: span, summed busy, slowest chunk, start spread, heaviest group and its size, serial prologue.
public (double SpanMs, double BusyMs, double MaxChunkMs, double StartSpreadMs, double HeaviestGroupMs, double HeaviestGroupMembers, double PrologueMs) InterestSpan { get; }
Property Value
- (double SpanMs, double BusyMs, double MaxChunkMs, double StartSpreadMs, double HeaviestGroupMs, double HeaviestGroupMembers, double PrologueMs)
InterestStageShape
The interest stage's shape, cumulative since start (design 23, phase 0): ticks folded, wall and summed busy, the heaviest group and how late it started, chunks that did almost nothing, distinct threads that ran chunks, and the snapshot claims that read a cluster's page privately because another worker was filling it. Durations in Stopwatch ticks.
public (long Ticks, long SpanTicks, long BusyTicks, long HeaviestTicks, long HeaviestStartTicks, long PhantomChunks, long Threads, long PrivateReads, long Fills, long Reads, long ActiveClusters, long Prefills, long PrefillTicks) InterestStageShape { get; }
Property Value
- (long Ticks, long SpanTicks, long BusyTicks, long HeaviestTicks, long HeaviestStartTicks, long PhantomChunks, long Threads, long PrivateReads, long Fills, long Reads, long ActiveClusters, long Prefills, long PrefillTicks)
InteriorAdmission
Clusters an interest cell admitted whole, and the entity reads that avoided.
public (long Clusters, long EntitiesSkipped) InteriorAdmission { get; }
Property Value
LeaveCauses
Why the leaves were sent: geometry, a reissued identity, or the known-set sweep — beside what the interest pass merely stopped reaching.
public (long Considered, long Interest, long Stale, long Swept) LeaveCauses { get; }
Property Value
ObserverMotion
How far the OBSERVERS moved: steps compared, total displacement in millimetres, and placed session-ticks seen.
public (long Steps, long Millimetres, long Placed) ObserverMotion { get; }
Property Value
Remarks
The quantity every enter and leave rate is a consequence of. An observer advancing d in a tick sweeps about 2Rd of area out of
the back of its disc, so at a known density the entities it must drop are arithmetic, and a churn figure read without it is a number with no
expected value beside it.
It is the counter that found the SWG demo binding its sessions to an unstable iteration order. The viewpoints were moving 118.8 m per tick against the 0.1 m a player covers at 50 Hz, and every symptom read as an engine fault — an enter-to-leave ratio of 1.05, an enter backlog that never drained, leaves naming entities that had not moved — followed from it. Nothing else in the track reported observer speed, so nothing else could tell a busy world from a teleporting camera.
ObserverStationary
Session-ticks whose viewpoint did not move at all.
public long ObserverStationary { get; }
Property Value
OpenSessions
Every session that is open right now, for an application that has to touch all of them — placing their observers, most of it.
public OpenSessionView OpenSessions { get; }
Property Value
ProjectPrologueMs
The projection stage's serial blocks step per tick, split into block creation, parked-entry drain and the watched-block gather, and its parallel busy time per tick, in ms. Zero unless phase timing is on.
public (double Create, double Drain, double Gather, double Busy) ProjectPrologueMs { get; }
Property Value
ProjectionBlocks
Blocks the projection pass read, and blocks it declined because their cluster was dormant, since start.
public (long Projected, long Dormant) ProjectionBlocks { get; }
Property Value
ProjectionSlots
Slots the projection addressed, and the subset it named as changed, since start.
public (long Addressed, long Changed) ProjectionSlots { get; }
Property Value
RunFlow
How a session's cluster set turns over: first seen since its last published frame, carried, and departed. Since start.
public (long FirstSeen, long Carried, long Departed) RunFlow { get; }
Property Value
RunsRetainedByView
Runs a moving member's view retained in one sequential pass instead of a probe each.
public long RunsRetainedByView { get; }
Property Value
SendPath
The send path, measured while phase timing is on — see SendPump.SendPath.
public (double WakeMsPerPublish, double WokenPerPublish, double QueueDelayUs, double SendUs, long SendsSync, long SendsAsync) SendPath { get; }
Property Value
- (double WakeMsPerPublish, double WokenPerPublish, double QueueDelayUs, double SendUs, long SendsSync, long SendsAsync)
SendTotals
Frames and bytes handed to links since start.
public (long Frames, long Bytes) SendTotals { get; }
Property Value
SessionEvents
This tick's session lifecycle events — Opened, Closed — delivered in Engine-Pre, so a system reacts to one with an ordinary transaction in
the same tick.
public ReadOnlySpan<SessionEvent> SessionEvents { get; }
Property Value
ShareCensus
Slots the projection named as changed, against the records the frame stage actually emitted — Layer 4's sharing ratio.
public (long ChangedSlots, long Records) ShareCensus { get; }
Property Value
SharedRunMisses
Why a run with something to say did not reference shared bytes (17 § 18).
public (long Gated, long NoRun, long NotReached) SharedRunMisses { get; }
Property Value
SharedRunSkips
Clusters that published a shared cluster run, and why the others did not (17 § 18).
public (long Published, long Released, long Init, long NoChange) SharedRunSkips { get; }
Property Value
SharedRuns
Runs referenced rather than encoded, the records they carried, the clusters that offered one and could not be shared, and the records the projection encoded once (17 § 18).
public (long Runs, long Records, long Refused, long Built) SharedRuns { get; }
Property Value
SnapshotUse
Clusters opened to fill the shared snapshot, against those read from it.
public (long Opens, long Reads) SnapshotUse { get; }
Property Value
SpanClaims
Span handouts the projected-component mask dropped, against those it let through.
public (long Suppressed, long Admitted) SpanClaims { get; }
Property Value
SparseTopology
Runs sparse sessions did not emit, and the content runs synthesized for them from the changed-block tables.
public (long Skipped, long Synthetic) SparseTopology { get; }
Property Value
StaleMask
Retained slots read in full because the block's change mask named another tick, and the runs that caused it.
public (long Slots, long Runs) StaleMask { get; }
Property Value
TopologyRunsRetained
Runs a session re-emitted from its maintained topology without running the kernel.
public long TopologyRunsRetained { get; }
Property Value
ViewFill
Mean entities a client holds, and mean enters still owed to it, over the frames that published.
public (double Known, double Owed, long Frames) ViewFill { get; }
Property Value
ViewShape
What moving members' views were made of: clusters wholly inside (admitted whole, or tested), partly inside, and those partial ones' entities.
public (long AdmittedWhole, long TestedWhole, long Partial, long PartialInside, long PartialTotal) ViewShape { get; }
Property Value
VisitParts
What the reduced gather's visited slots were made of: newly entered, named by the change mask, owed by an earlier frame.
public (long Entered, long Changed, long Owed) VisitParts { get; }
Property Value
Methods
Commands<T>()
This tick's commands of one type.
public CommandBatch<T> Commands<T>() where T : unmanaged
Returns
- CommandBatch<T>
The batch, empty when nothing arrived.
Type Parameters
TThe command's struct, as the application declared it.
Exceptions
- InvalidOperationException
Twas never declared as a command type.
CopyInterestGroupHistogram(Span<long>)
Copies the interest stage's cumulative log2 histogram of group microseconds (16 buckets) into into.
public void CopyInterestGroupHistogram(Span<long> into)
Parameters
Place(SessionId, Vector3D)
Places a session's spatial observers for THIS tick.
public bool Place(SessionId session, Vector3D position)
Parameters
Returns
Remarks
It applies now, unlike everything on Session(SessionId, int). A profile, a budget or the entity a session controls are configuration, and the request log stages them so they take effect from a known boundary. A viewpoint is not configuration — it is this tick's position — and a tick of latency on it means every session resolves its interest around where it was rather than where it is.
Call it from an application system, once per session per tick, before the replication track runs. A session with a Sphere observer that
has never been placed sees nothing at all: an unplaced session is nowhere, not at the origin, because a default position is a legal world position and
silently giving everyone a sphere around it is worse than giving them nothing.
Reject(SessionId, ushort, byte)
Answers a session's command by sequence number.
public bool Reject(SessionId session, ushort seq, byte reasonCode)
Parameters
sessionSessionIdWhose command it was.
sequshortIts sequence number.
reasonCodebyteAn AckReasons code.
Returns
Reject<T>(in ClientCommand<T>, byte)
Answers a command with a rejection, which reaches the client as an ACK record.
public bool Reject<T>(in ClientCommand<T> command, byte reasonCode) where T : unmanaged
Parameters
commandClientCommand<T>The command being refused.
reasonCodebyteAn AckReasons code; 128-255 are the application's own.
Returns
Type Parameters
TThe command's struct.
Remarks
A command that is simply not applied sends nothing — the client learns only that its seq was consumed. Rejecting is for telling it WHY, and it
costs a record in the next frame, so it is a decision rather than a default.
Session(SessionId, int)
Asks something of a session: the profile it is bound to, the observers it carries, the entity it controls, its budget, or a kick.
public SessionRequest Session(SessionId session, int worker = 0)
Parameters
sessionSessionIdThe session.
workerintWhich worker's segment to stage on: the tick context's worker index inside a chunked parallel system, and 0 on the tick driver or in a serial one.
Returns
- SessionRequest
A builder whose calls are recorded and applied by the next tick's prologue.
Remarks
This is how a session gets a profile, and without one it receives nothing: interest is gathered per profile, so a session bound to none is not
in any tick's session set. The natural place to call it is the SessionEvents loop, on the Opened event.
Nothing happens immediately. A session row belongs to the tick (SUB-05); the calls are appended to a per-worker segment and applied single-threaded at the start of the next tick, before interest is gathered. That is what makes it safe to call from any system on any worker.
One worker per segment, and an out-of-range index is refused rather than clamped. A segment's append is unsynchronized — that is what makes it
free — so two threads sharing one loses records, duplicates them, or throws out of an Array.Resize. Clamping a too-large index onto the last
segment would silently arrange exactly that, so it throws instead: a system that passes the wrong index learns immediately rather than corrupting a
neighbour's list under load.
Exceptions
- ArgumentOutOfRangeException
workeris negative, or at or above the worker count this tick dispatched.
SessionKindOf(SessionId)
The kind a session named in its HELLO, or null when it is gone.
public string SessionKindOf(SessionId session)
Parameters
sessionSessionIdThe session.
Returns
- string
The kind.
TryGetLastSeq(SessionId, out ushort)
The highest sequence number drained into a tick for a session, which is what SELF.lastSeq reports.
public bool TryGetLastSeq(SessionId session, out ushort lastSeq)
Parameters
Returns
Remarks
Every sequence at or below it was applied, rejected or coalesced away, which is exactly what a client's prediction reconciliation needs (03-wire-protocol § 12 W31).
TryResolve(SessionId, uint, out EntityId)
Resolves an entity reference a client sent — a netId on the wire — back to the entity it names.
public bool TryResolve(SessionId session, uint netId, out EntityId entity)
Parameters
sessionSessionIdThe session that sent the reference.
netIduintThe network identity, as the command carried it.
entityEntityIdThe entity.
Returns
Remarks
An unknown identity is not an error. A client may name an entity that has since left, or one it was never shown; the answer is "no", and the system decides what that means. Treating it as malformed input would let one stale reference close a connection.
The known-set half of this check is not built yet. 01-model § 7 requires that a client can only target what it was shown, which needs the per-session known-set that P1-13a builds. Today the identity must merely be live and bound, so a client that guesses a valid netId is not refused for it. That gap is stated here rather than hidden: the clause is added where the known-set arrives, and nothing above this line has to change for it.