e27a495530
- 10 EditMode files: every bare trailing world.Dispose() now inside using(world){} (single-world) or a [TearDown]+List<World> (the multi-world reject-matrix tests in BoonApplyTests/RouteSelectSystemTests) — an assertion failure can no longer leak the World and mask the true first failure. No test logic changed.
- HudSystem: collapse the blank-line run before the class close; trim the extracted route-map clause from the READY-panel comment.
466/466 EditMode tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
107 lines
4.8 KiB
C#
107 lines
4.8 KiB
C#
using NUnit.Framework;
|
|
using ProjectM.Server;
|
|
using ProjectM.Simulation;
|
|
using Unity.Collections;
|
|
using Unity.Core;
|
|
using Unity.Entities;
|
|
using Unity.NetCode;
|
|
|
|
namespace ProjectM.Tests
|
|
{
|
|
/// <summary>
|
|
/// Pins the boon pool math (<see cref="BoonMath.PickBoons"/>: deterministic, 3 distinct, class-filtered,
|
|
/// weight-0 excluded) and <see cref="BoonOfferSystem"/> (one deal per RoomEpoch; expedition players only;
|
|
/// owner-seeded per player so co-op offers differ).
|
|
/// </summary>
|
|
public class BoonOfferTests
|
|
{
|
|
[Test]
|
|
public void PickBoons_Deterministic_Distinct_ClassFiltered()
|
|
{
|
|
var blob = BoonCatalogData.BuildDefault(Allocator.Temp);
|
|
ref var pool = ref blob.Value;
|
|
|
|
for (byte classId = 0; classId <= 1; classId++)
|
|
{
|
|
for (uint seed = 1; seed < 200; seed += 7)
|
|
{
|
|
int n = BoonMath.PickBoons(seed, classId, ref pool, out byte a0, out byte a1, out byte a2);
|
|
Assert.AreEqual(3, n, "the default pool always fills 3 options");
|
|
Assert.AreNotEqual(a0, a1, "distinct");
|
|
Assert.AreNotEqual(a1, a2, "distinct");
|
|
Assert.AreNotEqual(a0, a2, "distinct");
|
|
|
|
// Deterministic re-draw.
|
|
BoonMath.PickBoons(seed, classId, ref pool, out byte b0, out byte b1, out byte b2);
|
|
Assert.AreEqual(a0, b0);
|
|
Assert.AreEqual(a1, b1);
|
|
Assert.AreEqual(a2, b2);
|
|
|
|
// Every option is class-legal.
|
|
byte bit = BoonMath.MaskFor(classId);
|
|
foreach (var id in new[] { a0, a1, a2 })
|
|
{
|
|
int idx = BoonMath.FindDef(ref pool, id);
|
|
Assert.GreaterOrEqual(idx, 0, "offered id exists");
|
|
Assert.AreNotEqual(0, pool.Defs[idx].ClassMask & bit,
|
|
$"boon {id} offered to class {classId} must pass its ClassMask");
|
|
}
|
|
}
|
|
}
|
|
blob.Dispose();
|
|
}
|
|
|
|
[Test]
|
|
public void OfferSystem_DealsOncePerRoom_ExpeditionOnly_PerPlayerSeeds()
|
|
{
|
|
var world = new World("BoonOfferTest");
|
|
using (world)
|
|
{
|
|
var group = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
|
|
group.AddSystemToUpdateList(world.GetOrCreateSystem<BoonOfferSystem>());
|
|
group.SortSystems();
|
|
world.SetTime(new TimeData(elapsedTime: 0f, deltaTime: 1f / 60f));
|
|
var em = world.EntityManager;
|
|
|
|
var dir = em.CreateEntity(typeof(RunInfo), typeof(RunRuntime));
|
|
em.SetComponentData(dir, new RunInfo { Lifecycle = RunLifecycle.RoomReward, CurrentRoom = 1 });
|
|
em.SetComponentData(dir, new RunRuntime { RunSeed = 4242u, RoomEpoch = 2 });
|
|
|
|
var catalog = em.CreateEntity(typeof(BoonCatalog));
|
|
em.SetComponentData(catalog, new BoonCatalog { Value = BoonCatalogData.BuildDefault() });
|
|
|
|
Entity MakePlayer(int netId, byte region, byte classId)
|
|
{
|
|
var e = em.CreateEntity(typeof(PlayerTag), typeof(BoonOffer), typeof(GhostOwner),
|
|
typeof(RegionTag), typeof(PlayerClass));
|
|
em.SetComponentData(e, new GhostOwner { NetworkId = netId });
|
|
em.SetComponentData(e, new RegionTag { Region = region });
|
|
em.SetComponentData(e, new PlayerClass { ClassId = classId });
|
|
return e;
|
|
}
|
|
var out1 = MakePlayer(1, RegionId.Expedition, 0);
|
|
var out2 = MakePlayer(2, RegionId.Expedition, 1);
|
|
var home = MakePlayer(3, RegionId.Base, 0);
|
|
|
|
group.Update(); // one-shot state attach
|
|
group.Update(); // deal
|
|
|
|
var offer1 = em.GetComponentData<BoonOffer>(out1);
|
|
var offer2 = em.GetComponentData<BoonOffer>(out2);
|
|
Assert.AreEqual(1, offer1.Pending, "expedition player 1 dealt");
|
|
Assert.AreEqual(1, offer2.Pending, "expedition player 2 dealt");
|
|
Assert.AreEqual(0, em.GetComponentData<BoonOffer>(home).Pending, "home player dealt NOTHING");
|
|
bool differ = offer1.Option0 != offer2.Option0 || offer1.Option1 != offer2.Option1
|
|
|| offer1.Option2 != offer2.Option2;
|
|
Assert.IsTrue(differ, "per-player seeds -> co-op offers differ (seed folds NetworkId)");
|
|
|
|
// Same epoch -> no re-deal (clear one offer and confirm it stays cleared).
|
|
em.SetComponentData(out1, default(BoonOffer));
|
|
group.Update();
|
|
Assert.AreEqual(0, em.GetComponentData<BoonOffer>(out1).Pending, "one deal per RoomEpoch (latch)");
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|