Files
Project-M/Assets/_Project/Scripts/Simulation/Items/InventorySlot.cs
T
kronic 599b9b4255 Inventory: per-player items backbone (DR-026 Phase 0)
Data-driven ItemDatabase catalog + per-player replicated InventorySlot ([GhostField] OwnerSendType.All, a StatModifier twin). Harvest reroutes to the firing player's personal inventory (optional ComponentLookup<GhostOwner> in ResourceHarvestSystem; remainder/un-owned -> ledger); the G-key InventoryDepositRequest RPC moves the bag into the shared ledger the build economy spends. Catalog asset (Aether/Ore/Biomass + Stone Pickaxe) wired into the Gameplay subscene; read-only HUD inventory panel. ushort ItemId subsumes ResourceId; byte Category/Tier baked for gear-tier progression. Session-only (no SaveData bump).

Play-validated host+client: catalog baked into both worlds, the re-baked player ghost carries InventorySlot with a clean handshake, a server write replicates to the client owner, the deposit RPC round-trips, and the HUD renders catalog names. See DR-026.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:43:31 -07:00

36 lines
2.1 KiB
C#

using Unity.Entities;
using Unity.NetCode;
namespace ProjectM.Simulation
{
/// <summary>
/// One (item, count) row in a player's PERSONAL inventory. The per-player DynamicBuffer of these is the
/// server-authoritative source of what that player is carrying. A structural twin of
/// <see cref="StatModifier"/>: a [GhostField] buffer with <see cref="SendToOwnerType.All"/> so the owning
/// (predicting) client receives its own inventory — without it the owner, being the owner, would not get
/// the owner-typed buffer at all and the HUD would read empty. BOTH fields carry [GhostField]; the
/// [GhostComponent] attribute alone does NOT auto-replicate fields (an un-annotated field ships as a
/// silent zero), so the annotations mirror <see cref="StorageEntry"/> field-for-field.
///
/// REPLICATION DISCIPLINE — the ONLY writers are server-only: <see cref="ProjectM.Server.ResourceHarvestSystem"/>
/// (harvest yield) and the deposit-to-base RPC handler, both in the plain server SimulationSystemGroup. So
/// there is no predicted-loop double-apply and the owner never mispredicts its inventory — it is a pure
/// server-authored snapshot. NEVER mutate this from a client predicted system (that would reintroduce a
/// double-apply / mispredict path). ItemId is the same opaque ushort id space as <see cref="StorageEntry"/>
/// and the <see cref="ItemDatabase"/> catalog.
///
/// NOTE: adding this [GhostField] buffer CHANGES the player ghost serialization hash — the player prefab /
/// subscene MUST be re-baked (consistently in both worlds) or the connect handshake desyncs.
/// </summary>
[GhostComponent(OwnerSendType = SendToOwnerType.All)]
[InternalBufferCapacity(24)]
public struct InventorySlot : IBufferElementData
{
/// <summary>Item carried in this slot (0 = empty/unused; aligns with InventoryMath's 0-id no-op).</summary>
[GhostField] public ushort ItemId;
/// <summary>Quantity in this slot (bounded by the item's StackMax when deposited via InventoryMath).</summary>
[GhostField] public int Count;
}
}