From fbda4c74fb0c1fc911be37acac2826eee16a20e7 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 30 Sep 2026 12:32:59 +0200 Subject: [PATCH] Scenarios Cut 1: Zone.Admit is the one admission into a zone Zone.Admit(entity, piloted) adds the entity to Entities, activates it and, when piloted, gives it the zone's Minion. It is the only writer of Entities.Add and the only creator of agents (docs/scenarios-cut.md, 2.2). The constructor's pack loop admits every packed ship but the player's as piloted and keeps its own origin-scatter rule, which is generation placement, not admission. Entity.TryUndock admits the undocked ship unpiloted. Co-Authored-By: Claude Opus 5.5 --- Assets/Scripts/ServerShared/Entity.cs | 3 +-- Assets/Scripts/ServerShared/Zone.cs | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Assets/Scripts/ServerShared/Entity.cs b/Assets/Scripts/ServerShared/Entity.cs index 6ad751e01..c2d1dbdca 100644 --- a/Assets/Scripts/ServerShared/Entity.cs +++ b/Assets/Scripts/ServerShared/Entity.cs @@ -1003,8 +1003,7 @@ public abstract class Entity bay.DockedShip = null; ship.RemoveParent(); - Zone.Entities.Add(ship); - ship.Activate(); + Zone.Admit(ship, piloted: false); return true; } diff --git a/Assets/Scripts/ServerShared/Zone.cs b/Assets/Scripts/ServerShared/Zone.cs index 9b34e6687..ec8aa304c 100644 --- a/Assets/Scripts/ServerShared/Zone.cs +++ b/Assets/Scripts/ServerShared/Zone.cs @@ -88,7 +88,7 @@ public class Zone // Cut 5, 5.6 (docs/fire-control-cut.md, Soul finding 7; operator ruling Q3): death removes the ship, // in the simulation, not only in Unity's own loot-drop subscription. One subscription point covers - // every join, whichever call site adds the entity (deserialization below, a jump, a spawned turret) -- + // every join, whichever caller admits the entity (deserialization below, a jump, a spawned turret) -- // ObserveAdd fires for all of them. Forbidden writer: no presentation may remove an entity from Zone. Entities.ObserveAdd().Subscribe(add => add.Value.Death.Subscribe(_ => { Entities.Remove(add.Value); add.Value.Deactivate(); })); @@ -121,19 +121,27 @@ public class Zone foreach (var entityPack in pack.Entities) { var entity = EntitySerializer.Unpack(_itemManager, this, entityPack); - Entities.Add(entity); - entity.Activate(); - if (entity is Ship {IsPlayerShip: false} ship) - { - Agents.Add(CreateAgent(ship)); - if (lengthsq(ship.Position) < 1) - ship.Position = _itemManager.Random.NextFloat3(float3(-pack.Radius * .5f), float3(pack.Radius * .5f)); - } + var ship = entity as Ship; + Admit(entity, piloted: ship is {IsPlayerShip: false}); + // Generation placement, not admission: a ship packed at the origin is scattered across the zone. + if (ship is {IsPlayerShip: false} && lengthsq(ship.Position) < 1) + ship.Position = _itemManager.Random.NextFloat3(float3(-pack.Radius * .5f), float3(pack.Radius * .5f)); } // TODO: Associate planets with stored entities for planetary colonies } + // The one admission into this zone (docs/scenarios-cut.md, 2.2): the only writer of Entities.Add and the only + // creator of agents. Construction, new-run staging (RunStart), warp arrival, undock and the console's spawned + // turret all join through here. A piloted entity gets the zone's Minion; an unpiloted one gets no agent at all. + // Only a ship can be piloted. The Death subscription above covers every join, whichever caller admits it. + public void Admit(Entity entity, bool piloted) + { + Entities.Add(entity); + entity.Activate(); + if (piloted) Agents.Add(CreateAgent((Ship) entity)); + } + private Agent CreateAgent(Ship ship) { var agent = new Minion(ship);