using System.Collections.Generic; using UnityEngine; using UnityEngine.Assertions; using Unity.Jobs; using Unity.Collections; using Unity.Networking.Transport; using Sirenix.OdinInspector; using KairoEngine.Core; namespace KairoEngine.Multiplayer { [HideMonoScript] public class JobifiedServerBehaviour : MonoBehaviour { [HideInInspector] public NetworkDriver m_Driver; [HideInInspector] public NativeList m_Connections; public ushort port = 9000; public int players = 4; public bool autoStart = true; private JobHandle ServerJobHandle; [HideInInspector] private NativeQueue m_eventQueue; private bool resourceLock = false; void Start () { if(autoStart) StartServer(); } public void StartServer() { resourceLock = true; m_eventQueue = new NativeQueue(Allocator.Persistent); m_Connections = new NativeList(players, Allocator.Persistent); m_Driver = NetworkDriver.Create(); var endpoint = NetworkEndPoint.AnyIpv4; endpoint.Port = port; if (m_Driver.Bind(endpoint) != 0) { Debug.Log($"Failed to bind to port {port}"); GenericEvents.Trigger("ServerEvent", $"Failed to bind to port {port}"); } else { Debug.Log($"Server listening on port {port}"); GenericEvents.Trigger("ServerEvent", $"Server listening on port {port}"); m_Driver.Listen(); } } public void OnDestroy() { // Make sure we run our jobs to completion before exiting. ServerJobHandle.Complete(); m_Connections.Dispose(); m_Driver.Dispose(); m_eventQueue.Dispose(); } void Update () { if(resourceLock == false) return; ServerJobHandle.Complete(); while(m_eventQueue.TryDequeue(out JobEvent jobEvent)) { TriggerJobEvent(jobEvent); } var connectionJob = new ServerUpdateConnectionsJob { driver = m_Driver, connections = m_Connections, eventQueue = m_eventQueue.AsParallelWriter() }; var serverUpdateJob = new ServerUpdateJob { driver = m_Driver.ToConcurrent(), connections = m_Connections.AsDeferredJobArray(), eventQueue = m_eventQueue.AsParallelWriter(), }; ServerJobHandle = m_Driver.ScheduleUpdate(); ServerJobHandle = connectionJob.Schedule(ServerJobHandle); ServerJobHandle = serverUpdateJob.Schedule(m_Connections, 1, ServerJobHandle); } public void TriggerJobEvent(JobEvent jobEvent) { switch (jobEvent.code) { case 1: GenericEvents.Trigger("ServerEvent", $"Client connected "); break; case 2: GenericEvents.Trigger("ServerEvent", $"Client disconnected"); break; case 3: GenericEvents.Trigger("ServerEvent", $"Data received from client"); break; case 4: GenericEvents.Trigger("ServerEvent", $"Error connecting to client"); break; default: break; } } } struct ServerUpdateConnectionsJob : IJob { public NetworkDriver driver; public NativeList connections; public NativeQueue.ParallelWriter eventQueue; public void Execute() { // CleanUpConnections for (int i = 0; i < connections.Length; i++) { if (!connections[i].IsCreated) { connections.RemoveAtSwapBack(i); --i; } } // AcceptNewConnections NetworkConnection c; while ((c = driver.Accept()) != default(NetworkConnection)) { connections.Add(c); Debug.Log("Accepted a connection"); eventQueue.Enqueue(new JobEvent((int)NetworkingEvents.Message.ClientConnected)); } } } struct ServerUpdateJob : IJobParallelForDefer { public NetworkDriver.Concurrent driver; public NativeArray connections; public NativeQueue.ParallelWriter eventQueue; public void Execute(int index) { DataStreamReader stream; Assert.IsTrue(connections[index].IsCreated); // Receive data from clients NetworkEvent.Type cmd; while ((cmd = driver.PopEventForConnection(connections[index], out stream)) != NetworkEvent.Type.Empty) { if (cmd == NetworkEvent.Type.Data) { eventQueue.Enqueue(new JobEvent((int)NetworkingEvents.Message.ServerReceivedData)); uint number = stream.ReadUInt(); Debug.Log("Got " + number + " from the Client adding + 2 to it."); number +=2; DataStreamWriter writer = new DataStreamWriter(); var result = driver.BeginSend(connections[index], out writer); writer.WriteUInt(number); driver.EndSend(writer); } else if (cmd == NetworkEvent.Type.Disconnect) { eventQueue.Enqueue(new JobEvent((int)NetworkingEvents.Message.ClientDisconnected)); Debug.Log("Client disconnected from server"); connections[index] = default(NetworkConnection); } } // Send data to clients } } }