using ProjectM.Simulation; using Unity.Burst; using Unity.Collections; using Unity.Entities; using Unity.NetCode; using Unity.Transforms; namespace ProjectM.Server { /// /// Server-authoritative handler for RPCs. Resolves the sender's player /// (via the source connection's -> ), flips its /// to the requested region, and teleports it to that region's origin /// (, centered on the base via ). /// Runs in the default server SimulationSystemGroup (NOT the prediction loop) so the transit applies once; /// the next snapshot reconciles the owner-predicted client and re-scopes /// which region's ghosts the connection receives. Mirrors the RPC shape. /// [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)] public partial struct RegionTransitSystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate(); var builder = new EntityQueryBuilder(Allocator.Temp) .WithAll(); state.RequireForUpdate(state.GetEntityQuery(builder)); } [BurstCompile] public void OnUpdate(ref SystemState state) { var baseCenter = BaseGridMath.PlotCenter(SystemAPI.GetSingleton()); // Map connection NetworkId -> player entity. var playerByConn = new NativeHashMap(8, Allocator.Temp); foreach (var (owner, entity) in SystemAPI.Query>().WithAll().WithEntityAccess()) { playerByConn[owner.ValueRO.NetworkId] = entity; } var ecb = new EntityCommandBuffer(Allocator.Temp); foreach (var (request, receive, requestEntity) in SystemAPI.Query, RefRO>().WithEntityAccess()) { var connEntity = receive.ValueRO.SourceConnection; if (SystemAPI.HasComponent(connEntity)) { int connId = SystemAPI.GetComponent(connEntity).Value; if (playerByConn.TryGetValue(connId, out var player)) { byte target = request.ValueRO.TargetRegion; SystemAPI.GetComponentRW(player).ValueRW.Region = target; SystemAPI.GetComponentRW(player).ValueRW.Position = RegionMath.RegionOrigin(target, baseCenter); } } ecb.DestroyEntity(requestEntity); } ecb.Playback(state.EntityManager); playerByConn.Dispose(); } } }