using Unity.Burst; using Unity.Entities; using Unity.NetCode; using Unity.Collections; using Unity.Mathematics; using Unity.Transforms; // RPC request from client to server for game to go "in game" and send snapshots / inputs public struct GoInGameRequest : IRpcCommand { } // When client has a connection with network id, go in game and tell server to also go in game [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation | WorldSystemFilterFlags.ThinClientSimulation)] public partial struct GoInGameClientSystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate(); var builder = new EntityQueryBuilder(Allocator.Temp) .WithAll() .WithNone(); state.RequireForUpdate(state.GetEntityQuery(builder)); } [BurstCompile] public void OnUpdate(ref SystemState state) { var commandBuffer = new EntityCommandBuffer(Allocator.Temp); foreach (var (id, entity) in SystemAPI.Query>().WithEntityAccess().WithNone()) { commandBuffer.AddComponent(entity); var req = commandBuffer.CreateEntity(); commandBuffer.AddComponent(req); commandBuffer.AddComponent(req, new SendRpcCommandRequest { TargetConnection = entity }); } commandBuffer.Playback(state.EntityManager); } } // When server receives go in game request, go in game and delete request [BurstCompile] [WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)] public partial struct GoInGameServerSystem : ISystem { private ComponentLookup networkIdFromEntity; [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate(); var builder = new EntityQueryBuilder(Allocator.Temp) .WithAll() .WithAll(); state.RequireForUpdate(state.GetEntityQuery(builder)); networkIdFromEntity = state.GetComponentLookup(true); } [BurstCompile] public void OnUpdate(ref SystemState state) { var prefab = SystemAPI.GetSingleton().Cube; state.EntityManager.GetName(prefab, out var prefabName); var worldName = state.WorldUnmanaged.Name; var commandBuffer = new EntityCommandBuffer(Allocator.Temp); networkIdFromEntity.Update(ref state); foreach (var (reqSrc, reqEntity) in SystemAPI.Query>().WithAll().WithEntityAccess()) { commandBuffer.AddComponent(reqSrc.ValueRO.SourceConnection); var networkId = networkIdFromEntity[reqSrc.ValueRO.SourceConnection]; UnityEngine.Debug.Log($"'{worldName}' setting connection '{networkId.Value}' to in game, spawning a Ghost '{prefabName}' for them!"); var player = commandBuffer.Instantiate(prefab); commandBuffer.SetComponent(player, new GhostOwner { NetworkId = networkId.Value}); // Add the player to the linked entity group so it is destroyed automatically on disconnect commandBuffer.AppendToBuffer(reqSrc.ValueRO.SourceConnection, new LinkedEntityGroup{Value = player}); // Give each NetworkId their own spawn pos: { var isEven = (networkId.Value & 1) == 0; const float halfCharacterWidthPlusHalfPadding = .55f; const float spawnStaggeredOffset = 0.25f; var staggeredXPos = networkId.Value * math.@select(halfCharacterWidthPlusHalfPadding, -halfCharacterWidthPlusHalfPadding, isEven) + math.@select(-spawnStaggeredOffset, spawnStaggeredOffset, isEven); var preventZFighting = -0.01f * networkId.Value; commandBuffer.SetComponent(player, LocalTransform.FromPosition(new float3(staggeredXPos, preventZFighting, 0))); } commandBuffer.DestroyEntity(reqEntity); } commandBuffer.Playback(state.EntityManager); } }