├── .static ├── demo.png ├── organizing_entities.png └── synchronizing_entities.png ├── Archetypes.meta ├── Archetypes ├── NetcodeArchetypeAttribute.cs ├── NetcodeArchetypeAttribute.cs.meta ├── NetcodeEntityArchetypeManager.cs └── NetcodeEntityArchetypeManager.cs.meta ├── Bootstrap.cs ├── Bootstrap.cs.meta ├── Client.meta ├── Client ├── ClientManager.cs ├── ClientManager.cs.meta ├── Components.meta ├── Components │ ├── ClientToServerCommandHandler.cs │ ├── ClientToServerCommandHandler.cs.meta │ ├── ConnectToServer.cs │ └── ConnectToServer.cs.meta ├── Entities.meta ├── Entities │ ├── AClientNetworkEntityBuilderSystem.cs │ ├── AClientNetworkEntityBuilderSystem.cs.meta │ ├── AClientNetworkEntityDestroySystem.cs │ ├── AClientNetworkEntityDestroySystem.cs.meta │ ├── ClientNetworkEntityBuilder.cs │ ├── ClientNetworkEntityBuilder.cs.meta │ ├── ClientNetworkEntitySystem.cs │ └── ClientNetworkEntitySystem.cs.meta ├── Groups.meta ├── Groups │ ├── ClientConnectionSystemGroup.cs │ ├── ClientConnectionSystemGroup.cs.meta │ ├── ClientGameSimulationSystemGroup.cs │ ├── ClientGameSimulationSystemGroup.cs.meta │ ├── ClientNetworkEntitySystemGroup.cs │ ├── ClientNetworkEntitySystemGroup.cs.meta │ ├── ClientRequestProcessingSystemGroup.cs │ └── ClientRequestProcessingSystemGroup.cs.meta ├── Lifecycle.meta ├── Lifecycle │ ├── ClientConnectToServerSystem.cs │ ├── ClientConnectToServerSystem.cs.meta │ ├── ClientConnectedSystem.cs │ ├── ClientConnectedSystem.cs.meta │ ├── ClientDisconnectedSystem.cs │ └── ClientDisconnectedSystem.cs.meta ├── Packets.meta └── Packets │ ├── AClientReceiveRpcCommandSystem.cs │ ├── AClientReceiveRpcCommandSystem.cs.meta │ ├── ClientToServerRpcCommandBuilder.cs │ ├── ClientToServerRpcCommandBuilder.cs.meta │ ├── INetworkEntityCopyRpcCommand.cs │ └── INetworkEntityCopyRpcCommand.cs.meta ├── Debugging.meta ├── Debugging ├── ValidateHangingRpcCommandsSystem.cs └── ValidateHangingRpcCommandsSystem.cs.meta ├── ECSPowerNetcode.asmdef ├── ECSPowerNetcode.asmdef.meta ├── ECSPowerNetcodeModule.cs ├── ECSPowerNetcodeModule.cs.meta ├── EntityBulderExtensions.meta ├── EntityBulderExtensions ├── CreateFromNetcodeArchetypeStrategy.cs ├── CreateFromNetcodeArchetypeStrategy.cs.meta ├── NetcodeEntityBuilder.cs ├── NetcodeEntityBuilder.cs.meta ├── NetcodeEntityManagerWrapper.cs ├── NetcodeEntityManagerWrapper.cs.meta ├── NetcodeEntityWrapper.cs └── NetcodeEntityWrapper.cs.meta ├── Extensions.meta ├── Extensions ├── CollectionExtension.cs ├── CollectionExtension.cs.meta ├── TimeExtensions.cs └── TimeExtensions.cs.meta ├── Features.meta ├── Features ├── ManagedRpcCommands.meta ├── ManagedRpcCommands │ ├── AServerReceiveManagedRpcCommandSystem.cs │ ├── AServerReceiveManagedRpcCommandSystem.cs.meta │ ├── ClientReceiveManagedRpcCommandResultSystem.cs │ ├── ClientReceiveManagedRpcCommandResultSystem.cs.meta │ ├── ClientToServerManagedRpcCommandBuilder.cs │ ├── ClientToServerManagedRpcCommandBuilder.cs.meta │ ├── IManagedRpcCommand.cs │ ├── IManagedRpcCommand.cs.meta │ ├── ManagedRpcCommandResponse.cs │ ├── ManagedRpcCommandResponse.cs.meta │ ├── ManagedRpcCommandResult.cs │ └── ManagedRpcCommandResult.cs.meta ├── NetworkEntities.meta └── NetworkEntities │ ├── NetworkEntity.cs │ ├── NetworkEntity.cs.meta │ ├── NetworkEntityRegistered.cs │ └── NetworkEntityRegistered.cs.meta ├── LICENSE ├── LICENSE.meta ├── Prefabs.meta ├── Prefabs ├── Network.prefab ├── Network.prefab.meta ├── NetworkConnectionGhost.prefab └── NetworkConnectionGhost.prefab.meta ├── README.md ├── README.md.meta ├── Server.meta ├── Server ├── Components.meta ├── Components │ ├── ServerToClientCommandHandler.cs │ ├── ServerToClientCommandHandler.cs.meta │ ├── StartServer.cs │ ├── StartServer.cs.meta │ ├── TransferNetworkEntityToAllClients.cs │ ├── TransferNetworkEntityToAllClients.cs.meta │ ├── TransferNetworkEntityToClient.cs │ └── TransferNetworkEntityToClient.cs.meta ├── Destroying.meta ├── Destroying │ ├── AServerNetworkEntityDestroySystem.cs │ ├── AServerNetworkEntityDestroySystem.cs.meta │ ├── ServerDestroy.cs │ ├── ServerDestroy.cs.meta │ ├── ServerNetworkEntityDestroyCommand.cs │ └── ServerNetworkEntityDestroyCommand.cs.meta ├── Entities.meta ├── Entities │ ├── AServerNetworkEntityBuilderSystem.cs │ ├── AServerNetworkEntityBuilderSystem.cs.meta │ ├── AServerNetworkEntityBuilderSystemT2.cs │ ├── AServerNetworkEntityBuilderSystemT2.cs.meta │ ├── AServerNetworkEntityBuilderSystemT3.cs │ ├── AServerNetworkEntityBuilderSystemT3.cs.meta │ ├── DefaultNetworkEntityIdFactory.cs │ ├── DefaultNetworkEntityIdFactory.cs.meta │ ├── INetworkEntityIdFactory.cs │ ├── INetworkEntityIdFactory.cs.meta │ ├── ServerNetworkEntityBuilder.cs │ ├── ServerNetworkEntityBuilder.cs.meta │ ├── ServerNetworkEntitySystem.cs │ └── ServerNetworkEntitySystem.cs.meta ├── Exceptions.meta ├── Exceptions │ ├── ClientConnectionNotFoundException.cs │ ├── ClientConnectionNotFoundException.cs.meta │ ├── ServerException.cs │ └── ServerException.cs.meta ├── Groups.meta ├── Groups │ ├── ServerCleanupSystemGroup.cs │ ├── ServerCleanupSystemGroup.cs.meta │ ├── ServerConnectionSystemGroup.cs │ ├── ServerConnectionSystemGroup.cs.meta │ ├── ServerGameSimulationSystemGroup.cs │ ├── ServerGameSimulationSystemGroup.cs.meta │ ├── ServerNetworkEntitySynchronizationSystemGroup.cs │ ├── ServerNetworkEntitySynchronizationSystemGroup.cs.meta │ ├── ServerNetworkEntitySystemGroup.cs │ ├── ServerNetworkEntitySystemGroup.cs.meta │ ├── ServerRequestProcessingSystemGroup.cs │ └── ServerRequestProcessingSystemGroup.cs.meta ├── Lifecycle.meta ├── Lifecycle │ ├── ServerClientDisconnectedSystem.cs │ ├── ServerClientDisconnectedSystem.cs.meta │ ├── ServerNewClientConnectedSystem.cs │ ├── ServerNewClientConnectedSystem.cs.meta │ ├── ServerStartSystem.cs │ └── ServerStartSystem.cs.meta ├── Packets.meta ├── Packets │ ├── AServerReceiveRpcCommandSystem.cs │ ├── AServerReceiveRpcCommandSystem.cs.meta │ ├── ServerToClientRpcCommandBuilder.cs │ └── ServerToClientRpcCommandBuilder.cs.meta ├── ServerManager.cs └── ServerManager.cs.meta ├── Shared.meta ├── Shared ├── Components.meta ├── Components │ ├── SyncGhostComponent.cs │ └── SyncGhostComponent.cs.meta ├── ConnectionDescription.cs ├── ConnectionDescription.cs.meta ├── DefaultNetworkEntityManager.cs ├── DefaultNetworkEntityManager.cs.meta ├── INetworkEntityManager.cs └── INetworkEntityManager.cs.meta ├── Utils.meta ├── Utils ├── MultiValueDictionary.cs └── MultiValueDictionary.cs.meta ├── Worlds.meta └── Worlds ├── EntityWorldManager.cs ├── EntityWorldManager.cs.meta ├── WorldType.cs └── WorldType.cs.meta /.static/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/actionk/ECSPowerNetcode/bcd4491566e15ca14f95a7b3964f94280f9afe8d/.static/demo.png -------------------------------------------------------------------------------- /.static/organizing_entities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/actionk/ECSPowerNetcode/bcd4491566e15ca14f95a7b3964f94280f9afe8d/.static/organizing_entities.png -------------------------------------------------------------------------------- /.static/synchronizing_entities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/actionk/ECSPowerNetcode/bcd4491566e15ca14f95a7b3964f94280f9afe8d/.static/synchronizing_entities.png -------------------------------------------------------------------------------- /Archetypes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 840a6c30b6584c469bb6674a50e95c15 3 | timeCreated: 1604751498 -------------------------------------------------------------------------------- /Archetypes/NetcodeArchetypeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Plugins.ECSPowerNetcode.Worlds; 3 | 4 | namespace Plugins.ECSPowerNetcode.Archetypes 5 | { 6 | [AttributeUsage(AttributeTargets.Class)] 7 | public class NetcodeArchetypeAttribute : Attribute 8 | { 9 | public WorldType WorldType { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Archetypes/NetcodeArchetypeAttribute.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 411df34ae2f54d57997bf6b4c052c8f4 3 | timeCreated: 1604751506 -------------------------------------------------------------------------------- /Archetypes/NetcodeEntityArchetypeManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Plugins.ECSEntityBuilder.Archetypes; 6 | using Plugins.ECSPowerNetcode.Worlds; 7 | using Unity.Entities; 8 | using UnityEngine; 9 | 10 | namespace Plugins.ECSPowerNetcode.Archetypes 11 | { 12 | public class NetcodeEntityArchetypeManager 13 | { 14 | #region Singleton 15 | 16 | private static NetcodeEntityArchetypeManager INSTANCE = new NetcodeEntityArchetypeManager(); 17 | 18 | static NetcodeEntityArchetypeManager() 19 | { 20 | } 21 | 22 | private NetcodeEntityArchetypeManager() 23 | { 24 | } 25 | 26 | public static NetcodeEntityArchetypeManager Instance 27 | { 28 | get { return INSTANCE; } 29 | } 30 | 31 | // for quick play mode entering 32 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 33 | public static void Reset() 34 | { 35 | INSTANCE = new NetcodeEntityArchetypeManager(); 36 | } 37 | 38 | #endregion 39 | 40 | private struct EntityArchetypeHolder 41 | { 42 | public EntityArchetype defaultArchetype; 43 | public EntityArchetype clientArchetype; 44 | public EntityArchetype serverArchetype; 45 | } 46 | 47 | private readonly Dictionary m_archetypes = new Dictionary(); 48 | 49 | public void InitializeArchetypes(Assembly assembly) 50 | { 51 | foreach (var type in assembly.GetTypes()) 52 | { 53 | if (Attribute.GetCustomAttribute(type, typeof(NetcodeArchetypeAttribute)) is NetcodeArchetypeAttribute archetypeAttribute) 54 | GetOrCreateArchetype(type, archetypeAttribute); 55 | } 56 | } 57 | 58 | public void InitializeArchetypes(params Type[] types) 59 | { 60 | foreach (var type in types) 61 | GetOrCreateArchetype(type); 62 | } 63 | 64 | public EntityArchetype GetOrCreateArchetype() where T : IArchetypeDescriptor 65 | { 66 | return GetOrCreateArchetype(WorldType.DEFAULT); 67 | } 68 | 69 | public EntityArchetype GetOrCreateArchetype(WorldType worldType) where T : IArchetypeDescriptor 70 | { 71 | var archetypeType = typeof(T); 72 | if (archetypeType.IsAbstract) 73 | { 74 | throw new NotImplementedException($"It's impossible to create an archetype from an abstract class: {archetypeType}"); 75 | } 76 | 77 | var entityArchetypeHolder = GetOrCreateArchetype(archetypeType); 78 | switch (worldType) 79 | { 80 | case WorldType.DEFAULT: 81 | return entityArchetypeHolder.defaultArchetype; 82 | case WorldType.CLIENT: 83 | return entityArchetypeHolder.clientArchetype; 84 | case WorldType.SERVER: 85 | return entityArchetypeHolder.serverArchetype; 86 | } 87 | 88 | throw new NotImplementedException(); 89 | } 90 | 91 | private EntityArchetypeHolder GetOrCreateArchetype(Type archetypeType, NetcodeArchetypeAttribute attribute = null) 92 | { 93 | if (m_archetypes.ContainsKey(archetypeType)) 94 | return m_archetypes[archetypeType]; 95 | 96 | if (attribute == null) 97 | attribute = Attribute.GetCustomAttribute(archetypeType, typeof(NetcodeArchetypeAttribute)) as NetcodeArchetypeAttribute; 98 | 99 | try 100 | { 101 | var instance = Activator.CreateInstance(archetypeType) as IArchetypeDescriptor; 102 | if (instance == null) 103 | { 104 | throw new NotImplementedException( 105 | $"Archetype descriptor {archetypeType} should implement {typeof(IArchetypeDescriptor)} interface and have an empty constructor"); 106 | } 107 | 108 | if (instance is IClientServerArchetypeDescriptor clientServerInstance) 109 | { 110 | var clientComponents = clientServerInstance.Components.Concat(clientServerInstance.ClientOnlyComponents).ToArray(); 111 | var clientArchetype = EntityWorldManager.Instance.Client.EntityManager.CreateArchetype(clientComponents); 112 | 113 | var serverComponents = clientServerInstance.Components.Concat(clientServerInstance.ServerOnlyComponents).ToArray(); 114 | var serverArchetype = EntityWorldManager.Instance.Server.EntityManager.CreateArchetype(serverComponents); 115 | 116 | var archetypeHolder = new EntityArchetypeHolder 117 | { 118 | clientArchetype = clientArchetype, 119 | serverArchetype = serverArchetype 120 | }; 121 | m_archetypes[archetypeType] = archetypeHolder; 122 | return archetypeHolder; 123 | } 124 | else if (instance is ICustomArchetypeDescriptor customArchetypeDescriptor) 125 | { 126 | var components = customArchetypeDescriptor.Components.Concat(customArchetypeDescriptor.CustomComponents).ToArray(); 127 | 128 | var archetype = GetEntityManagerByWorldType(attribute?.WorldType ?? WorldType.DEFAULT).CreateArchetype(components); 129 | var archetypeHolder = new EntityArchetypeHolder 130 | { 131 | defaultArchetype = archetype, 132 | }; 133 | m_archetypes[archetypeType] = archetypeHolder; 134 | return archetypeHolder; 135 | } 136 | else 137 | { 138 | var archetype = EntityWorldManager.Instance.Default.EntityManager.CreateArchetype(instance.Components); 139 | 140 | var archetypeHolder = new EntityArchetypeHolder 141 | { 142 | defaultArchetype = archetype 143 | }; 144 | m_archetypes[archetypeType] = archetypeHolder; 145 | return archetypeHolder; 146 | } 147 | } 148 | catch (NotImplementedException) 149 | { 150 | throw; 151 | } 152 | catch (Exception e) 153 | { 154 | throw new NotImplementedException($"Failed to instantiate archetype from archetype descriptor {archetypeType}", e); 155 | } 156 | } 157 | 158 | private EntityManager GetEntityManagerByWorldType(WorldType worldType) 159 | { 160 | switch (worldType) 161 | { 162 | default: 163 | return EntityWorldManager.Instance.Default.EntityManager; 164 | case WorldType.CLIENT: 165 | return EntityWorldManager.Instance.Client.EntityManager; 166 | case WorldType.SERVER: 167 | return EntityWorldManager.Instance.Server.EntityManager; 168 | } 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /Archetypes/NetcodeEntityArchetypeManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 419f6485b3e8492bbee3c50452ad26af 3 | timeCreated: 1604751556 -------------------------------------------------------------------------------- /Bootstrap.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Worlds; 2 | using Unity.Entities; 3 | using Unity.NetCode; 4 | using UnityEngine.LowLevel; 5 | 6 | namespace Plugins.ECSPowerNetcode 7 | { 8 | public class Bootstrap : ClientServerBootstrap 9 | { 10 | public override bool Initialize(string defaultWorldName) 11 | { 12 | Initialize(true, true); 13 | return true; 14 | } 15 | 16 | public void Initialize(bool createClientWorld, bool createServerWorld) 17 | { 18 | var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.Default); 19 | GenerateSystemLists(systems); 20 | 21 | var world = new World("DefaultWorld"); 22 | World.DefaultGameObjectInjectionWorld = world; 23 | 24 | DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(world, ExplicitDefaultWorldSystems); 25 | 26 | var currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); 27 | ScriptBehaviourUpdateOrder.AddWorldToPlayerLoop(world, ref currentPlayerLoop); 28 | PlayerLoop.SetPlayerLoop(currentPlayerLoop); 29 | 30 | if (createClientWorld) 31 | CreateClientWorld(world, "ClientWorld"); 32 | 33 | if (createServerWorld) 34 | CreateServerWorld(world, "ServerWorld"); 35 | 36 | EntityWorldManager.Instance.Initialize(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Bootstrap.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 019bad674ab140b199530b76838cdb5a 3 | timeCreated: 1601370476 -------------------------------------------------------------------------------- /Client.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a19addbb35024a0eb2045773918eaffa 3 | timeCreated: 1589632859 -------------------------------------------------------------------------------- /Client/ClientManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Plugins.ECSPowerNetcode.Client.Components; 4 | using Plugins.ECSPowerNetcode.Shared; 5 | using Plugins.ECSPowerNetcode.Utils; 6 | using Plugins.ECSPowerNetcode.Worlds; 7 | using Unity.Entities; 8 | using Unity.NetCode; 9 | using UnityEngine; 10 | 11 | namespace Plugins.ECSPowerNetcode.Client 12 | { 13 | public class ClientManager 14 | { 15 | private readonly MultiValueDictionary m_entitiesWaitingForManagedRpcCommandResult = new MultiValueDictionary(); 16 | private ulong m_currentManagedPacketId = 1; 17 | 18 | public delegate void OnConnected(ConnectionDescription connectionDescription); 19 | 20 | public delegate void OnDisconnected(); 21 | 22 | public event OnConnected OnConnectedHandler; 23 | public event OnDisconnected OnDisconnectedHandler; 24 | 25 | public INetworkEntityManager NetworkEntityManager { get; set; } = new DefaultNetworkEntityManager(); 26 | public ConnectionDescription ConnectionToServer { get; private set; } 27 | public bool IsConnected { get; private set; } 28 | public ulong NextManagedPacketId => m_currentManagedPacketId++; 29 | 30 | public uint ServerTick => EntityWorldManager.Instance.ClientTick; 31 | 32 | public void OnConnectedToServer(Entity connectionEntity, Entity commandHandlerEntity, int networkConnectionId) 33 | { 34 | ConnectionToServer = new ConnectionDescription(networkConnectionId, connectionEntity, commandHandlerEntity); 35 | IsConnected = true; 36 | 37 | OnConnectedHandler?.Invoke(ConnectionToServer); 38 | } 39 | 40 | public void OnDisconnectedFromServer() 41 | { 42 | IsConnected = false; 43 | OnDisconnectedHandler?.Invoke(); 44 | } 45 | 46 | public void ConnectToServer(ushort port, string host = "127.0.0.1") 47 | { 48 | var clientWorld = EntityWorldManager.Instance.Client; 49 | if (!clientWorld.IsCreated) 50 | throw new NotImplementedException("Client world doesn't exist!"); 51 | 52 | var connectToServer = clientWorld.EntityManager.CreateEntity(); 53 | clientWorld.EntityManager.AddComponentData(connectToServer, new ConnectToServer {port = port, host = host}); 54 | } 55 | 56 | public void Disconnect() 57 | { 58 | Debug.Log("Disconnecting from server..."); 59 | 60 | if (!IsConnected) 61 | return; 62 | 63 | EntityWorldManager.Instance.Client.EntityManager.AddComponent(ConnectionToServer.connectionEntity); 64 | 65 | IsConnected = false; 66 | } 67 | 68 | public void AddEntityWaitingForManagedRpcCommandResult(Entity entity, ulong packetId) 69 | { 70 | m_entitiesWaitingForManagedRpcCommandResult.Add(packetId, entity); 71 | } 72 | 73 | public HashSet GetEntitiesWaitingForManagedPacket(ulong commandPacketId) 74 | { 75 | if (m_entitiesWaitingForManagedRpcCommandResult.TryGetValue(commandPacketId, out var result)) 76 | { 77 | m_entitiesWaitingForManagedRpcCommandResult.Remove(commandPacketId); 78 | return result; 79 | } 80 | 81 | return null; 82 | } 83 | 84 | #region Singleton 85 | 86 | private static ClientManager INSTANCE = new ClientManager(); 87 | 88 | static ClientManager() 89 | { 90 | } 91 | 92 | private ClientManager() 93 | { 94 | } 95 | 96 | public static ClientManager Instance 97 | { 98 | get { return INSTANCE; } 99 | } 100 | 101 | // for quick play mode entering 102 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 103 | public static void Reset() 104 | { 105 | INSTANCE = new ClientManager(); 106 | } 107 | 108 | #endregion 109 | } 110 | } -------------------------------------------------------------------------------- /Client/ClientManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c412e12cf65a4bcfaa4a9b9da837b997 3 | timeCreated: 1589385053 -------------------------------------------------------------------------------- /Client/Components.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7c0c5a994d414f768064ec8120b95e6f 3 | timeCreated: 1589708937 -------------------------------------------------------------------------------- /Client/Components/ClientToServerCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Client.Components 4 | { 5 | public struct ClientToServerCommandHandler : IComponentData 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /Client/Components/ClientToServerCommandHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 932f0fa91bfe436b9e290ec018b81389 3 | timeCreated: 1589372801 -------------------------------------------------------------------------------- /Client/Components/ConnectToServer.cs: -------------------------------------------------------------------------------- 1 | using Unity.Collections; 2 | using Unity.Entities; 3 | 4 | namespace Plugins.ECSPowerNetcode.Client.Components 5 | { 6 | public struct ConnectToServer : IComponentData 7 | { 8 | public ushort port; 9 | public FixedString32 host; 10 | } 11 | } -------------------------------------------------------------------------------- /Client/Components/ConnectToServer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3b2ba703833467f8ec29c25d002643f 3 | timeCreated: 1589708937 -------------------------------------------------------------------------------- /Client/Entities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: afdbd82cdeb244abb1a29670dde5759c 3 | timeCreated: 1589730094 -------------------------------------------------------------------------------- /Client/Entities/AClientNetworkEntityBuilderSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client.Groups; 2 | using Plugins.ECSPowerNetcode.Client.Packets; 3 | using Unity.Entities; 4 | using Unity.NetCode; 5 | 6 | namespace Plugins.ECSPowerNetcode.Client.Entities 7 | { 8 | [UpdateInGroup(typeof(ClientRequestProcessingSystemGroup))] 9 | [UpdateInWorld(UpdateInWorld.TargetWorld.Client)] 10 | public abstract class AClientNetworkEntityBuilderSystem : ComponentSystem 11 | where TCommand : struct, INetworkEntityCopyRpcCommand 12 | { 13 | protected abstract void CreateNetworkEntity(uint networkEntityId, TCommand command); 14 | protected abstract void SynchronizeNetworkEntity(Entity entity, TCommand command); 15 | 16 | protected override void OnUpdate() 17 | { 18 | Entities 19 | .ForEach((Entity entity, ref TCommand command, ref ReceiveRpcCommandRequestComponent requestComponent) => 20 | { 21 | //Debug.Log($"[Client] Creating a client entity [{GetType()}] with id {command.NetworkEntityId}"); 22 | 23 | var existingEntity = ClientManager.Instance.NetworkEntityManager.TryGetEntityByNetworkEntityId(command.NetworkEntityId); 24 | if (existingEntity != Entity.Null) 25 | SynchronizeNetworkEntity(existingEntity, command); 26 | else 27 | CreateNetworkEntity(command.NetworkEntityId, command); 28 | 29 | PostUpdateCommands.DestroyEntity(entity); 30 | }); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Client/Entities/AClientNetworkEntityBuilderSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3ed3e6febe784a118115992c944cb340 3 | timeCreated: 1589816347 -------------------------------------------------------------------------------- /Client/Entities/AClientNetworkEntityDestroySystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client.Packets; 2 | using Plugins.ECSPowerNetcode.Server.Destroying; 3 | using Plugins.ECSPowerNetcode.Shared; 4 | using Unity.Entities; 5 | using Unity.NetCode; 6 | 7 | namespace Plugins.ECSPowerNetcode.Client.Entities 8 | { 9 | [UpdateInWorld(UpdateInWorld.TargetWorld.Client)] 10 | public abstract class AClientNetworkEntityDestroySystem : AClientReceiveRpcCommandSystem 11 | { 12 | protected abstract void OnDestroyEntity(uint networkEntityId, Entity entity); 13 | 14 | protected override void OnCommand(ref ServerNetworkEntityDestroyCommand packet, ConnectionDescription connectionToServer) 15 | { 16 | var networkEntity = ClientManager.Instance.NetworkEntityManager.TryGetEntityByNetworkEntityId(packet.networkEntityId); 17 | if (networkEntity == Entity.Null) 18 | return; 19 | 20 | OnDestroyEntity(packet.networkEntityId, networkEntity); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Client/Entities/AClientNetworkEntityDestroySystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 994ad0b32c0c4a6886270175f39d75b4 3 | timeCreated: 1590236208 -------------------------------------------------------------------------------- /Client/Entities/ClientNetworkEntityBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Plugins.ECSPowerNetcode.EntityBulderExtensions; 3 | using Plugins.ECSPowerNetcode.Features.NetworkEntities; 4 | 5 | namespace Plugins.ECSPowerNetcode.Client.Entities 6 | { 7 | public abstract class ClientNetworkEntityBuilder : NetcodeEntityBuilder 8 | { 9 | protected ClientNetworkEntityBuilder(uint networkEntityId) 10 | { 11 | if (networkEntityId == 0) 12 | throw new NotImplementedException("networkEntityId isn't set"); 13 | 14 | AddComponentData(new NetworkEntity {networkEntityId = networkEntityId}); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Client/Entities/ClientNetworkEntityBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d742591e250a443eb984c3a60b31b862 3 | timeCreated: 1590143670 -------------------------------------------------------------------------------- /Client/Entities/ClientNetworkEntitySystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client.Groups; 2 | using Plugins.ECSPowerNetcode.Features.NetworkEntities; 3 | using Unity.Entities; 4 | using Unity.NetCode; 5 | 6 | namespace Plugins.ECSPowerNetcode.Client.Entities 7 | { 8 | [UpdateInGroup(typeof(ClientNetworkEntitySystemGroup))] 9 | [UpdateInWorld(UpdateInWorld.TargetWorld.Client)] 10 | public class ClientNetworkEntitySystem : ComponentSystem 11 | { 12 | protected override void OnUpdate() 13 | { 14 | Entities 15 | .WithAll() 16 | .WithNone() 17 | .ForEach((Entity entity, ref NetworkEntity networkEntity) => 18 | { 19 | ClientManager.Instance.NetworkEntityManager.Add(networkEntity.networkEntityId, entity); 20 | PostUpdateCommands.AddComponent(entity, new NetworkEntityRegistered {networkEntityId = networkEntity.networkEntityId}); 21 | }); 22 | 23 | Entities 24 | .WithAll() 25 | .WithNone() 26 | .ForEach((Entity entity, ref NetworkEntityRegistered networkEntity) => 27 | { 28 | ClientManager.Instance.NetworkEntityManager.Remove(networkEntity.networkEntityId); 29 | PostUpdateCommands.RemoveComponent(entity); 30 | }); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Client/Entities/ClientNetworkEntitySystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ce6ff3900ca44c95aadbeb388bc9fd11 3 | timeCreated: 1589714105 -------------------------------------------------------------------------------- /Client/Groups.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3964eb7361b42a180cbad32d9f8efb4 3 | timeCreated: 1589810232 -------------------------------------------------------------------------------- /Client/Groups/ClientConnectionSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | using Unity.NetCode; 3 | using Unity.Transforms; 4 | 5 | namespace Plugins.ECSPowerNetcode.Client.Groups 6 | { 7 | [UpdateInGroup(typeof(ClientSimulationSystemGroup))] 8 | [UpdateBefore(typeof(TransformSystemGroup))] 9 | public class ClientConnectionSystemGroup : ComponentSystemGroup 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /Client/Groups/ClientConnectionSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 29fefe62ed204f3b9beb1167c3d77ad0 3 | timeCreated: 1589816813 -------------------------------------------------------------------------------- /Client/Groups/ClientGameSimulationSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | using Unity.NetCode; 3 | using Unity.Transforms; 4 | 5 | namespace Plugins.ECSPowerNetcode.Client.Groups 6 | { 7 | [UpdateInGroup(typeof(ClientSimulationSystemGroup))] 8 | [UpdateBefore(typeof(TransformSystemGroup))] 9 | public class ClientGameSimulationSystemGroup : ComponentSystemGroup 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /Client/Groups/ClientGameSimulationSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b92b17071c0c3314aa9798ad459c9060 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Client/Groups/ClientNetworkEntitySystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | using Unity.NetCode; 3 | 4 | namespace Plugins.ECSPowerNetcode.Client.Groups 5 | { 6 | [UpdateInGroup(typeof(ClientInitializationSystemGroup))] 7 | public class ClientNetworkEntitySystemGroup : ComponentSystemGroup 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /Client/Groups/ClientNetworkEntitySystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5297e2d1d73e45f3a94a7fa92e4122e0 3 | timeCreated: 1589810310 -------------------------------------------------------------------------------- /Client/Groups/ClientRequestProcessingSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | using Unity.NetCode; 3 | using Unity.Transforms; 4 | 5 | namespace Plugins.ECSPowerNetcode.Client.Groups 6 | { 7 | [UpdateInGroup(typeof(ClientSimulationSystemGroup))] 8 | [UpdateAfter(typeof(ClientConnectionSystemGroup))] 9 | [UpdateBefore(typeof(TransformSystemGroup))] 10 | public class ClientRequestProcessingSystemGroup : ComponentSystemGroup 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /Client/Groups/ClientRequestProcessingSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 531ea4cef99547db82afd7b820382d7e 3 | timeCreated: 1589810275 -------------------------------------------------------------------------------- /Client/Lifecycle.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 486008f5503a4a71a62edb2c9f449c2b 3 | timeCreated: 1589816931 -------------------------------------------------------------------------------- /Client/Lifecycle/ClientConnectToServerSystem.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Plugins.ECSPowerNetcode.Client.Components; 3 | using Plugins.ECSPowerNetcode.Client.Groups; 4 | using Unity.Collections; 5 | using Unity.Entities; 6 | using Unity.NetCode; 7 | using Unity.Networking.Transport; 8 | using UnityEngine; 9 | 10 | namespace Plugins.ECSPowerNetcode.Client.Lifecycle 11 | { 12 | [UpdateInGroup(typeof(ClientConnectionSystemGroup))] 13 | public class ClientConnectToServerSystem : ComponentSystem 14 | { 15 | protected override void OnCreate() 16 | { 17 | RequireSingletonForUpdate(); 18 | } 19 | 20 | protected override void OnUpdate() 21 | { 22 | var connectToServer = GetSingleton(); 23 | EntityManager.DestroyEntity(GetSingletonEntity()); 24 | 25 | var network = World.GetExistingSystem(); 26 | 27 | NetworkEndPoint ep = NetworkEndPoint.LoopbackIpv4; 28 | ep.Port = connectToServer.port; 29 | 30 | var address = IPAddress.Parse(connectToServer.host.ToString()); 31 | ep.SetRawAddressBytes(new NativeArray(address.GetAddressBytes(), Allocator.Temp)); 32 | 33 | network.Connect(ep); 34 | 35 | Debug.Log($"[Client] Connecting to server on {connectToServer.host.ToString()}:{ep.Port}"); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /Client/Lifecycle/ClientConnectToServerSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0e338571786149cdabb9a30db9b3b6af 3 | timeCreated: 1589708789 -------------------------------------------------------------------------------- /Client/Lifecycle/ClientConnectedSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSEntityBuilder; 2 | using Plugins.ECSPowerNetcode.Client.Components; 3 | using Plugins.ECSPowerNetcode.Client.Groups; 4 | using Unity.Entities; 5 | using Unity.NetCode; 6 | using UnityEngine; 7 | 8 | namespace Plugins.ECSPowerNetcode.Client.Lifecycle 9 | { 10 | [UpdateInGroup(typeof(ClientConnectionSystemGroup))] 11 | [UpdateAfter(typeof(ClientConnectToServerSystem))] 12 | public class ClientConnectedSystem : ComponentSystem 13 | { 14 | protected override void OnUpdate() 15 | { 16 | Entities 17 | .WithNone() 18 | .ForEach((Entity connectionEntity, ref NetworkIdComponent id) => 19 | { 20 | Debug.Log($"[Client] Connected to server. Network id = [{id.Value}]"); 21 | 22 | EntityWrapper.Wrap(connectionEntity, EntityManager) 23 | .SetName($"ServerConnection_{id.Value}"); 24 | 25 | var commandHandler = EntityWrapper.CreateEntity(EntityManager) 26 | .AddComponent() 27 | .SetName($"ServerConnection_{id.Value}_CommandHandler") 28 | .Entity; 29 | 30 | ClientManager.Instance.OnConnectedToServer(connectionEntity, commandHandler, id.Value); 31 | 32 | PostUpdateCommands.SetComponent(connectionEntity, new CommandTargetComponent {targetEntity = commandHandler}); 33 | PostUpdateCommands.AddComponent(connectionEntity); 34 | }); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Client/Lifecycle/ClientConnectedSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2123c84878474b9a86c0a50cc71496ad 3 | timeCreated: 1589389530 -------------------------------------------------------------------------------- /Client/Lifecycle/ClientDisconnectedSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client.Groups; 2 | using Unity.Entities; 3 | using Unity.NetCode; 4 | using UnityEngine; 5 | 6 | namespace Plugins.ECSPowerNetcode.Client.Lifecycle 7 | { 8 | [UpdateInGroup(typeof(ClientConnectionSystemGroup))] 9 | public class ClientDisconnectedSystem : ComponentSystem 10 | { 11 | protected override void OnUpdate() 12 | { 13 | Entities 14 | .WithAll() 15 | .ForEach((Entity entity, ref NetworkStreamDisconnected disconnected, ref CommandTargetComponent commandTargetComponent) => 16 | { 17 | PostUpdateCommands.DestroyEntity(commandTargetComponent.targetEntity); 18 | ClientManager.Instance.OnDisconnectedFromServer(); 19 | 20 | Debug.Log($"[Client] Disconnected from server. Reason: {disconnected.Reason}"); 21 | }); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Client/Lifecycle/ClientDisconnectedSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6bfa25efa49f4b14abbcad1213605714 3 | timeCreated: 1589811190 -------------------------------------------------------------------------------- /Client/Packets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c8026ec00a9e469a993a955db1910a8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Client/Packets/AClientReceiveRpcCommandSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client.Groups; 2 | using Plugins.ECSPowerNetcode.Shared; 3 | using Unity.Entities; 4 | using Unity.NetCode; 5 | 6 | namespace Plugins.ECSPowerNetcode.Client.Packets 7 | { 8 | [UpdateInGroup(typeof(ClientRequestProcessingSystemGroup))] 9 | [UpdateInWorld(UpdateInWorld.TargetWorld.Client)] 10 | public abstract class AClientReceiveRpcCommandSystem : ComponentSystem where T : struct, IComponentData 11 | { 12 | protected virtual bool ShouldDestroyEntity { get; } = true; 13 | 14 | protected abstract void OnCommand(ref T packet, ConnectionDescription connectionToServer); 15 | 16 | protected override void OnUpdate() 17 | { 18 | Entities 19 | .ForEach((Entity entity, ref T command, ref ReceiveRpcCommandRequestComponent requestComponent) => 20 | { 21 | var connectionDescription = ClientManager.Instance.ConnectionToServer; 22 | 23 | if (ShouldDestroyEntity) 24 | PostUpdateCommands.DestroyEntity(entity); 25 | 26 | OnCommand(ref command, connectionDescription); 27 | }); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Client/Packets/AClientReceiveRpcCommandSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c1793657e0124a2ba373acf9a8ebae25 3 | timeCreated: 1590233984 -------------------------------------------------------------------------------- /Client/Packets/ClientToServerRpcCommandBuilder.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.EntityBulderExtensions; 2 | using Unity.Entities; 3 | using Unity.NetCode; 4 | 5 | namespace Plugins.ECSPowerNetcode.Client.Packets 6 | { 7 | public class ClientToServerRpcCommandBuilder : NetcodeEntityBuilder 8 | { 9 | public static ClientToServerRpcCommandBuilder Send(T command) where T : struct, IComponentData 10 | { 11 | var builder = new ClientToServerRpcCommandBuilder(); 12 | builder.AddComponentData(command) 13 | .AddComponentData(new SendRpcCommandRequestComponent {TargetConnection = ClientManager.Instance.ConnectionToServer.connectionEntity}) 14 | .SetName($"RpcCommand {typeof(T).Name}"); 15 | 16 | return builder; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Client/Packets/ClientToServerRpcCommandBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 007d77de651c469188a5fdb5d28e9eeb 3 | timeCreated: 1589384864 -------------------------------------------------------------------------------- /Client/Packets/INetworkEntityCopyRpcCommand.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Client.Packets 4 | { 5 | public interface INetworkEntityCopyRpcCommand : IComponentData 6 | { 7 | uint NetworkEntityId { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /Client/Packets/INetworkEntityCopyRpcCommand.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9806d8e92af641908b4fc3b946702875 3 | timeCreated: 1589807367 -------------------------------------------------------------------------------- /Debugging.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c8d6438b823489e9290755ab413caf9 3 | timeCreated: 1589971432 -------------------------------------------------------------------------------- /Debugging/ValidateHangingRpcCommandsSystem.cs: -------------------------------------------------------------------------------- 1 | #if ENABLE_ECS_POWER_NETCODE_DEBUGGER 2 | 3 | using Unity.Entities; 4 | using Unity.NetCode; 5 | using UnityEngine; 6 | 7 | namespace Plugins.ECSPowerNetcode.Debugging 8 | { 9 | public class ValidateHangingRpcCommandsSystem : ComponentSystem 10 | { 11 | private struct RpcCommandAdded : ISystemStateComponentData 12 | { 13 | public double timeAdded; 14 | } 15 | 16 | private struct RpcCommandUserNotified : ISystemStateComponentData 17 | { 18 | } 19 | 20 | protected override void OnUpdate() 21 | { 22 | Entities 23 | .WithAll() 24 | .WithNone() 25 | .ForEach((Entity entity, ref RpcCommandAdded rpcCommandAdded) => 26 | { 27 | if (Time.ElapsedTime - rpcCommandAdded.timeAdded > 1.0f) 28 | { 29 | Debug.LogWarning($"[NetworkDebugger] There is a hanging RPC entity: {entity} in {World.Name} world"); 30 | PostUpdateCommands.AddComponent(entity); 31 | } 32 | }); 33 | 34 | Entities 35 | .WithAll() 36 | .WithNone() 37 | .ForEach(entity => 38 | { 39 | PostUpdateCommands.AddComponent(entity, new RpcCommandAdded 40 | { 41 | timeAdded = Time.ElapsedTime 42 | }); 43 | }); 44 | 45 | Entities 46 | .WithAll() 47 | .WithNone() 48 | .ForEach(entity => { PostUpdateCommands.RemoveComponent(entity); }); 49 | } 50 | } 51 | } 52 | 53 | #endif -------------------------------------------------------------------------------- /Debugging/ValidateHangingRpcCommandsSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b5495c3d07fe45ed8b9b08f02f601db9 3 | timeCreated: 1589971460 -------------------------------------------------------------------------------- /ECSPowerNetcode.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ECSPowerNetcode", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:2665a8d13d1b3f18800f46e256720795", 6 | "GUID:e0cd26848372d4e5c891c569017e11f1", 7 | "GUID:734d92eba21c94caba915361bd5ac177", 8 | "GUID:c38fb62397af04c51b143415e1db6d90", 9 | "GUID:d8b63aba1907145bea998dd612889d6b", 10 | "GUID:953adc2a6b8b4e3c8df5b728bcd546e9", 11 | "GUID:f2d49d9fa7e7eb3418e39723a7d3b92f", 12 | "GUID:a5baed0c9693541a5bd947d336ec7659", 13 | "GUID:44dcb2bf5b9be4b5aa76674c8edcd1d5", 14 | "GUID:b302dc12629081840aafa9de750c41e0" 15 | ], 16 | "includePlatforms": [], 17 | "excludePlatforms": [], 18 | "allowUnsafeCode": false, 19 | "overrideReferences": false, 20 | "precompiledReferences": [], 21 | "autoReferenced": true, 22 | "defineConstraints": [], 23 | "versionDefines": [], 24 | "noEngineReferences": false 25 | } -------------------------------------------------------------------------------- /ECSPowerNetcode.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: abe08cfe707fc8541a08247cedc36bc8 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /ECSPowerNetcodeModule.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Archetypes; 2 | using Plugins.ECSPowerNetcode.Client; 3 | using Plugins.ECSPowerNetcode.Server; 4 | using Plugins.ECSPowerNetcode.Worlds; 5 | using Unity.Collections; 6 | using Unity.Entities; 7 | using UnityEngine; 8 | using UnityEngine.LowLevel; 9 | 10 | namespace Plugins.ECSPowerNetcode 11 | { 12 | public static class ECSPowerNetcodeModule 13 | { 14 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 15 | public static void Clean() 16 | { 17 | // clean module 18 | NetcodeEntityArchetypeManager.Reset(); 19 | EntityWorldManager.Reset(); 20 | ClientManager.Reset(); 21 | ServerManager.Reset(); 22 | } 23 | 24 | public static void Shutdown() 25 | { 26 | // shutdown ECS 27 | var playerLoop = PlayerLoop.GetCurrentPlayerLoop(); 28 | foreach (var w in World.All) 29 | ScriptBehaviourUpdateOrder.RemoveWorldFromPlayerLoop(w, ref playerLoop); 30 | 31 | PlayerLoop.SetPlayerLoop(playerLoop); 32 | 33 | World.DisposeAllWorlds(); 34 | 35 | WordStorage.Instance.Dispose(); 36 | WordStorage.Instance = null; 37 | 38 | // clean module 39 | Clean(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /ECSPowerNetcodeModule.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f620efe67e114682bb33f3ed6ea8ddd0 3 | timeCreated: 1608165189 -------------------------------------------------------------------------------- /EntityBulderExtensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3bd319d192254a1fbc02264556f80e63 3 | timeCreated: 1604752085 -------------------------------------------------------------------------------- /EntityBulderExtensions/CreateFromNetcodeArchetypeStrategy.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSEntityBuilder; 2 | using Plugins.ECSEntityBuilder.Archetypes; 3 | using Plugins.ECSEntityBuilder.InstantiationStrategy; 4 | using Plugins.ECSEntityBuilder.Variables; 5 | using Plugins.ECSPowerNetcode.Archetypes; 6 | using Plugins.ECSPowerNetcode.Worlds; 7 | using Unity.Entities; 8 | 9 | namespace Plugins.ECSPowerNetcode.EntityBulderExtensions 10 | { 11 | public class CreateFromNetcodeArchetypeStrategy : IEntityCreationStrategy where T : IArchetypeDescriptor 12 | { 13 | private readonly WorldType m_worldType; 14 | 15 | public CreateFromNetcodeArchetypeStrategy(WorldType worldType) 16 | { 17 | m_worldType = worldType; 18 | } 19 | 20 | public Entity Create(EntityManagerWrapper wrapper, EntityVariableMap variables) 21 | { 22 | return wrapper.CreateEntity(NetcodeEntityArchetypeManager.Instance.GetOrCreateArchetype(m_worldType)); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /EntityBulderExtensions/CreateFromNetcodeArchetypeStrategy.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 65d101f3036447958bd31edf25627669 3 | timeCreated: 1585334269 -------------------------------------------------------------------------------- /EntityBulderExtensions/NetcodeEntityBuilder.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSEntityBuilder; 2 | using Plugins.ECSEntityBuilder.Archetypes; 3 | using Plugins.ECSPowerNetcode.Worlds; 4 | using Unity.Entities; 5 | 6 | namespace Plugins.ECSPowerNetcode.EntityBulderExtensions 7 | { 8 | public class NetcodeEntityBuilder : EntityBuilder 9 | { 10 | public new void CreateFromArchetype() 11 | where T : IArchetypeDescriptor 12 | { 13 | SetCreationStrategy(new CreateFromNetcodeArchetypeStrategy(WorldType.DEFAULT)); 14 | } 15 | 16 | public void CreateFromArchetype(WorldType worldType) 17 | where T : IArchetypeDescriptor 18 | { 19 | SetCreationStrategy(new CreateFromNetcodeArchetypeStrategy(worldType)); 20 | } 21 | 22 | public Entity Build(WorldType worldType) 23 | { 24 | return Build(new EntityManagerWrapper(EntityWorldManager.Instance.GetWorldByType(worldType).EntityManager)); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /EntityBulderExtensions/NetcodeEntityBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 711c867ff11f4e66ba912403bacc8932 3 | timeCreated: 1604751890 -------------------------------------------------------------------------------- /EntityBulderExtensions/NetcodeEntityManagerWrapper.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSEntityBuilder; 2 | using Plugins.ECSPowerNetcode.Worlds; 3 | using Unity.Entities; 4 | 5 | namespace Plugins.ECSPowerNetcode.EntityBulderExtensions 6 | { 7 | public static class NetcodeEntityManagerWrapper 8 | { 9 | public static EntityManagerWrapper FromWorld(WorldType entityWorldType) 10 | { 11 | switch (entityWorldType) 12 | { 13 | default: 14 | return new EntityManagerWrapper(World.DefaultGameObjectInjectionWorld.EntityManager); 15 | case WorldType.CLIENT: 16 | return new EntityManagerWrapper(EntityWorldManager.Instance.Client.EntityManager); 17 | case WorldType.SERVER: 18 | return new EntityManagerWrapper(EntityWorldManager.Instance.Server.EntityManager); 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /EntityBulderExtensions/NetcodeEntityManagerWrapper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b73dbdd51d3b4110aaf5252f121254d7 3 | timeCreated: 1604752248 -------------------------------------------------------------------------------- /EntityBulderExtensions/NetcodeEntityWrapper.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSEntityBuilder; 2 | using Plugins.ECSPowerNetcode.Worlds; 3 | using Unity.Entities; 4 | 5 | namespace Plugins.ECSPowerNetcode.EntityBulderExtensions 6 | { 7 | public static class NetcodeEntityWrapper 8 | { 9 | public static EntityWrapper Wrap(Entity entity, WorldType entityWorldType) 10 | { 11 | return new EntityWrapper(entity, NetcodeEntityManagerWrapper.FromWorld(entityWorldType)); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /EntityBulderExtensions/NetcodeEntityWrapper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 77aa3bce399b4ff1848837c0a5852991 3 | timeCreated: 1604752723 -------------------------------------------------------------------------------- /Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f5a5a40998744144affcea3a1093f8a7 3 | timeCreated: 1603974719 -------------------------------------------------------------------------------- /Extensions/CollectionExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Unity.Collections; 3 | 4 | namespace Plugins.ECSPowerNetcode.Extensions 5 | { 6 | public static class CollectionExtension 7 | { 8 | public static FixedListInt512 ToFixedListInt512(this IEnumerable array) 9 | { 10 | var list = new FixedListInt512(); 11 | foreach (var entry in array) 12 | list.AddNoResize(entry); 13 | return list; 14 | } 15 | 16 | public static int[] ToArray(this INativeList nativeList) 17 | { 18 | var array = new int[nativeList.Length]; 19 | for (var i = 0; i < nativeList.Length; i++) 20 | array[i] = nativeList[i]; 21 | return array; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Extensions/CollectionExtension.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 519ad51e00ec4681af2875d0f4c4ac6a 3 | timeCreated: 1604425453 -------------------------------------------------------------------------------- /Extensions/TimeExtensions.cs: -------------------------------------------------------------------------------- 1 | using Unity.Core; 2 | using UnityEngine; 3 | 4 | namespace Plugins.ECSPowerNetcode.Extensions 5 | { 6 | public static class TimeExtensions 7 | { 8 | public static ulong ElapsedTimeInMillis(this TimeData timeData) 9 | { 10 | return (ulong) (timeData.ElapsedTime * 1000); 11 | } 12 | 13 | public static uint ElapsedTimeInSeconds(this TimeData timeData) 14 | { 15 | return (uint) timeData.ElapsedTime; 16 | } 17 | 18 | public static ulong FixedTimeInMillis => (ulong) (Time.fixedTime * 1000); 19 | } 20 | } -------------------------------------------------------------------------------- /Extensions/TimeExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e61a16beccc14cb3b7c4cbd834daefbb 3 | timeCreated: 1603974728 -------------------------------------------------------------------------------- /Features.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e8ec3a7c4889444e95e76fa978fce99a 3 | timeCreated: 1590240034 -------------------------------------------------------------------------------- /Features/ManagedRpcCommands.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5a3cf2e714bb402699bf3ed97fe49082 3 | timeCreated: 1590240059 -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/AServerReceiveManagedRpcCommandSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Server.Packets; 2 | using Unity.Entities; 3 | using Unity.NetCode; 4 | 5 | namespace Plugins.ECSPowerNetcode.Features.ManagedRpcCommands 6 | { 7 | public abstract class AServerReceiveManagedRpcCommandSystem : ComponentSystem where T : struct, IManagedRpcCommand, IRpcCommand 8 | { 9 | protected virtual bool ShouldDestroyEntity { get; } = true; 10 | 11 | protected abstract int OnCommand(ref T command, ref ReceiveRpcCommandRequestComponent requestComponent); 12 | 13 | protected override void OnUpdate() 14 | { 15 | Entities 16 | .ForEach((Entity entity, ref T command, ref ReceiveRpcCommandRequestComponent requestComponent) => 17 | { 18 | if (ShouldDestroyEntity) 19 | PostUpdateCommands.DestroyEntity(entity); 20 | 21 | int result = OnCommand(ref command, ref requestComponent); 22 | ServerToClientRpcCommandBuilder 23 | .SendTo(requestComponent.SourceConnection, new ManagedRpcCommandResult 24 | { 25 | packetId = command.PacketId, 26 | result = result 27 | }) 28 | .Build(PostUpdateCommands); 29 | }); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/AServerReceiveManagedRpcCommandSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 882c29826deb4a81a48f9e50aebf9a2d 3 | timeCreated: 1590239684 -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/ClientReceiveManagedRpcCommandResultSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client; 2 | using Plugins.ECSPowerNetcode.Client.Packets; 3 | using Plugins.ECSPowerNetcode.Shared; 4 | using UnityEngine; 5 | 6 | namespace Plugins.ECSPowerNetcode.Features.ManagedRpcCommands 7 | { 8 | public class ClientReceiveManagedRpcCommandResultSystem : AClientReceiveRpcCommandSystem 9 | { 10 | protected override void OnCommand(ref ManagedRpcCommandResult packet, ConnectionDescription connectionToServer) 11 | { 12 | var entitiesToBeNotified = ClientManager.Instance.GetEntitiesWaitingForManagedPacket(packet.packetId); 13 | if (entitiesToBeNotified == null) 14 | { 15 | Debug.LogWarning($"[Client] No entities waiting for response registered for packet {packet.packetId}"); 16 | return; 17 | } 18 | 19 | foreach (var entity in entitiesToBeNotified) 20 | { 21 | PostUpdateCommands.AddComponent(entity, new ManagedRpcCommandResponse 22 | { 23 | packetId = packet.packetId, 24 | result = packet.result 25 | }); 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/ClientReceiveManagedRpcCommandResultSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 21afd223058c4227ba36439e729532f3 3 | timeCreated: 1590240176 -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/ClientToServerManagedRpcCommandBuilder.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client; 2 | using Plugins.ECSPowerNetcode.EntityBulderExtensions; 3 | using Unity.Entities; 4 | using Unity.NetCode; 5 | 6 | namespace Plugins.ECSPowerNetcode.Features.ManagedRpcCommands 7 | { 8 | public class ClientToServerManagedRpcCommandBuilder : NetcodeEntityBuilder 9 | { 10 | private readonly ulong m_packetId; 11 | 12 | public static ClientToServerManagedRpcCommandBuilder Send(T command) where T : struct, IManagedRpcCommand, IRpcCommand 13 | { 14 | var packetId = ClientManager.Instance.NextManagedPacketId; 15 | command.PacketId = packetId; 16 | 17 | var builder = new ClientToServerManagedRpcCommandBuilder(packetId); 18 | builder.AddComponentData(command) 19 | .AddComponentData(new SendRpcCommandRequestComponent {TargetConnection = ClientManager.Instance.ConnectionToServer.connectionEntity}) 20 | .SetName($"ManagedRpcCommand {typeof(T).Name}"); 21 | 22 | return builder; 23 | } 24 | 25 | public ClientToServerManagedRpcCommandBuilder(ulong packetId) 26 | { 27 | m_packetId = packetId; 28 | } 29 | 30 | public void AddEntityWaitingForResult(Entity entity) 31 | { 32 | ClientManager.Instance.AddEntityWaitingForManagedRpcCommandResult(entity, m_packetId); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/ClientToServerManagedRpcCommandBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1d0c6d4baef6446680df042fd0e51cc2 3 | timeCreated: 1590240545 -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/IManagedRpcCommand.cs: -------------------------------------------------------------------------------- 1 | using Unity.NetCode; 2 | 3 | namespace Plugins.ECSPowerNetcode.Features.ManagedRpcCommands 4 | { 5 | public interface IManagedRpcCommand : IRpcCommand 6 | { 7 | ulong PacketId { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/IManagedRpcCommand.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b1ea8cce4d254a9da9440f9cd6a8c285 3 | timeCreated: 1590239630 -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/ManagedRpcCommandResponse.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Features.ManagedRpcCommands 4 | { 5 | public struct ManagedRpcCommandResponse : IComponentData 6 | { 7 | public ulong packetId; 8 | public int result; 9 | } 10 | } -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/ManagedRpcCommandResponse.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 47a9a83251e44bfdbec81383b6a29019 3 | timeCreated: 1590241048 -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/ManagedRpcCommandResult.cs: -------------------------------------------------------------------------------- 1 | using Unity.Burst; 2 | using Unity.NetCode; 3 | 4 | namespace Plugins.ECSPowerNetcode.Features.ManagedRpcCommands 5 | { 6 | [BurstCompile] 7 | public struct ManagedRpcCommandResult : IRpcCommand 8 | { 9 | public ulong packetId; 10 | public int result; 11 | } 12 | } -------------------------------------------------------------------------------- /Features/ManagedRpcCommands/ManagedRpcCommandResult.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 02131d64b1b8425eb53de6742f3fb28d 3 | timeCreated: 1590239731 -------------------------------------------------------------------------------- /Features/NetworkEntities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: be191faabe7f4022a190a5fba7e73ea7 3 | timeCreated: 1590239405 -------------------------------------------------------------------------------- /Features/NetworkEntities/NetworkEntity.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Features.NetworkEntities 4 | { 5 | public struct NetworkEntity : IComponentData 6 | { 7 | public uint networkEntityId; 8 | } 9 | } -------------------------------------------------------------------------------- /Features/NetworkEntities/NetworkEntity.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 142812f8ba634200a1e2242b0158b21e 3 | timeCreated: 1589452453 -------------------------------------------------------------------------------- /Features/NetworkEntities/NetworkEntityRegistered.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Features.NetworkEntities 4 | { 5 | public struct NetworkEntityRegistered : ISystemStateComponentData 6 | { 7 | public uint networkEntityId; 8 | } 9 | } -------------------------------------------------------------------------------- /Features/NetworkEntities/NetworkEntityRegistered.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f269bb9510145c2949649b77ca98a39 3 | timeCreated: 1589714131 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Andrey Cherkashin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe1e998add714ae38d2bb8517dbeee01 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Prefabs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 691e52febfa5de5488a86b87587a2662 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Prefabs/Network.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &6381747876942137270 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 6381747876942137264} 12 | - component: {fileID: -6487455830700194190} 13 | - component: {fileID: -441121593477100115} 14 | m_Layer: 0 15 | m_Name: Network 16 | m_TagString: Untagged 17 | m_Icon: {fileID: 0} 18 | m_NavMeshLayer: 0 19 | m_StaticEditorFlags: 0 20 | m_IsActive: 1 21 | --- !u!4 &6381747876942137264 22 | Transform: 23 | m_ObjectHideFlags: 0 24 | m_CorrespondingSourceObject: {fileID: 0} 25 | m_PrefabInstance: {fileID: 0} 26 | m_PrefabAsset: {fileID: 0} 27 | m_GameObject: {fileID: 6381747876942137270} 28 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 29 | m_LocalPosition: {x: 0, y: 0, z: 0} 30 | m_LocalScale: {x: 1, y: 1, z: 1} 31 | m_Children: [] 32 | m_Father: {fileID: 0} 33 | m_RootOrder: 0 34 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 35 | --- !u!114 &-6487455830700194190 36 | MonoBehaviour: 37 | m_ObjectHideFlags: 0 38 | m_CorrespondingSourceObject: {fileID: 0} 39 | m_PrefabInstance: {fileID: 0} 40 | m_PrefabAsset: {fileID: 0} 41 | m_GameObject: {fileID: 6381747876942137270} 42 | m_Enabled: 1 43 | m_EditorHideFlags: 0 44 | m_Script: {fileID: 11500000, guid: 5c75b8787d8624691813514d3823a59f, type: 3} 45 | m_Name: 46 | m_EditorClassIdentifier: 47 | Ghosts: 48 | - prefab: {fileID: 5372516634151237887, guid: fa624df06de47f64daccfbbfedf9eed6, 49 | type: 3} 50 | enabled: 1 51 | --- !u!114 &-441121593477100115 52 | MonoBehaviour: 53 | m_ObjectHideFlags: 0 54 | m_CorrespondingSourceObject: {fileID: 0} 55 | m_PrefabInstance: {fileID: 0} 56 | m_PrefabAsset: {fileID: 0} 57 | m_GameObject: {fileID: 6381747876942137270} 58 | m_Enabled: 1 59 | m_EditorHideFlags: 0 60 | m_Script: {fileID: 11500000, guid: e739feae1d9c47c6a78209700d4fac47, type: 3} 61 | m_Name: 62 | m_EditorClassIdentifier: 63 | ConversionMode: 0 64 | ConversionTarget: -1 65 | canDestroy: 0 66 | -------------------------------------------------------------------------------- /Prefabs/Network.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e9075205cb06ceb4e97896ca65942938 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Prefabs/NetworkConnectionGhost.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &5372516634151237872 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 5372516634151237883} 12 | - component: {fileID: 5372516634151237887} 13 | - component: {fileID: 6759734984472540459} 14 | m_Layer: 0 15 | m_Name: NetworkConnectionGhost 16 | m_TagString: Untagged 17 | m_Icon: {fileID: 0} 18 | m_NavMeshLayer: 0 19 | m_StaticEditorFlags: 0 20 | m_IsActive: 1 21 | --- !u!4 &5372516634151237883 22 | Transform: 23 | m_ObjectHideFlags: 0 24 | m_CorrespondingSourceObject: {fileID: 0} 25 | m_PrefabInstance: {fileID: 0} 26 | m_PrefabAsset: {fileID: 0} 27 | m_GameObject: {fileID: 5372516634151237872} 28 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 29 | m_LocalPosition: {x: 0, y: 0, z: 0} 30 | m_LocalScale: {x: 1, y: 1, z: 1} 31 | m_Children: [] 32 | m_Father: {fileID: 0} 33 | m_RootOrder: 0 34 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 35 | --- !u!114 &5372516634151237887 36 | MonoBehaviour: 37 | m_ObjectHideFlags: 0 38 | m_CorrespondingSourceObject: {fileID: 0} 39 | m_PrefabInstance: {fileID: 0} 40 | m_PrefabAsset: {fileID: 0} 41 | m_GameObject: {fileID: 5372516634151237872} 42 | m_Enabled: 1 43 | m_EditorHideFlags: 0 44 | m_Script: {fileID: 11500000, guid: 7c79d771cedb4794bf100ce60df5f764, type: 3} 45 | m_Name: 46 | m_EditorClassIdentifier: 47 | ForcePrefabConversion: 0 48 | DefaultGhostMode: 0 49 | SupportedGhostModes: 3 50 | OptimizationMode: 0 51 | Importance: 1 52 | prefabId: fa624df06de47f64daccfbbfedf9eed6 53 | Name: NetworkConnectionGhost 54 | --- !u!114 &6759734984472540459 55 | MonoBehaviour: 56 | m_ObjectHideFlags: 0 57 | m_CorrespondingSourceObject: {fileID: 0} 58 | m_PrefabInstance: {fileID: 0} 59 | m_PrefabAsset: {fileID: 0} 60 | m_GameObject: {fileID: 5372516634151237872} 61 | m_Enabled: 1 62 | m_EditorHideFlags: 0 63 | m_Script: {fileID: 11500000, guid: 6f1aa6f7c21040f3b07bdc00d94070bd, type: 3} 64 | m_Name: 65 | m_EditorClassIdentifier: 66 | -------------------------------------------------------------------------------- /Prefabs/NetworkConnectionGhost.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fa624df06de47f64daccfbbfedf9eed6 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ECSPowerNetcode 2 | 3 | The library is made on top of the [Unity Netcode](https://docs.unity3d.com/Packages/com.unity.netcode@0.4/manual/index.html) package and saves you some time on configuring it / provides tools for client-server communication. 4 | 5 | [![](./.static/demo.png)](https://github.com/actionk/ECSPowerNetcodeDemo) 6 | 7 | # Table of Contents 8 | 9 | * [Install](#install) 10 | * [Dependencies](#dependencies) 11 | * [Getting started](#getting-started) 12 | * * [Starting a server and connecting to it locally](#starting-a-server-and-connecting-to-it-locally) 13 | * * [Connecting to a remote server](#connecting-to-a-remote-server) 14 | * * [Accessing connection's entities](#accessing-connections-entities) 15 | * * [Handlers](#handlers) 16 | * [Groups](#groups) 17 | * [Command builders](#command-builders) 18 | * [Command handlers](#command-handlers) 19 | * [Synchronizing entities](#synchronizing-entities) 20 | * * [Creating an entity on server side](#creating-an-entity-on-server-side) 21 | * * [Transferring the entity](#transferring-the-entity) 22 | * * [Creating the entity on the client side](#creating-the-entity-on-the-client-side) 23 | * * [Synchronizing the entity](#synchronizing-the-entity) 24 | * * [Destroying the entity](#destroying-the-entity) 25 | * * [Accessing the entities](#accessing-the-entities) 26 | * * [Customization](#customization) 27 | * [Synchronizing components](#synchronizing-components) 28 | * [Managed RPC commands](#managed-rpc-commands) 29 | 30 | # Demo 31 | 32 | I like to have this repo as a module without a unity project, so please go to another repo which I created just for a demo: [ECSPowerNetcodeDemo](https://github.com/actionk/ECSPowerNetcodeDemo). 33 | 34 | # Install 35 | 36 | You can either just put the files into `Assets/Plugins/ECSEntityBuilder` or use it as a submodule: 37 | ```sh 38 | git submodule add https://github.com/actionk/ECSPowerNetcode.git Assets/Plugins/ECSPowerNetcode 39 | ``` 40 | 41 | **Important!** 42 | After adding a plugin, please add the `Network` prefab into your scene from `ECSPowerNetcode/Prefabs` folder. This will enable networking. 43 | 44 | ## Dependencies 45 | 46 | The library depends on: 47 | * [UnityECSEntityBuilder](https://github.com/actionk/UnityECSEntityBuilder) 48 | * [Unity Netcode 0.3](https://docs.unity3d.com/Packages/com.unity.netcode@0.2/manual/index.html) 49 | 50 | These are required dependencies 51 | 52 | # Getting started 53 | 54 | ### Starting a server and connecting to it locally 55 | 56 | ```cs 57 | ServerManager.Instance.StartServer(7979); 58 | ClientManager.Instance.ConnectToServer(7979); 59 | ``` 60 | 61 | After doing so, the library will automatically establish a connection and create command handlers for each connection in both client & server world. 62 | 63 | ### Connecting to a remote server 64 | 65 | ```cs 66 | ClientManager.Instance.ConnectToServer(7979, "remote ip address"); 67 | ``` 68 | 69 | ### Accessing connection's entities 70 | 71 | Each connection is described by these parameters: 72 | 73 | ```cs 74 | public struct ConnectionDescription 75 | { 76 | public int networkId; // unique network id value for client-server connection 77 | public Entity connectionEntity; 78 | public Entity commandHandlerEntity; 79 | } 80 | ``` 81 | 82 | #### Client side 83 | 84 | `ClientManager.Instance.IsConnected` 85 | 86 | `ClientManager.Instance.ConnectionToServer` -> `ConnectionDescription` struct 87 | 88 | #### Server side 89 | 90 | `ServerManager.Instance.AllConnections` - getting list of all connected clients of `ConnectionDescription` struct 91 | 92 | `ServerManager.Instance.GetClientConnectionByNetworkId` 93 | 94 | ### Handlers 95 | 96 | #### Client 97 | 98 | You can set your handlers for `OnConnected` & `OnDisconnected` events: 99 | 100 | ```cs 101 | ClientManager.Instance.OnConnectedHandler += MyHandler; 102 | ClientManager.Instance.OnDisconnectedHandler += MyHandler; 103 | ``` 104 | 105 | #### Server 106 | 107 | You can set your handlers for `OnConnected` & `OnDisconnected` events for each player: 108 | 109 | ```cs 110 | public delegate void OnPlayerDisconnected(int networkConnectionId); 111 | 112 | ServerManager.Instance.OnPlayerConnectedHandler += MyHandler; 113 | ServerManager.Instance.OnPlayerDisconnectedHandler += MyHandler; 114 | ``` 115 | 116 | # Groups 117 | 118 | ### Client 119 | 120 | ```cs 121 | ClientConnectionSystemGroup 122 | ClientRequestProcessingSystemGroup 123 | ClientNetworkEntitySystemGroup 124 | ClientGameSimulationSystemGroup 125 | ``` 126 | 127 | | Group | Description | 128 | | --- | --- | 129 | | ClientConnectionSystemGroup | Connection/Disconnection from server | 130 | | ClientRequestProcessingSystemGroup | Processing requests from server | 131 | | ClientNetworkEntitySystemGroup | Processing network entities | 132 | | ClientGameSimulationSystemGroup | All your game simulations on client side | 133 | 134 | 135 | ### Server 136 | 137 | ```cs 138 | ServerConnectionSystemGroup 139 | ServerRequestProcessingSystemGroup 140 | ServerNetworkEntitySystemGroup 141 | ServerGameSimulationSystemGroup 142 | ``` 143 | 144 | | Group | Description | 145 | | --- | --- | 146 | | ServerConnectionSystemGroup | Connection/Disconnection from clients | 147 | | ServerRequestProcessingSystemGroup | Processing requests from clients | 148 | | ServerNetworkEntitySystemGroup | Processing network entities | 149 | | ServerGameSimulationSystemGroup | All your game simulations on server side | 150 | 151 | # Command builders 152 | 153 | For making your life easier, there are command builders for both client & server commands. 154 | First of all, you have to create an [IRpcCommand](https://docs.unity3d.com/Packages/com.unity.netcode@0.1/manual/getting-started.html) yourself. 155 | 156 | ### Client 157 | 158 | ```cs 159 | ClientToServerRpcCommandBuilder 160 | .Send(new ClientPlayerLoginCommand {localPlayerSide = PlayerManager.LocalPlayerSide.LEFT}) 161 | .Build(PostUpdateCommands); 162 | ``` 163 | 164 | Where `ClientPlayerLoginCommand` implements `IRpcCommand` 165 | 166 | ### Server 167 | 168 | 169 | You can specify which client to send the command to: 170 | 171 | ```cs 172 | ServerToClientRpcCommandBuilder 173 | .SendTo(clientConnectionEntity, command) 174 | .Build(PostUpdateCommands); 175 | 176 | ServerToClientRpcCommandBuilder 177 | .SendTo(networkConnectionId, command) 178 | .Build(PostUpdateCommands); 179 | ``` 180 | 181 | Or you can simply broadcast: 182 | 183 | ```cs 184 | ServerToClientRpcCommandBuilder 185 | .Broadcast(command) 186 | .Build(PostUpdateCommands); 187 | ``` 188 | 189 | # Command handlers 190 | 191 | ### Client 192 | 193 | Simply inherit from `AClientReceiveRpcCommandSystem` to implement a client command handler for RPC command of type `ServerPlayerLoginResponseCommand` (for example): 194 | 195 | ```cs 196 | public class ClientPlayerLoginResponseSystem : AClientReceiveRpcCommandSystem 197 | { 198 | protected override void OnCommand(ref ServerPlayerLoginResponseCommand command, ConnectionDescription clientConnection) 199 | { 200 | // process your command 201 | } 202 | } 203 | ``` 204 | 205 | ### Server 206 | 207 | Simply inherit from `AServerReceiveRpcCommandSystem` to implement a server command handler for RPC command of type `ClientDropItemCommand` (for example): 208 | 209 | ```cs 210 | public class ServerDropItemSystem : AServerReceiveRpcCommandSystem 211 | { 212 | protected override void OnCommand(ref ClientDropItemCommand command, ConnectionDescription clientConnection) 213 | { 214 | // process your command 215 | } 216 | } 217 | ``` 218 | 219 | # Synchronizing entities 220 | 221 | Usually your way of organizing entities in client-server architecture with ECS would look like that: 222 | 223 | ![](./.static/organizing_entities.png) 224 | 225 | That's for, the library provides you with a way of synchronizing entities without using ghost components: 226 | 227 | ![](./.static/synchronizing_entities.png) 228 | 229 | ### Creating an entity on server side 230 | 231 | You start with creating an entity builder by inheriting your builder from `ServerNetworkEntityBuilder`: 232 | 233 | ```cs 234 | public class ServerPlayerBuilder : ServerNetworkEntityBuilder 235 | { 236 | protected override ServerPlayerBuilder Self => this; 237 | 238 | public static ServerPlayerBuilder Create(int networkId, Entity connection, uint playerId, PlayerManager.LocalPlayerSide localPlayerSide) 239 | { 240 | return new ServerPlayerBuilder(networkId, connection, playerId, localPlayerSide); 241 | } 242 | 243 | private ServerPlayerBuilder(int networkId, Entity connection, uint playerId, PlayerManager.LocalPlayerSide localPlayerSide) : base() 244 | { 245 | CreateFromArchetype(WorldType.SERVER); 246 | SetComponentData(new Scale {Value = 1}); 247 | AddComponentData(new ServerPlayer 248 | { 249 | networkId = networkId, 250 | connection = connection, 251 | playerId = playerId, 252 | localPlayerSide = localPlayerSide 253 | }); 254 | } 255 | } 256 | ``` 257 | 258 | ### Transferring the entity 259 | 260 | Then, you create an RPC command to send the entity to the clients: 261 | 262 | ```cs 263 | [BurstCompile] 264 | public struct PlayerTransferCommand : INetworkEntityCopyRpcCommand, IRpcCommand 265 | { 266 | public ulong NetworkEntityId => networkEntityId; 267 | 268 | public int networkId; 269 | public ulong networkEntityId; 270 | public uint playerId; 271 | public PlayerManager.LocalPlayerSide localPlayerSide; 272 | public float3 position; 273 | } 274 | ``` 275 | 276 | Then, for creating the command, you create a system inherited from `AServerNetworkEntityTransferSystem`: 277 | 278 | ```cs 279 | [UpdateInGroup(typeof(ServerNetworkEntitySystemGroup))] 280 | public class ServerPlayerTransferSystem : AServerNetworkEntityTransferSystem 281 | { 282 | protected override PlayerTransferCommand CreateTransferCommandForEntity(Entity entity, NetworkEntity networkEntity, ServerPlayer selectorComponent) 283 | { 284 | return new PlayerTransferCommand 285 | { 286 | networkId = selectorComponent.networkId, 287 | networkEntityId = networkEntity.networkEntityId, 288 | playerId = selectorComponent.playerId, 289 | localPlayerSide = selectorComponent.localPlayerSide, 290 | position = EntityManager.GetComponentData(entity).Value 291 | }; 292 | } 293 | } 294 | ``` 295 | 296 | There are also options for selection more components (2 and 3), such as `AServerNetworkEntityTransferSystemT2` and `AServerNetworkEntityTransferSystemT3`. 297 | 298 | ### Creating the entity on the client side 299 | 300 | And the system for consuming this command on the client side: 301 | 302 | ```cs 303 | [UpdateInGroup(typeof(ClientEarlyUpdateSystemGroup))] 304 | public class ClientPlayerTransferSystem : AClientNetworkEntityTransferSystem 305 | { 306 | protected override void CreateNetworkEntity(ulong networkEntityId, PlayerTransferCommand command) 307 | { 308 | ClientPlayerBuilder 309 | .Create(command) 310 | .Build(EntityManager); 311 | } 312 | 313 | protected override void SynchronizeNetworkEntity(Entity entity, PlayerTransferCommand command) 314 | { 315 | EntityWrapper.Wrap(entity, PostUpdateCommands) 316 | .SetComponentData(new Translation {Value = command.position}); 317 | } 318 | } 319 | ``` 320 | 321 | That's it! When you server entity is created, it will be automatically transferred to the client side by using `TransferNetworkEntityToAllClients`, which is described below 322 | 323 | ### Synchronizing the entity 324 | 325 | You have two possibilities of controlling that: 326 | 327 | 1. By adding `TransferNetworkEntityToAllClients` component to your network entity. This will automatically send the transfer command to all the clients connected 328 | 329 | 2. By adding a buffer of `TransferNetworkEntityToClient` and specifing the client connection to send the entity to. You can use EntityWrapper to use an existing buffer or create one if it doesn't exist: 330 | 331 | ```cs 332 | EntityWrapper.Wrap(entity, EntityManager) 333 | .AddElementToBuffer(new TransferNetworkEntityToClient(reqSrcSourceConnection)); 334 | ``` 335 | 336 | ### Destroying the entity 337 | 338 | When you want to destroy the entity on the server and all the clients at the same time, you can just add a `ServerDestroy` component to server entity and it will be automatically destroyed on all the clients: 339 | 340 | ```cs 341 | PostUpdateCommands.AddComponent(myServerEntity); 342 | ``` 343 | 344 | ### Accessing the entities 345 | 346 | All: 347 | 348 | ```cs 349 | ClientManager.Instance.NetworkEntityManager.All 350 | ServerManager.Instance.NetworkEntityManager.All 351 | ``` 352 | 353 | By ID: 354 | 355 | ```cs 356 | ClientManager.Instance.NetworkEntityManager[networkEntityId] 357 | ServerManager.Instance.NetworkEntityManager[networkEntityId] 358 | ``` 359 | 360 | ### Customization 361 | 362 | You have two ways of customizing your network entities: 363 | 364 | #### Custom network entity manager 365 | 366 | The default network entity manager is `DefaultNetworkEntityManager` which implements `INetworkEntityManager`. You can just implement `INetworkEntityManager` on your own and set the entity manager for server/client: 367 | 368 | ```cs 369 | ClientManager.Instance.NetworkEntityManager = myEntityManager; 370 | ServerManager.Instance.NetworkEntityManager = myEntityManager; 371 | ``` 372 | 373 | #### Custom network entity id factory 374 | 375 | The default factory implementation is `DefaultNetworkEntityIdFactory`. However, you can also implement your own factory by implementing `INetworkEntityIdFactory` and replacing the default one: 376 | 377 | ```cs 378 | ServerManager.Instance.NetworkEntityIdFactory = myEntityManager; 379 | ``` 380 | 381 | As you can see, it only works for server-side as the server is the one who assign the IDs. 382 | 383 | # Synchronizing components 384 | 385 | As an alternatives to Unity Netcode's Ghosts, the lib provides a way of synchronizing components automatically from server to all clients. 386 | 387 | ### Defining a component 388 | 389 | Here is the example: 390 | 391 | ```cs 392 | [assembly: RegisterGenericComponentType(typeof(CopyEntityComponentRpcCommand))] 393 | 394 | namespace Entities.Players.Packets 395 | { 396 | public struct Velocity : IComponentData 397 | { 398 | public float3 value; 399 | 400 | public bool IsZero => math.lengthsq(value) <= 0.001f; 401 | } 402 | 403 | public struct VelocityConverter : ISyncEntityConverter 404 | { 405 | public Velocity velocity; 406 | public Velocity Value => velocity; 407 | 408 | public void Convert(Velocity value) 409 | { 410 | velocity = value; 411 | } 412 | 413 | public void Serialize(ref DataStreamWriter writer) 414 | { 415 | writer.WriteFloat(velocity.value.x); 416 | writer.WriteFloat(velocity.value.y); 417 | writer.WriteFloat(velocity.value.z); 418 | } 419 | 420 | public void Deserialize(ref DataStreamReader reader) 421 | { 422 | velocity.value = new float3( 423 | reader.ReadFloat(), 424 | reader.ReadFloat(), 425 | reader.ReadFloat() 426 | ); 427 | } 428 | } 429 | 430 | public class VelocityRpcCommandSender : RpcCommandSendSystem< 431 | CopyEntityComponentRpcCommand, 432 | CopyEntityComponentRpcCommand> 433 | { 434 | } 435 | } 436 | ``` 437 | 438 | This way when you add a `Velocity` component to an entity which also has `NetworkEntity` component, the `Velocity` component will be automatically synchronized. 439 | 440 | ### Triggering synchronization 441 | 442 | For triggering synchronization process for all the components on the entities, add `Synchronize` component to the entity. 443 | 444 | ### Transform synchronization 445 | 446 | You can also synchronize all transform components in one command by just adding `SyncTransformFromServerToClient` to any entity in server world which has `NetworkEntity` component as well. 447 | 448 | If you don't want to recieve such updates on the client side (for example, when you're controlling your character, you don't want to receive updates about his/her position), you can just add `IgnoreTransformCopyingFromServer` component to this entity in client world. 449 | 450 | # Managed RPC commands 451 | 452 | Sometimes you want to have control over the requests you send to server. For example, when you send a request to perform an action and you wait until the server confirms it. In this case, you need to get a response to that exact request you sent and react to that response. You can do that with so-called managed rpc commands. 453 | 454 | First of all, you should create your command which should implement `IManagedRpcCommand` instead of `IRPCCommand`. This interface adds a `PacketId` field that will be filled automatically. 455 | 456 | ### Client 457 | 458 | Then, you send a message to a server using `ClientToServerManagedRpcCommandBuilder`: 459 | 460 | ```cs 461 | ClientToServerManagedRpcCommandBuilder 462 | .Send(new MyManagedCommand()) 463 | .AddEntityWaitingForResult(myEntity) 464 | .Build(PostUpdateCommands) 465 | ``` 466 | 467 | ### Server 468 | 469 | To recieve the command on the server side and process it, you should create a system that implements `AServerReceiveManagedRpcCommandSystem`: 470 | 471 | ```cs 472 | class MyCommandRecieveSystem : AServerReceiveManagedRpcCommandSystem { 473 | protected int OnCommand(ref T command, ref ReceiveRpcCommandRequestComponent requestComponent) { 474 | // process command 475 | return (int)MyStatusEnum.SUCCESS; 476 | } 477 | } 478 | ``` 479 | 480 | `OnCommand` expects you to return a status int that will be sent back to the client. 481 | 482 | Once you respond, the response will be sent back to the client and added to the entity which is waiting for it: `.AddEntityWaitingForResult(myEntity)`. 483 | The added component is described below: 484 | 485 | ```cs 486 | public struct ManagedRpcCommandResponse : IComponentData 487 | { 488 | public ulong packetId; 489 | public int result; 490 | } 491 | ``` 492 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: afe20f7a4f3504e97baa54ae247c78a5 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Server.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b7e039432f24f1b8c1dc64ee5c20a41 3 | timeCreated: 1589632867 -------------------------------------------------------------------------------- /Server/Components.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 015055eb0a014309b446b4279e6554d6 3 | timeCreated: 1589708922 -------------------------------------------------------------------------------- /Server/Components/ServerToClientCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Components 4 | { 5 | public struct ServerToClientCommandHandler : IComponentData 6 | { 7 | public Entity connectionEntity; 8 | public uint lastProcessesMovementInTick; 9 | } 10 | } -------------------------------------------------------------------------------- /Server/Components/ServerToClientCommandHandler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: baaed436ddf84aa98a9457e6d138d586 3 | timeCreated: 1589372543 -------------------------------------------------------------------------------- /Server/Components/StartServer.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Components 4 | { 5 | public struct StartServer : IComponentData 6 | { 7 | public ushort port; 8 | } 9 | } -------------------------------------------------------------------------------- /Server/Components/StartServer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e39f8c0aebca4d049cf33867ec3849db 3 | timeCreated: 1589708720 -------------------------------------------------------------------------------- /Server/Components/TransferNetworkEntityToAllClients.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Components 4 | { 5 | public struct TransferNetworkEntityToAllClients : IComponentData 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /Server/Components/TransferNetworkEntityToAllClients.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f6d0ece1c8d443e8ab87df73523b5bc9 3 | timeCreated: 1589814775 -------------------------------------------------------------------------------- /Server/Components/TransferNetworkEntityToClient.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Components 4 | { 5 | public struct TransferNetworkEntityToClient : IBufferElementData 6 | { 7 | public Entity clientConnection; 8 | 9 | public TransferNetworkEntityToClient(Entity clientConnection) 10 | { 11 | this.clientConnection = clientConnection; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Server/Components/TransferNetworkEntityToClient.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c8f124fe0de40129117d68e53e3e779 3 | timeCreated: 1589814748 -------------------------------------------------------------------------------- /Server/Destroying.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ca028eddd6224fdea62a349e67c3a446 3 | timeCreated: 1590235962 -------------------------------------------------------------------------------- /Server/Destroying/AServerNetworkEntityDestroySystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Features.NetworkEntities; 2 | using Plugins.ECSPowerNetcode.Server.Groups; 3 | using Plugins.ECSPowerNetcode.Server.Packets; 4 | using Unity.Entities; 5 | 6 | namespace Plugins.ECSPowerNetcode.Server.Destroying 7 | { 8 | [UpdateInGroup(typeof(ServerCleanupSystemGroup))] 9 | public abstract class AServerNetworkEntityDestroySystem : ComponentSystem 10 | { 11 | protected abstract void OnDestroyEntity(uint networkEntityId, Entity entity); 12 | 13 | protected override void OnUpdate() 14 | { 15 | Entities 16 | .ForEach((Entity entity, ref NetworkEntity networkEntity, ref ServerDestroy destroy) => 17 | { 18 | ServerToClientRpcCommandBuilder 19 | .Broadcast(new ServerNetworkEntityDestroyCommand 20 | { 21 | networkEntityId = networkEntity.networkEntityId 22 | }) 23 | .Build(PostUpdateCommands); 24 | 25 | OnDestroyEntity(networkEntity.networkEntityId, entity); 26 | }); 27 | 28 | 29 | Entities 30 | .WithNone() 31 | .ForEach((Entity entity, ref ServerDestroy destroy) => { PostUpdateCommands.DestroyEntity(entity); }); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Server/Destroying/AServerNetworkEntityDestroySystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cf0f1a6869004678ade702100f0724d0 3 | timeCreated: 1590235967 -------------------------------------------------------------------------------- /Server/Destroying/ServerDestroy.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Destroying 4 | { 5 | public struct ServerDestroy : IComponentData 6 | { 7 | } 8 | } -------------------------------------------------------------------------------- /Server/Destroying/ServerDestroy.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 04e3c3e1284543b7b6886500df56740d 3 | timeCreated: 1590235967 -------------------------------------------------------------------------------- /Server/Destroying/ServerNetworkEntityDestroyCommand.cs: -------------------------------------------------------------------------------- 1 | using Unity.Burst; 2 | using Unity.NetCode; 3 | 4 | namespace Plugins.ECSPowerNetcode.Server.Destroying 5 | { 6 | [BurstCompile] 7 | public struct ServerNetworkEntityDestroyCommand : IRpcCommand 8 | { 9 | public uint networkEntityId; 10 | } 11 | } -------------------------------------------------------------------------------- /Server/Destroying/ServerNetworkEntityDestroyCommand.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b277eacd0f22493792e2ea90cbaf134f 3 | timeCreated: 1590236105 -------------------------------------------------------------------------------- /Server/Entities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c9e9e47c4c04fb291bd3255582ee540 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Server/Entities/AServerNetworkEntityBuilderSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client.Packets; 2 | using Plugins.ECSPowerNetcode.Features.NetworkEntities; 3 | using Plugins.ECSPowerNetcode.Server.Components; 4 | using Plugins.ECSPowerNetcode.Server.Groups; 5 | using Plugins.ECSPowerNetcode.Server.Packets; 6 | using Unity.Entities; 7 | using Unity.NetCode; 8 | 9 | namespace Plugins.ECSPowerNetcode.Server.Entities 10 | { 11 | [UpdateInGroup(typeof(ServerRequestProcessingSystemGroup))] 12 | [UpdateInWorld(UpdateInWorld.TargetWorld.Server)] 13 | public abstract class AServerNetworkEntityBuilderSystem : ComponentSystem 14 | where TSelector : struct, IComponentData 15 | where TCommand : struct, INetworkEntityCopyRpcCommand 16 | { 17 | protected abstract TCommand CreateTransferCommandForEntity(Entity entity, ref NetworkEntity networkEntity, ref TSelector selectorComponent); 18 | 19 | protected override void OnUpdate() 20 | { 21 | Entities 22 | .WithAll() 23 | .ForEach((Entity entity, ref NetworkEntity networkEntity, ref TSelector selectorComponent) => 24 | { 25 | if (ServerManager.Instance.HasConnections) 26 | { 27 | var command = CreateTransferCommandForEntity(entity, ref networkEntity, ref selectorComponent); 28 | ServerToClientRpcCommandBuilder 29 | .Broadcast(command) 30 | .Build(PostUpdateCommands); 31 | } 32 | 33 | PostUpdateCommands.RemoveComponent(entity); 34 | }); 35 | 36 | Entities 37 | .WithAll() 38 | .ForEach((Entity entity, DynamicBuffer clients, ref NetworkEntity networkEntity, ref TSelector selectorComponent) => 39 | { 40 | if (ServerManager.Instance.HasConnections) 41 | { 42 | var command = CreateTransferCommandForEntity(entity, ref networkEntity, ref selectorComponent); 43 | foreach (var clientEntity in clients) 44 | { 45 | ServerToClientRpcCommandBuilder 46 | .SendTo(clientEntity.clientConnection, command) 47 | .Build(PostUpdateCommands); 48 | } 49 | } 50 | 51 | PostUpdateCommands.RemoveComponent(entity); 52 | }); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /Server/Entities/AServerNetworkEntityBuilderSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7dd0aabcea864fd58b32616e5ce615ff 3 | timeCreated: 1589814706 -------------------------------------------------------------------------------- /Server/Entities/AServerNetworkEntityBuilderSystemT2.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client.Packets; 2 | using Plugins.ECSPowerNetcode.Features.NetworkEntities; 3 | using Plugins.ECSPowerNetcode.Server.Components; 4 | using Plugins.ECSPowerNetcode.Server.Groups; 5 | using Plugins.ECSPowerNetcode.Server.Packets; 6 | using Unity.Entities; 7 | using Unity.NetCode; 8 | 9 | namespace Plugins.ECSPowerNetcode.Server.Entities 10 | { 11 | [UpdateInGroup(typeof(ServerRequestProcessingSystemGroup))] 12 | [UpdateInWorld(UpdateInWorld.TargetWorld.Server)] 13 | public abstract class AServerNetworkEntityBuilderSystemT2 : ComponentSystem 14 | where TSelector : struct, IComponentData 15 | where TSelector2 : struct, IComponentData 16 | where TCommand : struct, INetworkEntityCopyRpcCommand 17 | { 18 | protected abstract TCommand CreateTransferCommandForEntity(Entity entity, ref NetworkEntity networkEntity, ref TSelector selectorComponent, 19 | ref TSelector2 selectorComponent2); 20 | 21 | protected override void OnUpdate() 22 | { 23 | Entities 24 | .WithAll() 25 | .ForEach((Entity entity, ref NetworkEntity networkEntity, ref TSelector selectorComponent, ref TSelector2 selectorComponent2) => 26 | { 27 | if (ServerManager.Instance.HasConnections) 28 | { 29 | var command = CreateTransferCommandForEntity(entity, ref networkEntity, ref selectorComponent, ref selectorComponent2); 30 | ServerToClientRpcCommandBuilder 31 | .Broadcast(command) 32 | .Build(PostUpdateCommands); 33 | } 34 | 35 | PostUpdateCommands.RemoveComponent(entity); 36 | }); 37 | 38 | Entities 39 | .WithAll() 40 | .ForEach((Entity entity, DynamicBuffer clients, ref NetworkEntity networkEntity, ref TSelector selectorComponent, 41 | ref TSelector2 selectorComponent2) => 42 | { 43 | if (ServerManager.Instance.HasConnections) 44 | { 45 | var command = CreateTransferCommandForEntity(entity, ref networkEntity, ref selectorComponent, ref selectorComponent2); 46 | foreach (var clientEntity in clients) 47 | { 48 | ServerToClientRpcCommandBuilder 49 | .SendTo(clientEntity.clientConnection, command) 50 | .Build(PostUpdateCommands); 51 | } 52 | } 53 | 54 | PostUpdateCommands.RemoveComponent(entity); 55 | }); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Server/Entities/AServerNetworkEntityBuilderSystemT2.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cab366dfcaef4b0a96e1f0dffd8d4855 3 | timeCreated: 1590143218 -------------------------------------------------------------------------------- /Server/Entities/AServerNetworkEntityBuilderSystemT3.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Client.Packets; 2 | using Plugins.ECSPowerNetcode.Features.NetworkEntities; 3 | using Plugins.ECSPowerNetcode.Server.Components; 4 | using Plugins.ECSPowerNetcode.Server.Groups; 5 | using Plugins.ECSPowerNetcode.Server.Packets; 6 | using Unity.Entities; 7 | using Unity.NetCode; 8 | 9 | namespace Plugins.ECSPowerNetcode.Server.Entities 10 | { 11 | [UpdateInGroup(typeof(ServerRequestProcessingSystemGroup))] 12 | [UpdateInWorld(UpdateInWorld.TargetWorld.Server)] 13 | public abstract class AServerNetworkEntityBuilderSystemT3 : ComponentSystem 14 | where TSelector : struct, IComponentData 15 | where TSelector2 : struct, IComponentData 16 | where TSelector3 : struct, IComponentData 17 | where TCommand : struct, INetworkEntityCopyRpcCommand 18 | { 19 | protected abstract TCommand CreateTransferCommandForEntity(Entity entity, ref NetworkEntity networkEntity, ref TSelector selectorComponent, 20 | ref TSelector2 selectorComponent2, 21 | ref TSelector3 selectorComponent3); 22 | 23 | protected override void OnUpdate() 24 | { 25 | Entities 26 | .WithAll() 27 | .ForEach((Entity entity, ref NetworkEntity networkEntity, ref TSelector selectorComponent, ref TSelector2 selectorComponent2, ref TSelector3 selectorComponent3) => 28 | { 29 | if (ServerManager.Instance.HasConnections) 30 | { 31 | var command = CreateTransferCommandForEntity(entity, ref networkEntity, ref selectorComponent, ref selectorComponent2, ref selectorComponent3); 32 | ServerToClientRpcCommandBuilder 33 | .Broadcast(command) 34 | .Build(PostUpdateCommands); 35 | } 36 | 37 | PostUpdateCommands.RemoveComponent(entity); 38 | }); 39 | 40 | Entities 41 | .WithAll() 42 | .ForEach((Entity entity, DynamicBuffer clients, ref NetworkEntity networkEntity, ref TSelector selectorComponent, 43 | ref TSelector2 selectorComponent2, ref TSelector3 selectorComponent3) => 44 | { 45 | if (ServerManager.Instance.HasConnections) 46 | { 47 | var command = CreateTransferCommandForEntity(entity, ref networkEntity, ref selectorComponent, ref selectorComponent2, ref selectorComponent3); 48 | foreach (var clientEntity in clients) 49 | { 50 | ServerToClientRpcCommandBuilder 51 | .SendTo(clientEntity.clientConnection, command) 52 | .Build(PostUpdateCommands); 53 | } 54 | } 55 | 56 | PostUpdateCommands.RemoveComponent(entity); 57 | }); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Server/Entities/AServerNetworkEntityBuilderSystemT3.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc9fd4d604224a8186b4f6316d246f58 3 | timeCreated: 1602061290 -------------------------------------------------------------------------------- /Server/Entities/DefaultNetworkEntityIdFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Plugins.ECSPowerNetcode.Server.Entities 2 | { 3 | public class DefaultNetworkEntityIdFactory : INetworkEntityIdFactory 4 | { 5 | private uint m_nextEntityId = 1; 6 | 7 | public uint NextId() 8 | { 9 | return m_nextEntityId++; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Server/Entities/DefaultNetworkEntityIdFactory.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 11d6205879264afe8232bc56ff68888c 3 | timeCreated: 1590666197 -------------------------------------------------------------------------------- /Server/Entities/INetworkEntityIdFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Plugins.ECSPowerNetcode.Server.Entities 2 | { 3 | public interface INetworkEntityIdFactory 4 | { 5 | uint NextId(); 6 | } 7 | } -------------------------------------------------------------------------------- /Server/Entities/INetworkEntityIdFactory.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dbf3648698a6e44cb8c61a86eb521255 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Server/Entities/ServerNetworkEntityBuilder.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.EntityBulderExtensions; 2 | using Plugins.ECSPowerNetcode.Features.NetworkEntities; 3 | 4 | namespace Plugins.ECSPowerNetcode.Server.Entities 5 | { 6 | public abstract class ServerNetworkEntityBuilder : NetcodeEntityBuilder 7 | { 8 | public uint NetworkEntityId { get; } 9 | 10 | protected ServerNetworkEntityBuilder() 11 | { 12 | NetworkEntityId = ServerManager.Instance.NextNetworkEntityId; 13 | AddComponentData(new NetworkEntity {networkEntityId = NetworkEntityId}); 14 | } 15 | 16 | protected ServerNetworkEntityBuilder(uint networkEntityId) 17 | { 18 | AddComponentData(new NetworkEntity {networkEntityId = networkEntityId}); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Server/Entities/ServerNetworkEntityBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 76b0c3ba19ed451090dc78438e834bd1 3 | timeCreated: 1589818280 -------------------------------------------------------------------------------- /Server/Entities/ServerNetworkEntitySystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Features.NetworkEntities; 2 | using Plugins.ECSPowerNetcode.Server.Groups; 3 | using Unity.Entities; 4 | using Unity.NetCode; 5 | 6 | namespace Plugins.ECSPowerNetcode.Server.Entities 7 | { 8 | [UpdateInGroup(typeof(ServerNetworkEntitySystemGroup))] 9 | [UpdateInWorld(UpdateInWorld.TargetWorld.Server)] 10 | public class ServerNetworkEntitySystem : ComponentSystem 11 | { 12 | protected override void OnUpdate() 13 | { 14 | Entities 15 | .WithAll() 16 | .WithNone() 17 | .ForEach((Entity entity, ref NetworkEntity networkEntity) => 18 | { 19 | ServerManager.Instance.NetworkEntityManager.Add(networkEntity.networkEntityId, entity); 20 | 21 | PostUpdateCommands.AddComponent(entity, new NetworkEntityRegistered {networkEntityId = networkEntity.networkEntityId}); 22 | }); 23 | 24 | Entities 25 | .WithAll() 26 | .WithNone() 27 | .ForEach((Entity entity, ref NetworkEntityRegistered networkEntity) => 28 | { 29 | ServerManager.Instance.NetworkEntityManager.Remove(networkEntity.networkEntityId); 30 | PostUpdateCommands.RemoveComponent(entity); 31 | }); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Server/Entities/ServerNetworkEntitySystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e6eafe8e9fd4545ba1563800173e84c 3 | timeCreated: 1589474656 -------------------------------------------------------------------------------- /Server/Exceptions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ecc9d00fabdc44d88707413d1328dd3f 3 | timeCreated: 1589880636 -------------------------------------------------------------------------------- /Server/Exceptions/ClientConnectionNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Exceptions 4 | { 5 | public class ClientConnectionNotFoundException : Exception 6 | { 7 | public ClientConnectionNotFoundException() 8 | { 9 | } 10 | 11 | public ClientConnectionNotFoundException(string message) : base(message) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Server/Exceptions/ClientConnectionNotFoundException.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe45c69c44cc4bfcbb3be659bc1f3940 3 | timeCreated: 1589880648 -------------------------------------------------------------------------------- /Server/Exceptions/ServerException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using JetBrains.Annotations; 4 | 5 | namespace Plugins.ECSPowerNetcode.Server.Exceptions 6 | { 7 | public class ServerException : Exception 8 | { 9 | public ServerException() 10 | { 11 | } 12 | 13 | protected ServerException([NotNull] SerializationInfo info, StreamingContext context) : base(info, context) 14 | { 15 | } 16 | 17 | public ServerException(string message) : base(message) 18 | { 19 | } 20 | 21 | public ServerException(string message, Exception innerException) : base(message, innerException) 22 | { 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Server/Exceptions/ServerException.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8500e9bdae14467999cf25850a7a9850 3 | timeCreated: 1603976287 -------------------------------------------------------------------------------- /Server/Groups.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 19b37f10b6694253a330089d7126bdb5 3 | timeCreated: 1589816650 -------------------------------------------------------------------------------- /Server/Groups/ServerCleanupSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Groups 4 | { 5 | [UpdateInGroup(typeof(Unity.NetCode.ServerSimulationSystemGroup))] 6 | [UpdateAfter(typeof(ServerGameSimulationSystemGroup))] 7 | public class ServerCleanupSystemGroup : ComponentSystemGroup 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /Server/Groups/ServerCleanupSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 85b6c6c61fdc45939ea72c6316a8b104 3 | timeCreated: 1590236023 -------------------------------------------------------------------------------- /Server/Groups/ServerConnectionSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Groups 4 | { 5 | [UpdateInGroup(typeof(Unity.NetCode.ServerSimulationSystemGroup))] 6 | public class ServerConnectionSystemGroup : ComponentSystemGroup 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /Server/Groups/ServerConnectionSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 63ee0cc41e5b44a6b3035018f7943dda 3 | timeCreated: 1589816758 -------------------------------------------------------------------------------- /Server/Groups/ServerGameSimulationSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | using Unity.NetCode; 3 | 4 | namespace Plugins.ECSPowerNetcode.Server.Groups 5 | { 6 | [UpdateInGroup(typeof(ServerSimulationSystemGroup))] 7 | [UpdateAfter(typeof(ServerNetworkEntitySynchronizationSystemGroup))] 8 | public class ServerGameSimulationSystemGroup : ComponentSystemGroup 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /Server/Groups/ServerGameSimulationSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a0f28e7586644b2b91e17431c5f4fb17 3 | timeCreated: 1589816715 -------------------------------------------------------------------------------- /Server/Groups/ServerNetworkEntitySynchronizationSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Groups 4 | { 5 | [UpdateInGroup(typeof(Unity.NetCode.ServerSimulationSystemGroup))] 6 | [UpdateAfter(typeof(ServerNetworkEntitySystemGroup))] 7 | public class ServerNetworkEntitySynchronizationSystemGroup : ComponentSystemGroup 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /Server/Groups/ServerNetworkEntitySynchronizationSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d96f2f2de4cf4e3c8d8e67e927eb3a76 3 | timeCreated: 1589920921 -------------------------------------------------------------------------------- /Server/Groups/ServerNetworkEntitySystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Groups 4 | { 5 | [UpdateInGroup(typeof(Unity.NetCode.ServerSimulationSystemGroup))] 6 | [UpdateAfter(typeof(ServerRequestProcessingSystemGroup))] 7 | public class ServerNetworkEntitySystemGroup : ComponentSystemGroup 8 | { 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /Server/Groups/ServerNetworkEntitySystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 79d7cb6225b940d0ae26016558a75249 3 | timeCreated: 1589816687 -------------------------------------------------------------------------------- /Server/Groups/ServerRequestProcessingSystemGroup.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Server.Groups 4 | { 5 | [UpdateInGroup(typeof(Unity.NetCode.ServerSimulationSystemGroup))] 6 | [UpdateAfter(typeof(ServerConnectionSystemGroup))] 7 | public class ServerRequestProcessingSystemGroup : ComponentSystemGroup 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /Server/Groups/ServerRequestProcessingSystemGroup.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe88fc43c87c4def9e570341da82d44a 3 | timeCreated: 1589816669 -------------------------------------------------------------------------------- /Server/Lifecycle.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 78a6d91d68c440b2815a30bbcfa87359 3 | timeCreated: 1589816888 -------------------------------------------------------------------------------- /Server/Lifecycle/ServerClientDisconnectedSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Server.Groups; 2 | using Unity.Entities; 3 | using Unity.NetCode; 4 | using UnityEngine; 5 | 6 | namespace Plugins.ECSPowerNetcode.Server.Lifecycle 7 | { 8 | [UpdateInGroup(typeof(ServerConnectionSystemGroup))] 9 | [UpdateInWorld(UpdateInWorld.TargetWorld.Server)] 10 | public class ServerClientDisconnectedSystem : ComponentSystem 11 | { 12 | protected override void OnUpdate() 13 | { 14 | Entities 15 | .WithAll() 16 | .ForEach( 17 | (Entity entity, ref NetworkIdComponent networkIdComponent, ref CommandTargetComponent commandTargetComponent, ref NetworkStreamDisconnected disconnected) => 18 | { 19 | PostUpdateCommands.DestroyEntity(commandTargetComponent.targetEntity); 20 | ServerManager.Instance.OnDisconnected(networkIdComponent.Value); 21 | 22 | Debug.Log($"[Server] Client disconnected from server with network id = [{networkIdComponent.Value}] because: {disconnected.Reason}"); 23 | }); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Server/Lifecycle/ServerClientDisconnectedSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8ff7b3a5188d42f2bf20d21db4a9eacd 3 | timeCreated: 1589811392 -------------------------------------------------------------------------------- /Server/Lifecycle/ServerNewClientConnectedSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSEntityBuilder; 2 | using Plugins.ECSPowerNetcode.Server.Components; 3 | using Plugins.ECSPowerNetcode.Server.Groups; 4 | using Plugins.ECSPowerNetcode.Shared.Components; 5 | using Unity.Entities; 6 | using Unity.NetCode; 7 | using UnityEngine; 8 | 9 | namespace Plugins.ECSPowerNetcode.Server.Lifecycle 10 | { 11 | [UpdateInGroup(typeof(ServerConnectionSystemGroup))] 12 | [UpdateAfter(typeof(ServerStartSystem))] 13 | [UpdateInWorld(UpdateInWorld.TargetWorld.Server)] 14 | public class ServerNewClientConnectedSystem : ComponentSystem 15 | { 16 | protected override void OnUpdate() 17 | { 18 | Entities 19 | .WithAll() 20 | .WithNone() 21 | .ForEach((Entity connectionEntity, ref NetworkIdComponent networkIdComponent) => 22 | { 23 | Debug.Log($"[Server] Client connected with network id = [{networkIdComponent.Value}]"); 24 | 25 | EntityWrapper.Wrap(connectionEntity, EntityManager) 26 | .SetName($"ClientConnection_{networkIdComponent.Value}"); 27 | 28 | var connectionCommandHandler = EntityWrapper.CreateEntity(EntityManager) 29 | .AddComponentData(new ServerToClientCommandHandler {connectionEntity = connectionEntity}) 30 | .SetName($"ClientConnection_{networkIdComponent.Value}_CommandBuffer") 31 | .Entity; 32 | 33 | PostUpdateCommands.SetComponent(connectionEntity, new CommandTargetComponent {targetEntity = connectionCommandHandler}); 34 | PostUpdateCommands.AddComponent(connectionEntity); 35 | 36 | ServerManager.Instance.OnConnected(networkIdComponent.Value, connectionEntity, connectionCommandHandler); 37 | 38 | var ghostCollection = GetSingletonEntity(); 39 | var prefab = Entity.Null; 40 | var prefabs = EntityManager.GetBuffer(ghostCollection); 41 | for (int ghostId = 0; ghostId < prefabs.Length; ++ghostId) 42 | { 43 | if (EntityManager.HasComponent(prefabs[ghostId].Value)) 44 | prefab = prefabs[ghostId].Value; 45 | } 46 | 47 | EntityWrapper.Instantiate(prefab, PostUpdateCommands) 48 | .SetName($"ClientConnection_{networkIdComponent.Value}_Ghost") 49 | .AddComponentData(new GhostOwnerComponent {NetworkId = networkIdComponent.Value}); 50 | }); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Server/Lifecycle/ServerNewClientConnectedSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bea5167ffaa34b8fbb3d407eaefa9659 3 | timeCreated: 1589389401 -------------------------------------------------------------------------------- /Server/Lifecycle/ServerStartSystem.cs: -------------------------------------------------------------------------------- 1 | using Plugins.ECSPowerNetcode.Server.Components; 2 | using Plugins.ECSPowerNetcode.Server.Groups; 3 | using Unity.Entities; 4 | using Unity.NetCode; 5 | using Unity.Networking.Transport; 6 | using UnityEngine; 7 | 8 | namespace Plugins.ECSPowerNetcode.Server.Lifecycle 9 | { 10 | [UpdateInGroup(typeof(ServerConnectionSystemGroup))] 11 | [UpdateInWorld(UpdateInWorld.TargetWorld.Server)] 12 | public class ServerStartSystem : ComponentSystem 13 | { 14 | protected override void OnCreate() 15 | { 16 | RequireSingletonForUpdate(); 17 | } 18 | 19 | protected override void OnUpdate() 20 | { 21 | var startServer = GetSingleton(); 22 | EntityManager.DestroyEntity(GetSingletonEntity()); 23 | 24 | var network = World.GetExistingSystem(); 25 | NetworkEndPoint ep = NetworkEndPoint.AnyIpv4; 26 | ep.Port = startServer.port; 27 | network.Listen(ep); 28 | 29 | Debug.Log($"[Server] Started on {ep.Port} port in {World.Name} world"); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Server/Lifecycle/ServerStartSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8c654cb54bc1413dbea7b2483d4f3371 3 | timeCreated: 1589708653 -------------------------------------------------------------------------------- /Server/Packets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 44a51b18d52f140db90f53f483efd0ec 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Server/Packets/AServerReceiveRpcCommandSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Plugins.ECSPowerNetcode.Server.Exceptions; 3 | using Plugins.ECSPowerNetcode.Server.Groups; 4 | using Plugins.ECSPowerNetcode.Shared; 5 | using Unity.Entities; 6 | using Unity.NetCode; 7 | using UnityEngine; 8 | 9 | namespace Plugins.ECSPowerNetcode.Server.Packets 10 | { 11 | [UpdateInGroup(typeof(ServerRequestProcessingSystemGroup))] 12 | public abstract class AServerReceiveRpcCommandSystem : ComponentSystem where T : struct, IComponentData 13 | { 14 | protected virtual bool ShouldDestroyEntity { get; } = true; 15 | 16 | protected abstract void OnCommand(ref T packet, ConnectionDescription clientConnection); 17 | 18 | protected override void OnUpdate() 19 | { 20 | Entities 21 | .ForEach((Entity entity, ref T command, ref ReceiveRpcCommandRequestComponent requestComponent) => 22 | { 23 | var clientConnection = ServerManager.Instance.GetClientConnectionByConnectionEntity(requestComponent.SourceConnection); 24 | 25 | if (ShouldDestroyEntity) 26 | PostUpdateCommands.DestroyEntity(entity); 27 | 28 | try 29 | { 30 | OnCommand(ref command, clientConnection); 31 | } 32 | catch (ServerException e) 33 | { 34 | Debug.LogWarning($"{GetType()} Expected server error: {e.Message}"); 35 | } 36 | catch (Exception e) 37 | { 38 | Debug.LogError($"{GetType()} Unexpected server error: {e.Message}"); 39 | Debug.LogException(e); 40 | } 41 | }); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Server/Packets/AServerReceiveRpcCommandSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 540416fece514afcaeaed75743f39959 3 | timeCreated: 1590232903 -------------------------------------------------------------------------------- /Server/Packets/ServerToClientRpcCommandBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Plugins.ECSEntityBuilder; 3 | using Plugins.ECSPowerNetcode.EntityBulderExtensions; 4 | using Plugins.ECSPowerNetcode.Server.Exceptions; 5 | using Unity.Entities; 6 | using Unity.NetCode; 7 | 8 | namespace Plugins.ECSPowerNetcode.Server.Packets 9 | { 10 | public class ServerToClientRpcCommandBuilder : NetcodeEntityBuilder 11 | { 12 | public static ServerToClientRpcCommandBuilder SendTo(Entity serverToClientConnection, T command) where T : struct, IComponentData 13 | { 14 | var builder = new ServerToClientRpcCommandBuilder(); 15 | builder.AddComponentData(command) 16 | .AddComponentData(new SendRpcCommandRequestComponent {TargetConnection = serverToClientConnection}); 17 | //.SetName($"RpcCommand {typeof(T).Name}"); 18 | return builder; 19 | } 20 | 21 | public static ServerToClientRpcCommandBuilder SendTo(int networkConnectionId, T packet) where T : struct, IComponentData 22 | { 23 | var connection = ServerManager.Instance.GetClientConnectionByNetworkId(networkConnectionId); 24 | if (connection.IsEmpty) 25 | throw new ClientConnectionNotFoundException($"No connection with network id [{networkConnectionId}] found"); 26 | 27 | var builder = new ServerToClientRpcCommandBuilder(); 28 | builder.AddComponentData(packet) 29 | .AddComponentData(new SendRpcCommandRequestComponent {TargetConnection = connection.connectionEntity}); 30 | //.SetName($"RpcCommand {typeof(T).Name}"); 31 | return builder; 32 | } 33 | 34 | public static ServerToClientRpcCommandBuilder Broadcast(T packet) where T : struct, IComponentData 35 | { 36 | var builder = new ServerToClientRpcCommandBuilder(); 37 | builder.AddComponentData(packet) 38 | .AddComponentData(new SendRpcCommandRequestComponent()); // if there is no TargetConnection, it will be broadcasted 39 | //.SetName($"BroadcastRpcCommand {typeof(T).Name}"); 40 | return builder; 41 | } 42 | 43 | public static ServerToClientMassiveRpcCommandBuilder SendToAllConnectionsExcept(T packet, Entity excludeNetworkConnection) where T : struct, IComponentData 44 | { 45 | var connectionsToSendTo = ServerManager.Instance.AllConnections 46 | .FindAll(x => x.connectionEntity != excludeNetworkConnection) 47 | .Select(x => x.connectionEntity) 48 | .ToArray(); 49 | 50 | var builder = new ServerToClientMassiveRpcCommandBuilder(connectionsToSendTo); 51 | builder.AddComponentData(packet); 52 | //.SetName($"RpcCommand {typeof(T).Name}"); 53 | return builder; 54 | } 55 | } 56 | 57 | public class ServerToClientMassiveRpcCommandBuilder : NetcodeEntityBuilder 58 | { 59 | private readonly Entity[] connections; 60 | 61 | internal ServerToClientMassiveRpcCommandBuilder(Entity[] connections) 62 | { 63 | this.connections = connections; 64 | } 65 | 66 | public override Entity Build(EntityManagerWrapper wrapper) 67 | { 68 | foreach (var connection in connections) 69 | { 70 | AddComponentData(new SendRpcCommandRequestComponent {TargetConnection = connection}); 71 | base.Build(wrapper); 72 | } 73 | 74 | return Entity.Null; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Server/Packets/ServerToClientRpcCommandBuilder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ec0c37371f9c46d9a87b8cda3d240a96 3 | timeCreated: 1589385198 -------------------------------------------------------------------------------- /Server/ServerManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Plugins.ECSPowerNetcode.Server.Components; 5 | using Plugins.ECSPowerNetcode.Server.Entities; 6 | using Plugins.ECSPowerNetcode.Shared; 7 | using Plugins.ECSPowerNetcode.Worlds; 8 | using Unity.Entities; 9 | using UnityEngine; 10 | 11 | namespace Plugins.ECSPowerNetcode.Server 12 | { 13 | public class ServerManager 14 | { 15 | public delegate void OnPlayerConnected(int networkConnectionId, Entity connectionEntity, Entity commandHandlerEntity); 16 | 17 | public delegate void OnPlayerDisconnected(int networkConnectionId); 18 | 19 | public event OnPlayerConnected OnPlayerConnectedHandler; 20 | public event OnPlayerDisconnected OnPlayerDisconnectedHandler; 21 | 22 | public uint Tick => EntityWorldManager.Instance.ServerTick; 23 | public bool HasConnections => AllConnections.Count > 0; 24 | 25 | public INetworkEntityManager NetworkEntityManager { get; set; } = new DefaultNetworkEntityManager(); 26 | public INetworkEntityIdFactory NetworkEntityIdFactory { get; set; } = new DefaultNetworkEntityIdFactory(); 27 | public uint NextNetworkEntityId => NetworkEntityIdFactory.NextId(); 28 | 29 | private readonly Dictionary m_openedConnectionsByConnectionEntity = new Dictionary(); 30 | private readonly Dictionary m_openedConnectionsById = new Dictionary(); 31 | 32 | public void OnConnected(int networkConnectionId, Entity connectionEntity, Entity commandHandlerEntity) 33 | { 34 | var connectionDescription = new ConnectionDescription(networkConnectionId, connectionEntity, commandHandlerEntity); 35 | m_openedConnectionsByConnectionEntity[connectionEntity] = connectionDescription; 36 | m_openedConnectionsById[networkConnectionId] = connectionDescription; 37 | OnPlayerConnectedHandler?.Invoke(networkConnectionId, connectionEntity, commandHandlerEntity); 38 | } 39 | 40 | public void OnDisconnected(int networkId) 41 | { 42 | m_openedConnectionsById.Remove(networkId); 43 | OnPlayerDisconnectedHandler?.Invoke(networkId); 44 | } 45 | 46 | public ConnectionDescription GetClientConnectionByNetworkId(int networkId) 47 | { 48 | return m_openedConnectionsById[networkId]; 49 | } 50 | 51 | public ConnectionDescription GetClientConnectionByConnectionEntity(Entity connectionEntity) 52 | { 53 | return m_openedConnectionsByConnectionEntity[connectionEntity]; 54 | } 55 | 56 | public void StartServer(ushort port) 57 | { 58 | var serverWorld = EntityWorldManager.Instance.Server; 59 | if (!serverWorld.IsCreated) 60 | throw new NotImplementedException("Server world doesn't exist!"); 61 | 62 | var startServerEntity = serverWorld.EntityManager.CreateEntity(); 63 | serverWorld.EntityManager.AddComponentData(startServerEntity, new StartServer {port = port}); 64 | } 65 | 66 | public void StopServer() 67 | { 68 | } 69 | 70 | public List AllConnections => m_openedConnectionsById.Values.ToList(); 71 | 72 | #region Singleton 73 | 74 | private static ServerManager INSTANCE = new ServerManager(); 75 | 76 | static ServerManager() 77 | { 78 | } 79 | 80 | private ServerManager() 81 | { 82 | } 83 | 84 | public static ServerManager Instance 85 | { 86 | get { return INSTANCE; } 87 | } 88 | 89 | // for quick play mode entering 90 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 91 | public static void Reset() 92 | { 93 | INSTANCE = new ServerManager(); 94 | } 95 | 96 | #endregion 97 | } 98 | } -------------------------------------------------------------------------------- /Server/ServerManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9427028cfe8f41d1bc0ba2459995a553 3 | timeCreated: 1589385339 -------------------------------------------------------------------------------- /Shared.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e0256173972a4f64a584555b78dbdc0d 3 | timeCreated: 1589730064 -------------------------------------------------------------------------------- /Shared/Components.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d6cf8825b98549d4b7b41aec346ee4c3 3 | timeCreated: 1601463301 -------------------------------------------------------------------------------- /Shared/Components/SyncGhostComponent.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Shared.Components 4 | { 5 | [GenerateAuthoringComponent] 6 | public struct SyncGhostComponent : IComponentData 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Shared/Components/SyncGhostComponent.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f1aa6f7c21040f3b07bdc00d94070bd 3 | timeCreated: 1601463312 -------------------------------------------------------------------------------- /Shared/ConnectionDescription.cs: -------------------------------------------------------------------------------- 1 | using Unity.Entities; 2 | 3 | namespace Plugins.ECSPowerNetcode.Shared 4 | { 5 | public class ConnectionDescription 6 | { 7 | public readonly int networkConnectionId; 8 | public readonly Entity connectionEntity; 9 | public readonly Entity commandHandlerEntity; 10 | 11 | public ConnectionDescription(int networkConnectionId, Entity connectionEntity, Entity commandHandlerEntity) 12 | { 13 | this.networkConnectionId = networkConnectionId; 14 | this.connectionEntity = connectionEntity; 15 | this.commandHandlerEntity = commandHandlerEntity; 16 | } 17 | 18 | public bool IsEmpty => networkConnectionId == 0; 19 | } 20 | } -------------------------------------------------------------------------------- /Shared/ConnectionDescription.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4bd8be60a5e9420a9f7df9b6f1252c42 3 | timeCreated: 1589878594 -------------------------------------------------------------------------------- /Shared/DefaultNetworkEntityManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Unity.Entities; 3 | 4 | namespace Plugins.ECSPowerNetcode.Shared 5 | { 6 | public class DefaultNetworkEntityManager : INetworkEntityManager 7 | { 8 | protected readonly Dictionary entities = new Dictionary(); 9 | 10 | public void Add(uint networkEntityId, Entity entity) 11 | { 12 | entities[networkEntityId] = entity; 13 | } 14 | 15 | public Entity TryGetEntityByNetworkEntityId(uint networkEntityId) 16 | { 17 | if (!entities.TryGetValue(networkEntityId, out var entity)) 18 | return Entity.Null; 19 | 20 | return entity; 21 | } 22 | 23 | public Entity GetEntityByNetworkEntityId(uint networkEntityId) 24 | { 25 | if (!entities.TryGetValue(networkEntityId, out var entity)) 26 | throw new KeyNotFoundException($"No network entity with {networkEntityId} found"); 27 | 28 | return entity; 29 | } 30 | 31 | public bool Remove(uint networkEntityId) 32 | { 33 | return entities.Remove(networkEntityId); 34 | } 35 | 36 | public Entity this[uint networkEntityId] => TryGetEntityByNetworkEntityId(networkEntityId); 37 | 38 | public Dictionary All => entities; 39 | } 40 | } -------------------------------------------------------------------------------- /Shared/DefaultNetworkEntityManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad9f5b423dac4fa2b49d77b3319dc972 3 | timeCreated: 1590831022 -------------------------------------------------------------------------------- /Shared/INetworkEntityManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Unity.Entities; 3 | 4 | namespace Plugins.ECSPowerNetcode.Shared 5 | { 6 | public interface INetworkEntityManager 7 | { 8 | void Add(uint networkEntityId, Entity entity); 9 | Entity TryGetEntityByNetworkEntityId(uint networkEntityId); 10 | bool Remove(uint networkEntityId); 11 | 12 | Dictionary All { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /Shared/INetworkEntityManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 132d354c6d844d59ba883f8bf8850a88 3 | timeCreated: 1590830914 -------------------------------------------------------------------------------- /Utils.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c2ce7bf120f0465aa2a5567e9dc0d8bd 3 | timeCreated: 1589632892 -------------------------------------------------------------------------------- /Utils/MultiValueDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Plugins.ECSPowerNetcode.Utils 5 | { 6 | /// 7 | /// Extension to the normal Dictionary. This class can store more than one value for every key. It keeps a HashSet for every Key value. 8 | /// Calling Add with the same Key and multiple values will store each value under the same Key in the Dictionary. Obtaining the values 9 | /// for a Key will return the HashSet with the Values of the Key. 10 | /// 11 | /// The type of the key. 12 | /// The type of the value. 13 | public class MultiValueDictionary : Dictionary> 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public MultiValueDictionary() 19 | : base() 20 | { 21 | } 22 | 23 | 24 | /// 25 | /// Adds the specified value under the specified key 26 | /// 27 | /// The key. 28 | /// The value. 29 | public void Add(TKey key, TValue value) 30 | { 31 | HashSet container = null; 32 | if (!this.TryGetValue(key, out container)) 33 | { 34 | container = new HashSet(); 35 | base.Add(key, container); 36 | } 37 | 38 | container.Add(value); 39 | } 40 | 41 | 42 | /// 43 | /// Adds all values under the specified key 44 | /// 45 | /// The key. 46 | /// The value. 47 | public void AddRange(TKey key, HashSet value) 48 | { 49 | HashSet container = null; 50 | if (!this.TryGetValue(key, out container)) 51 | { 52 | container = new HashSet(); 53 | base.Add(key, container); 54 | } 55 | 56 | foreach (var inner in value) 57 | { 58 | container.Add(inner); 59 | } 60 | } 61 | 62 | 63 | /// 64 | /// Determines whether this dictionary contains the specified value for the specified key 65 | /// 66 | /// The key. 67 | /// The value. 68 | /// true if the value is stored for the specified key in this dictionary, false otherwise 69 | public bool ContainsValue(TKey key, TValue value) 70 | { 71 | bool toReturn = false; 72 | HashSet values = null; 73 | if (this.TryGetValue(key, out values)) 74 | { 75 | toReturn = values.Contains(value); 76 | } 77 | 78 | return toReturn; 79 | } 80 | 81 | 82 | /// 83 | /// Removes the specified value for the specified key. It will leave the key in the dictionary. 84 | /// 85 | /// The key. 86 | /// The value. 87 | public void Remove(TKey key, TValue value) 88 | { 89 | HashSet container = null; 90 | if (this.TryGetValue(key, out container)) 91 | { 92 | container.Remove(value); 93 | if (container.Count <= 0) 94 | { 95 | this.Remove(key); 96 | } 97 | } 98 | } 99 | 100 | 101 | /// 102 | /// Merges the specified multivaluedictionary into this instance. 103 | /// 104 | /// To merge with. 105 | public void Merge(MultiValueDictionary toMergeWith) 106 | { 107 | if (toMergeWith == null) 108 | { 109 | return; 110 | } 111 | 112 | foreach (KeyValuePair> pair in toMergeWith) 113 | { 114 | foreach (TValue value in pair.Value) 115 | { 116 | this.Add(pair.Key, value); 117 | } 118 | } 119 | } 120 | 121 | 122 | /// 123 | /// Gets the values for the key specified. This method is useful if you want to avoid an exception for key value retrieval and you can't use TryGetValue 124 | /// (e.g. in lambdas) 125 | /// 126 | /// The key. 127 | /// if set to true and the key isn't found, an empty hashset is returned, otherwise, if the key isn't found, null is returned 128 | /// 129 | /// This method will return null (or an empty set if returnEmptySet is true) if the key wasn't found, or 130 | /// the values if key was found. 131 | /// 132 | public HashSet GetValues(TKey key, bool returnEmptySet) 133 | { 134 | HashSet toReturn = null; 135 | if (!base.TryGetValue(key, out toReturn) && returnEmptySet) 136 | { 137 | toReturn = new HashSet(); 138 | } 139 | 140 | return toReturn; 141 | } 142 | 143 | public IEnumerable GetAllValues() 144 | { 145 | foreach (var hashSet in Values) 146 | { 147 | foreach (var value in hashSet) 148 | { 149 | yield return value; 150 | } 151 | } 152 | } 153 | 154 | public HashSet GetValues() 155 | { 156 | var result = new HashSet(); 157 | foreach (var hashSet in Values) 158 | { 159 | foreach (var value in hashSet) 160 | result.Add(value); 161 | } 162 | 163 | return result; 164 | } 165 | } 166 | 167 | public static class MultiValueDictionaryExtensions 168 | { 169 | public static MultiValueDictionary ToMultiValueDictionary(this IEnumerable values, Func keySelector) 170 | { 171 | var multiValueDictionary = new MultiValueDictionary(); 172 | foreach (var entry in values) 173 | multiValueDictionary.Add(keySelector.Invoke(entry), entry); 174 | return multiValueDictionary; 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /Utils/MultiValueDictionary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 58b91d4b6eb1f42058a511e8c8dc7d83 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Worlds.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cf7a88ffc87441e88104fa22f145e1e1 3 | timeCreated: 1589631861 -------------------------------------------------------------------------------- /Worlds/EntityWorldManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Unity.Entities; 3 | using Unity.NetCode; 4 | using Unity.Transforms; 5 | using UnityEngine; 6 | 7 | namespace Plugins.ECSPowerNetcode.Worlds 8 | { 9 | public class EntityWorldManager 10 | { 11 | public World Default { get; private set; } 12 | public World Client { get; private set; } 13 | public uint ClientTick => m_clientSimulationSystemGroup.ServerTick; 14 | public World Server { get; private set; } 15 | public uint ServerTick => m_serverSimulationSystemGroup.ServerTick; 16 | public bool HasServerWorld { get; private set; } 17 | public bool HasClientWorld { get; private set; } 18 | 19 | private ClientSimulationSystemGroup m_clientSimulationSystemGroup; 20 | private ServerSimulationSystemGroup m_serverSimulationSystemGroup; 21 | 22 | public void Initialize() 23 | { 24 | foreach (var world in World.All) 25 | { 26 | if (world.GetExistingSystem() != null) 27 | { 28 | m_clientSimulationSystemGroup = world.GetExistingSystem(); 29 | Client = world; 30 | HasClientWorld = true; 31 | } 32 | else if (world.GetExistingSystem() != null) 33 | { 34 | m_serverSimulationSystemGroup = world.GetExistingSystem(); 35 | Server = world; 36 | HasServerWorld = true; 37 | } 38 | 39 | if (world.GetExistingSystem() != null) 40 | Default = world; 41 | } 42 | } 43 | 44 | public World GetWorldByType(WorldType worldType) 45 | { 46 | switch (worldType) 47 | { 48 | case WorldType.DEFAULT: return Default; 49 | case WorldType.SERVER: return Server; 50 | case WorldType.CLIENT: return Client; 51 | } 52 | 53 | throw new NotImplementedException(); 54 | } 55 | 56 | #region Singleton 57 | 58 | private static EntityWorldManager INSTANCE = new EntityWorldManager(); 59 | 60 | static EntityWorldManager() 61 | { 62 | } 63 | 64 | private EntityWorldManager() 65 | { 66 | } 67 | 68 | public static EntityWorldManager Instance 69 | { 70 | get { return INSTANCE; } 71 | } 72 | 73 | // for quick play mode entering 74 | [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] 75 | public static void Reset() 76 | { 77 | INSTANCE = new EntityWorldManager(); 78 | } 79 | 80 | #endregion 81 | } 82 | } -------------------------------------------------------------------------------- /Worlds/EntityWorldManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b8e9c10ceac74b1a949ff274903c64ae 3 | timeCreated: 1589631874 -------------------------------------------------------------------------------- /Worlds/WorldType.cs: -------------------------------------------------------------------------------- 1 | namespace Plugins.ECSPowerNetcode.Worlds 2 | { 3 | public enum WorldType 4 | { 5 | DEFAULT, 6 | CLIENT, 7 | SERVER 8 | } 9 | } -------------------------------------------------------------------------------- /Worlds/WorldType.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ae4fe8df06b048d7a8b1b5d13855593c 3 | timeCreated: 1589631797 --------------------------------------------------------------------------------