From 67095024b909e3346a419c8e9f4526a64af29355 Mon Sep 17 00:00:00 2001 From: Luis Gonzalez Date: Tue, 14 Jul 2026 10:35:58 -0700 Subject: [PATCH] LANTERN P1 step 1: 4-socket kit data model (player-ghost re-bake) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additive socket data model per Phase1_Combat_Gym_Build_Spec §3 (no behaviour; legacy AbilityRef/AbilityCooldown left intact for the step-2 migration): - AbilitySocket: cold per-socket loadout buffer (EquipmentSlot-modelled, [GhostField] byte SparkId, SendToOwnerType.All, 4 rows baked empty). - SocketCooldown: hot owner-predicted per-socket cooldown (4x [GhostField] uint + Burst-safe switch indexer; SendToOwnerType.All for remote ability-feel parity with AbilityCooldown). - FrameId: replicated [GhostField] byte frame/class signal (SendToOwner; the class source the socket model needs since no single ability id maps 1:1 to a class). Baked onto the player ghost in PlayerAuthoring. L1 clean compile + domain reload. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Authoring/Player/PlayerAuthoring.cs | 10 +++ .../Simulation/Combat/SocketComponents.cs | 85 +++++++++++++++++++ .../Combat/SocketComponents.cs.meta | 2 + 3 files changed, 97 insertions(+) create mode 100644 Assets/_Project/Scripts/Simulation/Combat/SocketComponents.cs create mode 100644 Assets/_Project/Scripts/Simulation/Combat/SocketComponents.cs.meta diff --git a/Assets/_Project/Scripts/Authoring/Player/PlayerAuthoring.cs b/Assets/_Project/Scripts/Authoring/Player/PlayerAuthoring.cs index 845548071..2132a076e 100644 --- a/Assets/_Project/Scripts/Authoring/Player/PlayerAuthoring.cs +++ b/Assets/_Project/Scripts/Authoring/Player/PlayerAuthoring.cs @@ -109,6 +109,16 @@ namespace ProjectM.Authoring // the Returning edge) + the server-only Blade-Dash per-dash dedup accumulator (non-replicated). AddComponent(entity); AddComponent(entity); + // LANTERN Phase 1 (Step 1): 4-socket kit data model - parallels AbilityRef/AbilityCooldown + // (both kept until the AbilityFireSystem migration in Step 2). AbilitySocket = cold per-socket + // loadout (EquipmentSlot-modelled, 4 empty rows); SocketCooldown = hot owner-predicted per-socket + // cooldown; FrameId = replicated frame/class signal (baked 0, written server-side at frame select). + var sockets = AddBuffer(entity); + for (int sk = 0; sk < SocketId.Count; sk++) + sockets.Add(new AbilitySocket { SparkId = 0 }); + AddComponent(entity); + AddComponent(entity); + } } } diff --git a/Assets/_Project/Scripts/Simulation/Combat/SocketComponents.cs b/Assets/_Project/Scripts/Simulation/Combat/SocketComponents.cs new file mode 100644 index 000000000..14ec8e434 --- /dev/null +++ b/Assets/_Project/Scripts/Simulation/Combat/SocketComponents.cs @@ -0,0 +1,85 @@ +using Unity.Entities; +using Unity.NetCode; + +namespace ProjectM.Simulation +{ + /// Socket count / index constants for the LANTERN 4-socket kit. + public static class SocketId + { + public const int Count = 4; + } + + /// + /// Per-player ability socket loadout — one replicated row per socket (buffer index = socket 0..3), + /// each holding the (Spark) occupying that socket (0 = empty). The LANTERN + /// 4-socket-kit analog of the single ; modelled on : + /// COLD (mutated only when socketing in the hub, server-sole-writer — NOT predicted). Read by + /// AbilityFireSystem via a BufferLookup, never as a SystemAPI.Query type arg (the 7-arg cap). + /// + [InternalBufferCapacity(4)] + [GhostComponent(OwnerSendType = SendToOwnerType.All)] + public struct AbilitySocket : IBufferElementData + { + /// The (Spark) occupying this socket; 0 = empty. + [GhostField] public byte SparkId; + } + + /// + /// HOT, owner-predicted per-socket ability cooldown for the 4-socket kit — the LANTERN analog of the + /// single scalar (deliberately NOT the cold + /// buffer: a scalar-shaped, index-addressable cooldown whose per-tick rollback restore is proven, unlike + /// a per-tick-mutating [GhostField] buffer). Four raw next-fire ticks, one per socket, each a + /// so the gate survives the frame→tick→rollback boundary and converges without + /// double-firing. 0 = ready. Route stored ticks through and compare + /// by wrapping into a + (raw subtraction + /// is unsafe across wraparound). Explicit SendToOwnerType.All (parity with today's implicit-send-all + /// , so remote clients still drive teammates' ability feel). + /// + [GhostComponent(OwnerSendType = SendToOwnerType.All)] + public struct SocketCooldown : IComponentData + { + [GhostField] public uint Next0; + [GhostField] public uint Next1; + [GhostField] public uint Next2; + [GhostField] public uint Next3; + + /// Next-fire tick for socket (0..3). Burst-safe (switch, not a field index). + public uint Get(int i) + { + switch (i) + { + case 0: return Next0; + case 1: return Next1; + case 2: return Next2; + default: return Next3; + } + } + + /// Set the next-fire tick for socket (0..3). + public void Set(int i, uint v) + { + switch (i) + { + case 0: Next0 = v; break; + case 1: Next1 = v; break; + case 2: Next2 = v; break; + default: Next3 = v; break; + } + } + } + + /// + /// Replicated per-player frame/class signal (a LANTERN suit-frame IS the class). Baked on the player + /// ghost, written server-side when the frame/class is chosen. Exists because, under the 4-socket kit, no + /// single ability id maps 1:1 to a class the way the old single did — the HUD/ + /// feel systems read this instead of ClassTraits.ClassForAbility(AbilityRef.Id). SendToOwner: + /// only the owning client's HUD needs its own frame. (Distinct from the server-only PlayerClass, + /// which is runtime-added and cannot carry a GhostField.) + /// + [GhostComponent(OwnerSendType = SendToOwnerType.SendToOwner)] + public struct FrameId : IComponentData + { + /// The frame/class id (see the CharacterId / ClassId convention). + [GhostField] public byte Value; + } +} diff --git a/Assets/_Project/Scripts/Simulation/Combat/SocketComponents.cs.meta b/Assets/_Project/Scripts/Simulation/Combat/SocketComponents.cs.meta new file mode 100644 index 000000000..1eea0cacf --- /dev/null +++ b/Assets/_Project/Scripts/Simulation/Combat/SocketComponents.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7e1ff5b506cb3c84c9913c58d64c1975 \ No newline at end of file