12f86c86de
The netcode core of the 4-socket kit (Phase1_Combat_Gym_Build_Spec §1,§2,§5; review NP-1/ RS-1/DB-1/DB-3): - PlayerInput: +4 Socket0..3 InputEvents + Burst-safe GetSocket(i); legacy Fire kept vestigial. - PlayerInputGatherSystem: gather sockets from keyboard 1..4 (+gamepad RT/LB/RB); socket 0 also fires on the legacy primary (right-click / pad LT) as a bridge. - AbilityFireSystem: query dropped to 4 type args (PlayerInput/PlayerFacing/LocalTransform/ GhostOwner) + BufferLookup<AbilitySocket>/ComponentLookup<SocketCooldown>/BufferLookup< EffectiveSocketStats> (the 7-arg-cap fix); loops 4 sockets; per-socket cooldown + per-socket effective stats; Cone + Projectile dispatch per socket (predict-spawn Projectile-only). - ProjectileSpawnId.Pack: pure Burst-safe key owner14|socket2|fireCount12|fork4 so same-tick multi-socket projectiles never collide; AbilityFireSystem uses it. L1 clean; L2 494/494 (+5 ProjectileSpawnId tests: distinct-per-socket, fork, owner, bit-ranges, count-wrap). L3 two-player + rollback is the group-A gate (after step 2.5). Note: the client feel layer (muzzle/anim) still reads the legacy cooldown until step 2.5; sockets bake empty until content (step 4), so nothing fires in-game yet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
30 lines
1.8 KiB
C#
30 lines
1.8 KiB
C#
namespace ProjectM.Simulation
|
|
{
|
|
/// <summary>
|
|
/// Pure, Burst-safe packing of the predicted-projectile classification key
|
|
/// (<see cref="Projectile.SpawnId"/>). Layout: <c>owner(14) | socket(2) | fireCount(12) | fork(4)</c> = 32
|
|
/// bits exact. Reserving 2 socket bits is the review's NP-1/RS-1/DB-3 fix: under the LANTERN 4-socket kit,
|
|
/// two sockets firing the same-prefab projectile on ONE tick carry independent per-socket fire counts that
|
|
/// are near-guaranteed equal early (both 0->1); without a socket discriminator their SpawnIds collide and
|
|
/// <c>ProjectileClassificationSystem</c> cross-adopts one predicted entity and orphans the other. Fork keeps
|
|
/// all 4 low bits (Phase-4 Fork mutation fans up to 8 shots). Extracted so the collision-free property is
|
|
/// unit-tested independently of the netcode prediction context.
|
|
/// </summary>
|
|
public static class ProjectileSpawnId
|
|
{
|
|
public const int OwnerBits = 14;
|
|
public const int SocketBits = 2;
|
|
public const int FireCountBits = 12;
|
|
public const int ForkBits = 4;
|
|
|
|
/// <summary>Pack the classification key. Inputs are masked to their bit widths (wrap, not overflow).</summary>
|
|
public static uint Pack(int netId, int socket, uint fireCount, int fork)
|
|
{
|
|
return ((((uint)netId) & 0x3FFFu) << (SocketBits + FireCountBits + ForkBits)) // owner -> bits 18..31
|
|
| (((uint)socket & 0x3u) << (FireCountBits + ForkBits)) // socket -> bits 16..17
|
|
| ((fireCount & 0x0FFFu) << ForkBits) // fireCount -> bits 4..15
|
|
| ((uint)fork & 0xFu); // fork -> bits 0..3
|
|
}
|
|
}
|
|
}
|