Grid Placement

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.
  • 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, IPlacementContext but 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 checks BuildingState, PlacementContext, PlacedParent — no concept of which Owner is executing.
  • 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 IBuildingState models Owner/placer (single vs multi Owner).
      • How IPlacementContext is scoped.
    • 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 from ManipulationService2D.GetActiveManipulations().
  • Multi‑user readiness
    • _isActive is 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 ManipulationState and 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.
  • Owner API
    • Base API: PlaceBuilding(Vector2I gridPosition, string buildingType) and RemoveBuilding(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)
  • 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.

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)
  • 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.

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 _isActive flag + 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.

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#)

  1. Introduce explicit Owner context

    • Add Owner‑aware API surface:
      • BuildActionData ExecutePlacementForOwner(OwnerId ownerId, object? placeable, BuildType buildType, PlacementOptions? options = null).
    • Ensure IBuildingState exposes an Owner identifier or an Owner context object.
  2. Clarify IBuildingState semantics

    • Document and/or refactor IBuildingState so 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.
  3. Route through per‑Owner policies

    • Before executing placement:
      • Resolve Owner from OwnerId to an OwnerContext / GBOwnerContext equivalent.
      • Enforce permissions, quotas, cooldowns via policy services.
  4. 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.

B. Core ManipulationSystem (C#)

  1. Model Owner explicitly in ManipulationState / ManipulationContext

    • Ensure ManipulationContext includes OwnerId (or OwnerContext).
    • Store this in ManipulationState via SetContextData("ownerId", ownerId).
  2. Remove global _isActive bottleneck

    • Replace single _isActive flag 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.
  3. Owner‑aware public APIs

    • Extend signatures to carry Owner:
      • StartMove(OwnerId ownerId, ManipulationTarget target, ManipulationContext context).
      • Similar for Rotate/Flip/Demolish.
    • Internally, use ownerId to:
      • Validate against Owner policies.
      • Tag ManipulationState with owner metadata for later filtering.
  4. Owner‑aware completion/cancelation

    • CompleteOperation / CancelOperation should:
      • Either accept OwnerId and/or manipulationId.
      • Use both to select the correct `Manipulafirst active manipulation.
  5. Align ManipulationService2D with multi‑Owner semantics

    • Ensure IManipulationService APIs support:
      • Query by Owner id.
      • Enforce per‑Owner limits.

C. Godot BuildingSystem Node

  1. Wire Owner overloads through to Core

    • Update PlaceBuildingForOwner and RemoveBuildingForOwner to:
      • Resolve OwnerId to Owner context (scene node, player session, AI controller).
      • Call into Owner‑aware Core placement APIs once introduced.
  2. 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.
  3. Migrate call sites

    • Update UI/controllers to use PlaceBuildingForOwner / RemoveBuildingForOwner rather than Owner‑agnostic versions.
  4. Mark Owner‑agnostic methods as legacy

    • Add [Obsolete] attributes once callers are migrated.

D. Godot ManipulationSystemNode

  1. Pass Owner context into the Core service

    • Extend or adapt IManipulationService APIs to accept Owner (if not already):
      • e.g. StartManipulation(OwnerId ownerId, ManipulationMode mode, Vector2I origin).
    • Implement StartManipulationForOwner to:
      • Resolve Owner from ownerId.
      • Call the Owner‑aware Core method.
  2. Owner‑aware signal payloads

    • Extend ManipulationData or the emitted signals to include OwnerId so UI can differentiate players.
  3. Enforce one node per session

    • Same pattern as BuildingSystem: treat this as session‑level, not Owner node.
  4. Update integration tests

    • Write tests that:
      • Start manipulations for multiple Owners simultaneously.
      • Assert they dont conflict and are reported independently.

  1. Decide on canonical Owner representation

    • Pick a single OwnerId/OwnerContext type shared across Building + Manipulation.
  2. Introduce Owner‑aware APIs in Core first

    • Extend BuildingSystem and ManipulationSystem with Owner parameters.
    • Keep legacy methods as shims for now.
  3. Wire Godot nodes to the new Core APIs

    • Implement *ForOwner methods as real Owner‑aware calls.
  4. Add multi‑Owner integration tests

    • At least one scenario for:
      • Two Owners building concurrently.
      • One Owner building while another manipulates.
  5. Mark legacy Owner‑agnostic entrypoints as obsolete

    • After call sites are migrated, start deprecating Owner‑agnostic APIs.