GridBuilding 6.0 — Building & Manipulation Systems Multi‑User Audit
Scope
- Systems covered:
- Core C#:
GridBuilding.Core.Systems.Building.BuildingSystem(incomplete),GridBuilding.Core.Systems.Manipulation.ManipulationSystem(incomplete). - Godot wrappers:
GridBuilding.Core.Systems.Building.BuildingSystem(obsolete node),GridBuilding.Godot.Systems.Manipulation.ManipulationSystemNode.
- Core C#:
- Question: Are these systems architected as session/world‑level orchestrators for many Owners or as single‑Owner systems?
Current Status Snapshot
1. Core BuildingSystem (C#) — Core/_incomplete/Systems/Placement/Building.cs/BuildingSystem.cs
- Ownership model
- Uses
IBuildingState,IPlacementContextbut does not expose any Owner/User id. ExecutePlacement(placeable, buildType)only takes the placeable & build type; any Owner intent must be encoded in state/context.GetRuntimeIssues()only checksBuildingState,PlacementContext,PlacedParent— no concept of which Owner is executing.
- Uses
- Multi‑user readiness
- Implicitly behaves as a session/world‑level placement engine: it doesnt bind to a specific Owner, but it also doesnt model Owners explicitly.
- Multi‑user behavior depends entirely on:
- How
IBuildingStatemodels Owner/placer (single vs multi Owner). - How
IPlacementContextis scoped.
- How
- No explicit per‑Owner API surface (e.g.
ExecutePlacementForOwner(ownerId, ...)).
Assessment: Functionally session‑level, but Owner awareness is implicit and under‑specified. Needs explicit Owner context in the public API to be clearly multi‑user safe.
2. Core ManipulationSystem (C#) — Core/_incomplete/Systems/Placement/Manipulation/ManipulationSystem.cs
- Ownership model
- Uses
SystemsContext,ManipulationService2D,ManipulationSettings, plus workflow components. - Public APIs:
StartMove(ManipulationTarget target, ManipulationContext context)StartRotate(ManipulationTarget target, float rotationAngle, ManipulationContext context)StartFlip(ManipulationTarget target, FlipDirection flipDirection, ManipulationContext context)StartDemolish(ManipulationTarget target, ManipulationContext context)CompleteOperation(),CancelOperation()
- No explicit Owner/User id in these methods.
- Internal helper
GetActiveManipulation()currently returns the first active manipulation fromManipulationService2D.GetActiveManipulations().
- Uses
- Multi‑user readiness
_isActiveis a single bool on the system.- Enforced: only one active manipulation at a time per system, regardless of how many Owners exist.
- Retrieval via
GetActiveManipulation()does not filter by Owner. - Multi‑Owner scenarios would require:
- Either one system instance per Owner, or
- Owner keys in
ManipulationStateand queries filtered by Owner.
Assessment: Architecturally closer to a single‑session, single‑active‑manipulation system. It does not yet model multiple simultaneous Owners cleanly.
3. Godot BuildingSystem Node — Godot/Systems/Building/BuildingSystem.cs
- Status
- Marked
[Obsolete]and described as legacy GridBuilding system implementation. - Design notes explicitly say:
- Node should be treated as a session‑level orchestrator for all Owners in the session.
- At most one instance per session.
- Marked
- Owner API
- Base API:
PlaceBuilding(Vector2I gridPosition, string buildingType)andRemoveBuilding(Vector2I gridPosition)— no Owner parameter. - Owner‑aware overloads exist:
PlaceBuildingForOwner(string ownerId, Vector2I gridPosition, string buildingType)RemoveBuildingForOwner(string ownerId, Vector2I gridPosition)
- Both overloads are thin shims that simply delegate to the Owner‑agnostic methods:
PlaceBuildingForOwner(...) => PlaceBuilding(gridPosition, buildingType)RemoveBuildingForOwner(...) => RemoveBuilding(gridPosition)
- Base API:
- Multi‑user readiness
- Conceptually positioned as multi‑Owner session orchestrator, but the actual implementation still:
- Has no per‑Owner selection/permissions logic.
- Does not differentiate Owners in behavior.
- Uses Owner overloads purely as future hooks, not as active multi‑user features.
- Conceptually positioned as multi‑Owner session orchestrator, but the actual implementation still:
Assessment: Correct direction (session‑level orchestrator; Owner‑aware signatures) but still effectively single‑Owner behavior in practice.
4. Godot ManipulationSystemNode — Godot/Systems/ManipulationSystemNode.cs
- Status & design notes
- Explicit comment: treat as session‑level orchestrator in v6.x Owner‑based architecture.
- Exactly one instance per session; coordinates manipulations for all Owners.
- Owner API
- Core API:
StartManipulation(ManipulationMode mode, Vector2I origin) - Owner‑aware overload:
StartManipulationForOwner(string ownerId, ManipulationMode mode, Vector2I origin)
- Owner‑aware method currently delegates directly to Owner‑agnostic version:
StartManipulationForOwner(...) => StartManipulation(mode, origin)
- Core API:
- Multi‑user readiness
- Delegates all business logic to
IManipulationService. - This node itself:
- Does not yet pass Owner context into the Core service.
- Does not manage separate per‑Owner streams of manipulations.
- Delegates all business logic to
Assessment: Like the legacy BuildingSystem node, this is architected as a session‑level orchestrator with Owner‑aware stubs, but it currently treats all callers as the same Owner.
Summary — Are we multi‑user ready?
Building (Core + Godot):
- Conceptual model: session/world‑level orchestrator.
- Implementation reality: effectively single‑Owner; Owner overloads exist but do not alter behavior.
- Risk: without explicit Owner context, concurrent players can step on each other via shared state.
Manipulation (Core + Godot):
- Core ManipulationSystem: single
_isActiveflag +GetActiveManipulation()=> effectively single‑active‑operation, single‑Owner per system. - ManipulationSystemNode: session‑level Godot wrapper with Owner‑aware stubs that still act as Owner‑agnostic proxies.
- Core ManipulationSystem: single
Overall: The architecture points toward per‑session orchestrators that serve multiple Owners, but the concrete implementation is still at “single Owner at a time” semantics, with Owner context not yet wired through.
Evolution Plan — From Single‑Owner Semantics to Multi‑Owner Services
A. Core BuildingSystem (C#)
Introduce explicit Owner context
- Add Owner‑aware API surface:
BuildActionData ExecutePlacementForOwner(OwnerId ownerId, object? placeable, BuildType buildType, PlacementOptions? options = null).
- Ensure
IBuildingStateexposes an Owner identifier or an Owner context object.
- Add Owner‑aware API surface:
Clarify
IBuildingStatesemantics- Document and/or refactor
IBuildingStateso it can either:- Represent one Owners state, and you run one BuildingSystem per Owner, or
- Represent session‑level state with clear per‑Owner slices.
- Prefer: per‑Owner state objects consumed by a session‑level BuildingSystem.
- Document and/or refactor
Route through per‑Owner policies
- Before executing placement:
- Resolve Owner from
OwnerIdto anOwnerContext/GBOwnerContextequivalent. - Enforce permissions, quotas, cooldowns via policy services.
- Resolve Owner from
- Before executing placement:
Make single‑Owner method a shim
- Legacy
ExecutePlacement(placeable, buildType)should internally:- Use a default Owner id / system Owner.
- Be marked as obsolete once callers are migrated.
- Legacy
B. Core ManipulationSystem (C#)
Model Owner explicitly in ManipulationState / ManipulationContext
- Ensure
ManipulationContextincludesOwnerId(orOwnerContext). - Store this in
ManipulationStateviaSetContextData("ownerId", ownerId).
- Ensure
Remove global
_isActivebottleneck- Replace single
_isActiveflag with per‑Owner concurrency control:- Either allow one operation per Owner but many Owners concurrently, or
- Allow N operations per Owner with optional limits.
- Example: derive activity from
ManipulationService2D.GetActiveManipulations()filtered by Owner id.
- Replace single
Owner‑aware public APIs
- Extend signatures to carry Owner:
StartMove(OwnerId ownerId, ManipulationTarget target, ManipulationContext context).- Similar for Rotate/Flip/Demolish.
- Internally, use
ownerIdto:- Validate against Owner policies.
- Tag
ManipulationStatewith owner metadata for later filtering.
- Extend signatures to carry Owner:
Owner‑aware completion/cancelation
CompleteOperation/CancelOperationshould:- Either accept
OwnerIdand/ormanipulationId. - Use both to select the correct `Manipulafirst active manipulation.
- Either accept
Align ManipulationService2D with multi‑Owner semantics
- Ensure
IManipulationServiceAPIs support:- Query by Owner id.
- Enforce per‑Owner limits.
- Ensure
C. Godot BuildingSystem Node
Wire Owner overloads through to Core
- Update
PlaceBuildingForOwnerandRemoveBuildingForOwnerto:- Resolve
OwnerIdto Owner context (scene node, player session, AI controller). - Call into Owner‑aware Core placement APIs once introduced.
- Resolve
- Update
Define one node per session
- Document and enforce that scenes have at most one BuildingSystem node per session.
- Use DI / Service Registry to expose it as a session service.
Migrate call sites
- Update UI/controllers to use
PlaceBuildingForOwner/RemoveBuildingForOwnerrather than Owner‑agnostic versions.
- Update UI/controllers to use
Mark Owner‑agnostic methods as legacy
- Add
[Obsolete]attributes once callers are migrated.
- Add
D. Godot ManipulationSystemNode
Pass Owner context into the Core service
- Extend or adapt
IManipulationServiceAPIs to accept Owner (if not already):- e.g.
StartManipulation(OwnerId ownerId, ManipulationMode mode, Vector2I origin).
- e.g.
- Implement
StartManipulationForOwnerto:- Resolve Owner from
ownerId. - Call the Owner‑aware Core method.
- Resolve Owner from
- Extend or adapt
Owner‑aware signal payloads
- Extend
ManipulationDataor the emitted signals to include OwnerId so UI can differentiate players.
- Extend
Enforce one node per session
- Same pattern as BuildingSystem: treat this as session‑level, not Owner node.
Update integration tests
- Write tests that:
- Start manipulations for multiple Owners simultaneously.
- Assert they dont conflict and are reported independently.
- Write tests that:
Recommended Next Actions
Decide on canonical Owner representation
- Pick a single
OwnerId/OwnerContexttype shared across Building + Manipulation.
- Pick a single
Introduce Owner‑aware APIs in Core first
- Extend
BuildingSystemandManipulationSystemwith Owner parameters. - Keep legacy methods as shims for now.
- Extend
Wire Godot nodes to the new Core APIs
- Implement
*ForOwnermethods as real Owner‑aware calls.
- Implement
Add multi‑Owner integration tests
- At least one scenario for:
- Two Owners building concurrently.
- One Owner building while another manipulates.
- At least one scenario for:
Mark legacy Owner‑agnostic entrypoints as obsolete
- After call sites are migrated, start deprecating Owner‑agnostic APIs.