Owner State (SELF)
What only the entity's owner may see, and the acknowledgement a predicting client reconciles against.
Status: ✅ Implemented · Visibility: Public · Level: 🔵 Core · Category: Subscriptions
🎯 What it solves
A player must see its own exact health, inventory count or objective marker — and nobody else should. A client that predicts its own movement must know which of its commands the server has applied. Both are per-session facts about one entity.
⚙️ How it works (in brief)
- Owner fields are declared in the projection's owner section (
.Owner(o => o.Field(…))or[Owner]), in their own change groups. - Control:
ctx.Subscriptions.Session(id).Control(entity)— staged, applied next tick. Many sessions may control one entity. SELFblock in each frame to a controlling session: the entity's netId, its owner groups that changed since the session's last frame, andu16 lastSeq— the highest command sequence number the tick drained for the session. Every owner group is sent after a change of control, a reset, or the session's firstSELF(SUB-11).- No controlled entity — a spectator, or a controlled entity that was destroyed — is sent as netId 0, once, so the client knows.
- Skips are unions: a session that missed frames receives every owner group that changed while it was away (no per-session value copy is kept — a pending mask per session, OR-ed by the projection).
💻 Usage
subs.Archetype<Player>(a => a
.Motion(Player.Bounds, m => m.Teleport(12))
// Everyone: an 8-bit health bar.
.Fraction(Player.Vitals, v => v.Health, v => v.MaxHealth, bits: 8, name: "hp", group: "vitals")
// The owner only: the exact health, and its mission state.
.Owner(o => o
.Field(Player.Vitals, v => v.Health, Codec.VarUInt, name: "health")
.Field(Player.State, s => s.MissionX, Codec.F32, name: "missionX", group: "mission")));
⚠️ Guarantees & limits
- An archetype with owner fields must have a position and is kept in replication state even when no profile observes it; a controlled
entity's
SELFis sent even when it lies outside the session's view. - A
Staticarchetype cannot declare owner fields (refused). - Owner fields reach only controlling sessions — a field both public and owner-only is refused by the attribute generator (TPH1108).
🧪 Tests
- SelfBlockTests — owner fields reach the owner only and converge across skips;
lastSeq; netId 0
🔗 Related
- Commands & acknowledgements —
lastSeqandACKS - Concept: Replication session