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 { /// /// Plain-Entities EditMode tests for the server-only — 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. /// public class RegionTransitSystemTests { static (World world, SimulationSystemGroup group) MakeWorld(string name) { var world = new World(name); var group = world.GetOrCreateSystemManaged(); group.AddSystemToUpdateList(world.GetOrCreateSystem()); 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(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(player).Region, "The sender's player flips to the requested region."); Assert.AreEqual(1005f, em.GetComponentData(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(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."); } } } }