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>
69 lines
3.0 KiB
C#
69 lines
3.0 KiB
C#
using NUnit.Framework;
|
|
using ProjectM.Simulation;
|
|
|
|
namespace ProjectM.Tests
|
|
{
|
|
/// <summary>
|
|
/// Pure tests for the predicted-projectile SpawnId pack (<see cref="ProjectileSpawnId.Pack"/>). The
|
|
/// load-bearing property (review NP-1/RS-1/DB-3): two ability sockets firing the same-prefab projectile on
|
|
/// ONE tick with EQUAL per-socket fire counts must produce DISTINCT SpawnIds, so
|
|
/// <c>ProjectileClassificationSystem</c> binds each predicted entity to its own server ghost instead of
|
|
/// cross-adopting one and orphaning the other (the visible mis-prediction snap the old 32-bit key caused).
|
|
/// </summary>
|
|
public class ProjectileSpawnIdTests
|
|
{
|
|
[Test]
|
|
public void DifferentSocket_SameOwnerCountFork_ProducesDistinctIds()
|
|
{
|
|
// The exact collision scenario: owner 1, every socket's first shot (count 1), fork 0.
|
|
uint a = ProjectileSpawnId.Pack(netId: 1, socket: 0, fireCount: 1, fork: 0);
|
|
uint b = ProjectileSpawnId.Pack(netId: 1, socket: 1, fireCount: 1, fork: 0);
|
|
uint c = ProjectileSpawnId.Pack(netId: 1, socket: 2, fireCount: 1, fork: 0);
|
|
uint d = ProjectileSpawnId.Pack(netId: 1, socket: 3, fireCount: 1, fork: 0);
|
|
Assert.AreNotEqual(a, b);
|
|
Assert.AreNotEqual(a, c);
|
|
Assert.AreNotEqual(a, d);
|
|
Assert.AreNotEqual(b, c);
|
|
Assert.AreNotEqual(b, d);
|
|
Assert.AreNotEqual(c, d);
|
|
}
|
|
|
|
[Test]
|
|
public void DifferentFork_ProducesDistinctIds()
|
|
{
|
|
uint s0 = ProjectileSpawnId.Pack(2, 1, 5, 0);
|
|
uint s1 = ProjectileSpawnId.Pack(2, 1, 5, 1);
|
|
uint s2 = ProjectileSpawnId.Pack(2, 1, 5, 7);
|
|
Assert.AreNotEqual(s0, s1);
|
|
Assert.AreNotEqual(s0, s2);
|
|
Assert.AreNotEqual(s1, s2);
|
|
}
|
|
|
|
[Test]
|
|
public void DifferentOwner_ProducesDistinctIds()
|
|
{
|
|
Assert.AreNotEqual(ProjectileSpawnId.Pack(1, 0, 0, 0), ProjectileSpawnId.Pack(2, 0, 0, 0));
|
|
}
|
|
|
|
[Test]
|
|
public void FieldsDecodeToTheirBitRanges()
|
|
{
|
|
uint id = ProjectileSpawnId.Pack(netId: 3, socket: 2, fireCount: 7, fork: 5);
|
|
Assert.AreEqual(5u, id & 0xFu, "fork in bits 0-3");
|
|
Assert.AreEqual(7u, (id >> 4) & 0x0FFFu, "fireCount in bits 4-15");
|
|
Assert.AreEqual(2u, (id >> 16) & 0x3u, "socket in bits 16-17");
|
|
Assert.AreEqual(3u, (id >> 18) & 0x3FFFu, "owner in bits 18-31");
|
|
}
|
|
|
|
[Test]
|
|
public void FireCountWrapsAt4096_WithoutBleedingIntoSocket()
|
|
{
|
|
// count 4096 wraps to 0 within its 12-bit field and must not flip the socket bits.
|
|
uint wrapped = ProjectileSpawnId.Pack(1, 1, 4096, 0);
|
|
uint zero = ProjectileSpawnId.Pack(1, 1, 0, 0);
|
|
Assert.AreEqual(zero, wrapped, "count 4096 wraps to 0 within its 12 bits");
|
|
Assert.AreEqual(1u, (wrapped >> 16) & 0x3u, "socket bit intact across the wrap");
|
|
}
|
|
}
|
|
}
|