Run Re-Do
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
using NUnit.Framework;
|
||||
using ProjectM.Server;
|
||||
using ProjectM.Simulation;
|
||||
using Unity.Collections;
|
||||
using Unity.Core;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.NetCode;
|
||||
using Unity.Transforms;
|
||||
|
||||
namespace ProjectM.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Pins Step 12a — the born-correct PERMANENT meta seeding in <see cref="GoInGameServerSystem"/>: a spawning
|
||||
/// player replays its class's persisted <see cref="MetaTierState"/> tiers as meta-band StatModifiers
|
||||
/// (Value = ValuePerTier * tier, SourceId = MetaSourceIdBase + id); other-class rows and unknown ids are
|
||||
/// skipped; an over-MaxTier saved row is CLAMPED (D-F5); and the availability guard blocks the WHOLE spawn
|
||||
/// (request preserved, nothing consumed) when the catalog is absent (N2).
|
||||
/// </summary>
|
||||
public class MetaSeedingTests
|
||||
{
|
||||
static (World world, SimulationSystemGroup group) MakeWorld()
|
||||
{
|
||||
var world = new World("MetaSeedTest");
|
||||
var group = world.GetOrCreateSystemManaged<SimulationSystemGroup>();
|
||||
group.AddSystemToUpdateList(world.GetOrCreateSystem<GoInGameServerSystem>());
|
||||
group.SortSystems();
|
||||
world.SetTime(new TimeData(elapsedTime: 0f, deltaTime: 1f / 60f));
|
||||
return (world, group);
|
||||
}
|
||||
|
||||
static Entity MakePlayerPrefab(EntityManager em)
|
||||
{
|
||||
var e = em.CreateEntity(typeof(LocalTransform), typeof(GhostOwner), typeof(AbilityRef), typeof(PlayerTag));
|
||||
em.SetComponentData(e, LocalTransform.Identity);
|
||||
em.AddBuffer<StatModifier>(e);
|
||||
em.AddComponent<Prefab>(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
static void MakeSpawnRequest(EntityManager em, byte classId)
|
||||
{
|
||||
var conn = em.CreateEntity(typeof(NetworkId));
|
||||
em.SetComponentData(conn, new NetworkId { Value = 1 });
|
||||
em.AddBuffer<LinkedEntityGroup>(conn);
|
||||
var req = em.CreateEntity(typeof(GoInGameRequest), typeof(ReceiveRpcCommandRequest));
|
||||
em.SetComponentData(req, new GoInGameRequest { ClassId = classId });
|
||||
em.SetComponentData(req, new ReceiveRpcCommandRequest { SourceConnection = conn });
|
||||
}
|
||||
|
||||
static int MetaRows(EntityManager em, Entity player, out float firstValue, out uint firstSource)
|
||||
{
|
||||
firstValue = 0f; firstSource = 0;
|
||||
var mods = em.GetBuffer<StatModifier>(player);
|
||||
int n = 0;
|
||||
for (int i = 0; i < mods.Length; i++)
|
||||
if (mods[i].SourceId >= Tuning.MetaSourceIdBase
|
||||
&& mods[i].SourceId < Tuning.MetaSourceIdBase + Tuning.MetaSourceIdSpan)
|
||||
{
|
||||
if (n == 0) { firstValue = mods[i].Value; firstSource = mods[i].SourceId; }
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Spawn_SeedsClassTiers_SkipsOthers_ClampsOverMax()
|
||||
{
|
||||
var (world, group) = MakeWorld();
|
||||
var em = world.EntityManager;
|
||||
var prefab = MakePlayerPrefab(em);
|
||||
var spawnerE = em.CreateEntity(typeof(PlayerSpawner));
|
||||
em.SetComponentData(spawnerE, new PlayerSpawner { PlayerPrefab = prefab, SpawnRingRadius = 2f, RingSlots = 8 });
|
||||
|
||||
var catalogE = em.CreateEntity(typeof(MetaUpgradeCatalog));
|
||||
em.SetComponentData(catalogE, new MetaUpgradeCatalog { Value = MetaCatalogData.BuildDefault() });
|
||||
var record = em.AddBuffer<MetaTierState>(catalogE); // the tier record rides any singleton entity in tests
|
||||
byte warrior = ClassTraits.WarriorClass; // normalized CharacterId (2)
|
||||
record.Add(new MetaTierState { ClassId = warrior, UpgradeId = 1, Tier = 2 }); // Reinforced Frame t2 -> +30
|
||||
record.Add(new MetaTierState { ClassId = warrior, UpgradeId = 5, Tier = 9 }); // Warrior's Might, saved OVER MaxTier(4) -> clamp
|
||||
record.Add(new MetaTierState { ClassId = warrior, UpgradeId = 200, Tier = 1 }); // unknown id -> skipped
|
||||
record.Add(new MetaTierState { ClassId = ClassTraits.RangerClass, UpgradeId = 2, Tier = 3 }); // other class -> skipped
|
||||
record.Add(new MetaTierState { ClassId = warrior, UpgradeId = 7, Tier = 1 }); // Ranger-masked (Longshot) -> skipped
|
||||
|
||||
MakeSpawnRequest(em, warrior);
|
||||
group.Update();
|
||||
|
||||
var pq = em.CreateEntityQuery(typeof(PlayerTag), typeof(PlayerClass));
|
||||
var players = pq.ToEntityArray(Allocator.Temp);
|
||||
Assert.AreEqual(1, players.Length, "player spawned");
|
||||
var player = players[0];
|
||||
players.Dispose(); pq.Dispose(); // BEFORE the world
|
||||
|
||||
var mods = em.GetBuffer<StatModifier>(player);
|
||||
float frameValue = 0f, mightValue = 0f;
|
||||
int metaRows = 0;
|
||||
for (int i = 0; i < mods.Length; i++)
|
||||
{
|
||||
if (mods[i].SourceId == Tuning.MetaSourceIdBase + 1) { frameValue = mods[i].Value; metaRows++; }
|
||||
else if (mods[i].SourceId == Tuning.MetaSourceIdBase + 5) { mightValue = mods[i].Value; metaRows++; }
|
||||
else if (mods[i].SourceId >= Tuning.MetaSourceIdBase
|
||||
&& mods[i].SourceId < Tuning.MetaSourceIdBase + Tuning.MetaSourceIdSpan) metaRows++;
|
||||
}
|
||||
Assert.AreEqual(2, metaRows, "exactly the two legal Warrior tiers seeded (unknown/other-class/other-mask skipped)");
|
||||
Assert.AreEqual(30f, frameValue, 1e-3f, "Reinforced Frame tier 2 = 15 * 2");
|
||||
Assert.AreEqual(0.40f, mightValue, 1e-3f, "Warrior's Might CLAMPED to MaxTier 4 = 0.10 * 4 (D-F5)");
|
||||
world.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MissingCatalog_BlocksSpawn_PreservesRequest()
|
||||
{
|
||||
var (world, group) = MakeWorld();
|
||||
var em = world.EntityManager;
|
||||
var prefab = MakePlayerPrefab(em);
|
||||
var spawnerE = em.CreateEntity(typeof(PlayerSpawner));
|
||||
em.SetComponentData(spawnerE, new PlayerSpawner { PlayerPrefab = prefab, SpawnRingRadius = 2f, RingSlots = 8 });
|
||||
// NO catalog, NO tier record.
|
||||
MakeSpawnRequest(em, ClassTraits.WarriorClass);
|
||||
|
||||
group.Update();
|
||||
|
||||
var pq = em.CreateEntityQuery(typeof(PlayerClass));
|
||||
Assert.AreEqual(0, pq.CalculateEntityCount(), "no player spawned while blocked (N2)");
|
||||
var rq = em.CreateEntityQuery(typeof(GoInGameRequest));
|
||||
Assert.AreEqual(1, rq.CalculateEntityCount(), "request PRESERVED — the spawn retries when the catalog streams in");
|
||||
pq.Dispose(); rq.Dispose(); // BEFORE the world (a using-var here would outlive it)in");
|
||||
world.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user