namespace ProjectM.Simulation { /// /// Pure, Burst-safe packing of the predicted-projectile classification key /// (). Layout: owner(14) | socket(2) | fireCount(12) | fork(4) = 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 /// ProjectileClassificationSystem 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. /// public static class ProjectileSpawnId { public const int OwnerBits = 14; public const int SocketBits = 2; public const int FireCountBits = 12; public const int ForkBits = 4; /// Pack the classification key. Inputs are masked to their bit widths (wrap, not overflow). 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 } } }