Files
2026-06-04 11:35:57 -07:00

107 lines
4.6 KiB
C#

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.");
}
}
}
}