using ProjectM.Simulation; using Unity.Burst; using Unity.Collections; using Unity.Entities; using Unity.NetCode; namespace ProjectM.Server { /// /// Server-authoritative handler for RPCs: moves items from the /// sender's PERSONAL inventory into the shared base stockpile (the global /// the build/upgrade/automation economy spends from). Resolves the sender's /// player (SourceConnection -> NetworkId -> GhostOwner) via the AbilityUpgradeSystem owner-map idiom, /// then withdraws from the player's inventory and deposits into the ledger IN-PLACE (buffer mutation is not /// a structural change). ItemId == 0 ("deposit all") is handled BEFORE any per-item withdraw and /// never writes a 0-id row. Resolves the ledger via GetSingletonEntity<ResourceLedger>() then /// GetBuffer<StorageEntry>() — NEVER GetSingleton<StorageEntry> (the base /// container owns a second StorageEntry buffer). Plain server SimulationSystemGroup (not predicted, so the /// effect applies exactly once — no rollback double-apply). /// [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)] public partial struct InventoryDepositSystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate(); var builder = new EntityQueryBuilder(Allocator.Temp) .WithAll(); state.RequireForUpdate(state.GetEntityQuery(builder)); } [BurstCompile] public void OnUpdate(ref SystemState state) { var ledger = SystemAPI.GetBuffer(SystemAPI.GetSingletonEntity()); var playerByConn = new NativeHashMap(8, Allocator.Temp); foreach (var (owner, entity) in SystemAPI.Query>().WithAll().WithEntityAccess()) playerByConn[owner.ValueRO.NetworkId] = entity; var ecb = new EntityCommandBuffer(Allocator.Temp); foreach (var (request, receive, requestEntity) in SystemAPI.Query, RefRO>().WithEntityAccess()) { var conn = receive.ValueRO.SourceConnection; if (SystemAPI.HasComponent(conn) && playerByConn.TryGetValue(SystemAPI.GetComponent(conn).Value, out var player)) { var inv = SystemAPI.GetBuffer(player); var req = request.ValueRO; if (req.ItemId == 0) { // Deposit EVERYTHING: drain each non-empty stack into the ledger, then clear the bag. for (int i = 0; i < inv.Length; i++) { var slot = inv[i]; if (slot.ItemId != 0 && slot.Count > 0) StorageMath.Deposit(ledger, slot.ItemId, slot.Count); } inv.Clear(); } else { // Count <= 0 means "all of that item"; Withdraw clamps to what is available. int want = req.Count <= 0 ? int.MaxValue : req.Count; int moved = InventoryMath.Withdraw(inv, req.ItemId, want); if (moved > 0) StorageMath.Deposit(ledger, req.ItemId, moved); } } ecb.DestroyEntity(requestEntity); } ecb.Playback(state.EntityManager); playerByConn.Dispose(); } } }