Hygiene B2: dead-code removal
Delete (unreferenced in any scene, per operator decision):
- TrainingDummy* chain (authoring/spawner/system/components/prefab) + UpgradePickup* chain (authoring/spawner/systems/components/prefab); remove TrainingDummyTag from the 3 live combat queries (AbilityFire/Melee/HealthApplyDamage now key on EnemyTag) and repoint HealthApplyDamageSystemTests to EnemyTag.
- Dead RPC RegionTransitSystem + RegionTransitRequest (no sender, ungated) + its test.
- Heartbeat + HeartbeatSystem (no WorldSystemFilter, ran no-op every tick) + its test.
- BuildSendSystem dead conveyor/pylon dev hooks (s_ConveyorDir, [/] rotation, Place{Pylon,Harvester,Conveyor} statics).
- Deprecated CyclePhase alias consts (Expedition/Defend/Build/*Ticks); repoint WaveSystemTests to Siege/Calm.
- Unread FeelConfig fields (HitFlashDurationMs, RumbleHeavy) + dangling HudUi doc-comments + stale crefs.
451/451 EditMode tests pass. CLAUDE.md HeartbeatSystemTests example ref to be repointed in B7.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ namespace ProjectM.Tests
|
||||
/// filter is ignored, so it still runs in this netcode-free world. The system plays its
|
||||
/// EntityCommandBuffer back to state.EntityManager immediately (Temp allocator) per the build
|
||||
/// contract, so no separate ECB system is required for destruction to take effect within a single
|
||||
/// group update. Mirrors PlayerMoveSystemTests and HeartbeatSystemTests.
|
||||
/// group update. Uses the plain-Entities world+tick pattern shared across the suite.
|
||||
/// </summary>
|
||||
public class HealthApplyDamageSystemTests
|
||||
{
|
||||
@@ -40,9 +40,9 @@ namespace ProjectM.Tests
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
// TrainingDummyTag included so this entity is a valid death candidate too, but with
|
||||
// EnemyTag included so this entity is a valid death candidate too, but with
|
||||
// 50 HP and 35 total damage it survives, so it must NOT be destroyed here.
|
||||
var entity = em.CreateEntity(typeof(Health), typeof(DamageEvent), typeof(TrainingDummyTag));
|
||||
var entity = em.CreateEntity(typeof(Health), typeof(DamageEvent), typeof(EnemyTag));
|
||||
em.SetComponentData(entity, new Health { Current = 50f, Max = 50f });
|
||||
|
||||
var dmg = em.GetBuffer<DamageEvent>(entity);
|
||||
@@ -61,13 +61,13 @@ namespace ProjectM.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Lethal_Damage_Clamps_Health_To_Zero_And_Destroys_TrainingDummy()
|
||||
public void Lethal_Damage_Clamps_Health_To_Zero_And_Destroys_Enemy()
|
||||
{
|
||||
var (world, group) = MakeWorld("HealthApplyDamageLethalDummyWorld");
|
||||
var (world, group) = MakeWorld("HealthApplyDamageLethalEnemyWorld");
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var entity = em.CreateEntity(typeof(Health), typeof(DamageEvent), typeof(TrainingDummyTag));
|
||||
var entity = em.CreateEntity(typeof(Health), typeof(DamageEvent), typeof(EnemyTag));
|
||||
em.SetComponentData(entity, new Health { Current = 50f, Max = 50f });
|
||||
|
||||
var dmg = em.GetBuffer<DamageEvent>(entity);
|
||||
@@ -75,22 +75,22 @@ namespace ProjectM.Tests
|
||||
|
||||
group.Update();
|
||||
|
||||
// The system clamps Current to a floor of 0 then destroys the dummy via an
|
||||
// The system clamps Current to a floor of 0 then destroys the enemy via an
|
||||
// immediately-played ECB, so the entity is gone within this single group update.
|
||||
Assert.IsFalse(em.Exists(entity),
|
||||
"A lethally-hit TrainingDummyTag entity must be destroyed by the system's ECB.");
|
||||
"A lethally-hit EnemyTag entity must be destroyed by the system's ECB.");
|
||||
|
||||
using var remaining = em.CreateEntityQuery(typeof(TrainingDummyTag));
|
||||
using var remaining = em.CreateEntityQuery(typeof(EnemyTag));
|
||||
Assert.AreEqual(0, remaining.CalculateEntityCount(),
|
||||
"No TrainingDummyTag entities should remain after a lethal hit.");
|
||||
"No EnemyTag entities should remain after a lethal hit.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Lethal_Damage_On_NonDummy_Clamps_To_Zero_Without_Destroying()
|
||||
public void Lethal_Damage_On_Untagged_Entity_Clamps_To_Zero_Without_Destroying()
|
||||
{
|
||||
// Player death is deferred in M2; only TrainingDummyTag entities are destroyed.
|
||||
// A non-dummy (no TrainingDummyTag) at zero HP must be clamped to 0 and kept alive.
|
||||
// Only death candidates (EnemyTag / Destructible) are destroyed. An untagged entity
|
||||
// (e.g. a player, whose death is deferred) at zero HP must be clamped to 0 and kept alive.
|
||||
var (world, group) = MakeWorld("HealthApplyDamageLethalPlayerWorld");
|
||||
using (world)
|
||||
{
|
||||
@@ -104,7 +104,7 @@ namespace ProjectM.Tests
|
||||
group.Update();
|
||||
|
||||
Assert.IsTrue(em.Exists(entity),
|
||||
"A non-dummy (player) entity must survive lethal damage in M2; death is deferred.");
|
||||
"An untagged (player) entity must survive lethal damage; death is deferred.");
|
||||
Assert.AreEqual(0f, em.GetComponentData<Health>(entity).Current, 1e-4f,
|
||||
"Health.Current must be clamped to 0 (never negative).");
|
||||
Assert.AreEqual(0, em.GetBuffer<DamageEvent>(entity).Length,
|
||||
@@ -120,7 +120,7 @@ namespace ProjectM.Tests
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var entity = em.CreateEntity(typeof(Health), typeof(DamageEvent), typeof(TrainingDummyTag));
|
||||
var entity = em.CreateEntity(typeof(Health), typeof(DamageEvent), typeof(EnemyTag));
|
||||
em.SetComponentData(entity, new Health { Current = 42f, Max = 60f });
|
||||
|
||||
group.Update();
|
||||
@@ -193,6 +193,5 @@ namespace ProjectM.Tests
|
||||
Assert.AreEqual(65f, em.GetComponentData<Health>(e).Current, 1e-4f, "No stats ceiling -> 100-35 = 65.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using Unity.Entities;
|
||||
using ProjectM.Simulation;
|
||||
|
||||
namespace ProjectM.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Setup smoke test: proves the DOTS toolchain compiles, source-generates, and ticks.
|
||||
/// Boots a bare ECS world, registers <see cref="HeartbeatSystem"/> in the
|
||||
/// SimulationSystemGroup, and asserts the component advances once per group update.
|
||||
/// (Netcode client/server world boot is covered separately by the §3 Play Mode check.)
|
||||
/// </summary>
|
||||
public class HeartbeatSystemTests
|
||||
{
|
||||
[Test]
|
||||
public void Heartbeat_Advances_Once_Per_SimulationGroup_Tick()
|
||||
{
|
||||
using var world = new World("HeartbeatTestWorld");
|
||||
var simulationGroup = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
|
||||
var heartbeatSystem = world.GetOrCreateSystem<HeartbeatSystem>();
|
||||
simulationGroup.AddSystemToUpdateList(heartbeatSystem);
|
||||
simulationGroup.SortSystems();
|
||||
|
||||
var entityManager = world.EntityManager;
|
||||
var entity = entityManager.CreateEntity(typeof(Heartbeat));
|
||||
|
||||
Assert.AreEqual(0, entityManager.GetComponentData<Heartbeat>(entity).Tick,
|
||||
"Heartbeat should start at 0.");
|
||||
|
||||
const int ticks = 8;
|
||||
for (int i = 0; i < ticks; i++)
|
||||
{
|
||||
simulationGroup.Update();
|
||||
}
|
||||
|
||||
Assert.AreEqual(ticks, entityManager.GetComponentData<Heartbeat>(entity).Tick,
|
||||
"Heartbeat.Tick should advance exactly once per SimulationSystemGroup update.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74e3326af426947cfaff903aa94ed9f0
|
||||
@@ -9,7 +9,7 @@ namespace ProjectM.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Plain-Entities EditMode tests for the server-only <see cref="InventoryDepositSystem"/> — the RPC that
|
||||
/// moves a player's PERSONAL inventory into the shared ledger. Mirrors RegionTransitSystemTests' seeding:
|
||||
/// moves a player's PERSONAL inventory into the shared ledger. Mirrors the RPC-receive tests' seeding:
|
||||
/// a ResourceLedger singleton, a mock connection (NetworkId), a player (GhostOwner + InventorySlot +
|
||||
/// PlayerTag), and an InventoryDepositRequest + ReceiveRpcCommandRequest. Pins: a specific-item deposit
|
||||
/// moves the clamped amount; ItemId 0 deposits everything and empties the bag; an unresolvable connection
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using ProjectM.Server;
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Core;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.NetCode;
|
||||
using Unity.Transforms;
|
||||
|
||||
namespace ProjectM.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Plain-Entities EditMode tests for the server-only <see cref="RegionTransitSystem"/> — the RPC region-transit
|
||||
/// handler. A bare world is seeded with a BaseAnchor, a mock connection entity (NetworkId), a player
|
||||
/// (GhostOwner + RegionTag + LocalTransform + PlayerTag) and a RegionTransitRequest + ReceiveRpcCommandRequest
|
||||
/// whose SourceConnection points at the connection. Pins: a request from a resolvable connection flips the
|
||||
/// player's region + teleports it to the region origin; an unresolvable connection transits nobody; the request
|
||||
/// is consumed either way.
|
||||
/// </summary>
|
||||
public class RegionTransitSystemTests
|
||||
{
|
||||
static (World world, SimulationSystemGroup group) MakeWorld(string name)
|
||||
{
|
||||
var world = new World(name);
|
||||
var group = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
|
||||
group.AddSystemToUpdateList(world.GetOrCreateSystem<RegionTransitSystem>());
|
||||
group.SortSystems();
|
||||
world.SetTime(new TimeData(elapsedTime: 0f, deltaTime: 1f / 60f));
|
||||
var em = world.EntityManager;
|
||||
var anchor = em.CreateEntity(typeof(BaseAnchor));
|
||||
em.SetComponentData(anchor, new BaseAnchor
|
||||
{
|
||||
AnchorPos = new float3(5, 0, 5),
|
||||
GridOrigin = new float3(0, 0, 0),
|
||||
CellSize = 2f,
|
||||
GridDims = new int2(5, 5),
|
||||
});
|
||||
return (world, group);
|
||||
}
|
||||
|
||||
static Entity MakeConnection(EntityManager em, int networkId)
|
||||
{
|
||||
var e = em.CreateEntity();
|
||||
em.AddComponentData(e, new NetworkId { Value = networkId });
|
||||
return e;
|
||||
}
|
||||
|
||||
static Entity MakePlayer(EntityManager em, int networkId, byte region, float3 pos)
|
||||
{
|
||||
var e = em.CreateEntity();
|
||||
em.AddComponentData(e, new GhostOwner { NetworkId = networkId });
|
||||
em.AddComponentData(e, new RegionTag { Region = region });
|
||||
em.AddComponentData(e, LocalTransform.FromPosition(pos));
|
||||
em.AddComponent<PlayerTag>(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
static void MakeTransitRequest(EntityManager em, byte targetRegion, Entity sourceConnection)
|
||||
{
|
||||
var e = em.CreateEntity();
|
||||
em.AddComponentData(e, new RegionTransitRequest { TargetRegion = targetRegion });
|
||||
em.AddComponentData(e, new ReceiveRpcCommandRequest { SourceConnection = sourceConnection });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Request_From_Known_Connection_Transits_Its_Player()
|
||||
{
|
||||
var (world, group) = MakeWorld("RegionTransitOk");
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var conn = MakeConnection(em, networkId: 1);
|
||||
var player = MakePlayer(em, networkId: 1, region: RegionId.Base, pos: new float3(5, 1, 5));
|
||||
MakeTransitRequest(em, RegionId.Expedition, conn);
|
||||
|
||||
group.Update();
|
||||
|
||||
Assert.AreEqual(RegionId.Expedition, em.GetComponentData<RegionTag>(player).Region,
|
||||
"The sender's player flips to the requested region.");
|
||||
Assert.AreEqual(1005f, em.GetComponentData<LocalTransform>(player).Position.x, 1e-2f,
|
||||
"The player teleports to the expedition region origin (base center + 1000 on X).");
|
||||
using var reqQ = em.CreateEntityQuery(typeof(RegionTransitRequest));
|
||||
Assert.AreEqual(0, reqQ.CalculateEntityCount(), "The handled request is destroyed.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Request_From_Unresolvable_Connection_Transits_Nobody()
|
||||
{
|
||||
var (world, group) = MakeWorld("RegionTransitUnknown");
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
var player = MakePlayer(em, networkId: 1, region: RegionId.Base, pos: new float3(5, 1, 5));
|
||||
MakeTransitRequest(em, RegionId.Expedition, Entity.Null); // no NetworkId on Entity.Null
|
||||
|
||||
group.Update();
|
||||
|
||||
Assert.AreEqual(RegionId.Base, em.GetComponentData<RegionTag>(player).Region,
|
||||
"A request whose connection can't be resolved transits nobody.");
|
||||
using var reqQ = em.CreateEntityQuery(typeof(RegionTransitRequest));
|
||||
Assert.AreEqual(0, reqQ.CalculateEntityCount(), "The request is still consumed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 727cdc6da6f5b8842ba5b311702e5224
|
||||
@@ -1,73 +0,0 @@
|
||||
using NUnit.Framework;
|
||||
using ProjectM.Server;
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
|
||||
namespace ProjectM.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Plain-Entities test for <see cref="UpgradePickupSystem"/> (server grant). A player overlapping a
|
||||
/// pickup gets the pickup's modifier appended to its StatModifier buffer and the pickup is destroyed;
|
||||
/// a player out of range leaves the buffer empty and the pickup alive. The system's ECB plays back
|
||||
/// immediately (server pattern), so no separate ECB system is needed.
|
||||
/// </summary>
|
||||
public class UpgradePickupSystemTests
|
||||
{
|
||||
static (World world, Entity player, Entity pickup) MakeWorld(float3 playerPos, float3 pickupPos, float radius)
|
||||
{
|
||||
var world = new World("UpgradePickupTestWorld");
|
||||
var group = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
|
||||
group.AddSystemToUpdateList(world.GetOrCreateSystem<UpgradePickupSystem>());
|
||||
group.SortSystems();
|
||||
|
||||
var em = world.EntityManager;
|
||||
|
||||
var player = em.CreateEntity(typeof(PlayerTag), typeof(StatModifier), typeof(LocalTransform));
|
||||
em.SetComponentData(player, LocalTransform.FromPosition(playerPos));
|
||||
|
||||
var pickup = em.CreateEntity(typeof(UpgradePickup), typeof(HitRadius), typeof(LocalTransform));
|
||||
em.SetComponentData(pickup, LocalTransform.FromPosition(pickupPos));
|
||||
em.SetComponentData(pickup, new HitRadius { Value = radius });
|
||||
em.SetComponentData(pickup, new UpgradePickup
|
||||
{
|
||||
Target = (byte)StatTarget.Damage, Op = (byte)ModOp.Flat, Value = 25f, SourceId = 7u
|
||||
});
|
||||
|
||||
return (world, player, pickup);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Overlap_Grants_Modifier_And_Destroys_Pickup()
|
||||
{
|
||||
var (world, player, pickup) = MakeWorld(float3.zero, new float3(0.5f, 0f, 0f), 1f);
|
||||
try
|
||||
{
|
||||
world.GetExistingSystemManaged<SimulationSystemGroup>().Update();
|
||||
var buf = world.EntityManager.GetBuffer<StatModifier>(player);
|
||||
Assert.AreEqual(1, buf.Length);
|
||||
Assert.AreEqual((byte)StatTarget.Damage, buf[0].Target);
|
||||
Assert.AreEqual((byte)ModOp.Flat, buf[0].Op);
|
||||
Assert.AreEqual(25f, buf[0].Value, 1e-4f);
|
||||
Assert.AreEqual(7u, buf[0].SourceId);
|
||||
Assert.IsFalse(world.EntityManager.Exists(pickup), "Pickup should be destroyed after grant.");
|
||||
}
|
||||
finally { world.Dispose(); }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Out_Of_Range_Leaves_Buffer_Empty_And_Pickup_Alive()
|
||||
{
|
||||
var (world, player, pickup) = MakeWorld(float3.zero, new float3(10f, 0f, 0f), 1f);
|
||||
try
|
||||
{
|
||||
world.GetExistingSystemManaged<SimulationSystemGroup>().Update();
|
||||
var buf = world.EntityManager.GetBuffer<StatModifier>(player);
|
||||
Assert.AreEqual(0, buf.Length);
|
||||
Assert.IsTrue(world.EntityManager.Exists(pickup), "Out-of-range pickup should remain.");
|
||||
}
|
||||
finally { world.Dispose(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34f9fd7580c614586b6a5c68a0e6393c
|
||||
@@ -68,7 +68,7 @@ namespace ProjectM.Tests
|
||||
[Test]
|
||||
public void Due_Lull_Starts_Wave_With_Escalating_Count()
|
||||
{
|
||||
var (world, group) = MakeWorld("WaveLullStart", serverTick: 100, cyclePhase: CyclePhase.Defend);
|
||||
var (world, group) = MakeWorld("WaveLullStart", serverTick: 100, cyclePhase: CyclePhase.Siege);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
@@ -88,7 +88,7 @@ namespace ProjectM.Tests
|
||||
[Test]
|
||||
public void Spawning_Emits_One_Husk_And_Decrements_Remaining()
|
||||
{
|
||||
var (world, group) = MakeWorld("WaveSpawnOne", serverTick: 100, cyclePhase: CyclePhase.Defend);
|
||||
var (world, group) = MakeWorld("WaveSpawnOne", serverTick: 100, cyclePhase: CyclePhase.Siege);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
@@ -107,7 +107,7 @@ namespace ProjectM.Tests
|
||||
[Test]
|
||||
public void Director_Is_Gated_Off_Outside_Defend()
|
||||
{
|
||||
var (world, group) = MakeWorld("WaveGated", serverTick: 100, cyclePhase: CyclePhase.Expedition);
|
||||
var (world, group) = MakeWorld("WaveGated", serverTick: 100, cyclePhase: CyclePhase.Calm);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
@@ -125,7 +125,7 @@ namespace ProjectM.Tests
|
||||
[Test]
|
||||
public void Fully_Spawned_Cleared_Wave_Returns_To_Lull()
|
||||
{
|
||||
var (world, group) = MakeWorld("WaveCleared", serverTick: 100, cyclePhase: CyclePhase.Defend);
|
||||
var (world, group) = MakeWorld("WaveCleared", serverTick: 100, cyclePhase: CyclePhase.Siege);
|
||||
using (world)
|
||||
{
|
||||
var em = world.EntityManager;
|
||||
|
||||
Reference in New Issue
Block a user