using UnityEngine; using UnityEngine.Assertions; using Unity.Collections; using Unity.Networking.Transport; using KairoEngine.Core; using Sirenix.OdinInspector; namespace KairoEngine.Multiplayer { [HideMonoScript] public class ServerBehaviour : MonoBehaviour { public NetMsgController netMsgController; public string eventStreamName = "ServerEvents"; public ushort port = 9000; public int players = 4; public bool autoStart = true; public bool debug = false; public NetworkDriver m_Driver; public NativeList m_Connections; private bool locked = false; private void Awake() { if(netMsgController == null) { Debug.LogError("Server is missing NetMsgController!", this); return; } } private void Start () { if(autoStart) StartServer(); } public void StartServer() { if(locked) return; if(netMsgController == null) { Debug.LogError("Server cannot start with missing NetMsgController!", this); return; } m_Driver = NetworkDriver.Create(); var endpoint = NetworkEndPoint.AnyIpv4; endpoint.Port = port; if (m_Driver.Bind(endpoint) != 0) { if(debug) Debug.Log($"Failed to bind to port {port}"); GenericEvents.Trigger($"{eventStreamName}_PortError", $"Failed to bind to port {port}"); } else { m_Driver.Listen(); if(debug) Debug.Log($"Server listening on port {port}"); GenericEvents.Trigger($"{eventStreamName}_Listening", $"Server listening on port {port}"); locked = true; } m_Connections = new NativeList(players, Allocator.Persistent); } public void StopServer() { NetDisconnectMsg netDisconnectMsg = new NetDisconnectMsg(); SendNetMsgToAllClients((uint)NetOpCode.Disconnect, netDisconnectMsg); Timer.ExecuteRealTime(500, () => { if(debug) Debug.Log($"Server has stopped"); GenericEvents.Trigger($"{eventStreamName}_Stopped", $"Server has stopped"); m_Driver.Dispose(); m_Connections.Dispose(); locked = false; }); } public void SendNetMsgToClient(int id, uint code, NetMsg netMsg) { netMsgController.SendData(code, netMsg, this, id); } public void SendNetMsgToAllClients(uint code, NetMsg netMsg) { for (int i = 0; i < m_Connections.Length; i++) { Assert.IsTrue(m_Connections[i].IsCreated); SendNetMsgToClient(i, code, netMsg); } } public void OnDestroy() { if(!m_Driver.IsCreated) return; m_Driver.Dispose(); m_Connections.Dispose(); } void Update () { if(!m_Driver.IsCreated) return; m_Driver.ScheduleUpdate().Complete(); // CleanUpConnections for (int i = 0; i < m_Connections.Length; i++) { if (!m_Connections[i].IsCreated) { m_Connections.RemoveAtSwapBack(i); --i; } } // AcceptNewConnections NetworkConnection c; while ((c = m_Driver.Accept()) != default(NetworkConnection)) { m_Connections.Add(c); if(debug) Debug.Log($"Accepted a connection (ID {c.InternalId})"); GenericEvents.Trigger($"{eventStreamName}_ClientConnected", $"Accepted a connection (ID {c.InternalId})", c.InternalId); } DataStreamReader stream; for (int i = 0; i < m_Connections.Length; i++) { Assert.IsTrue(m_Connections[i].IsCreated); NetworkEvent.Type cmd; while ((cmd = m_Driver.PopEventForConnection(m_Connections[i], out stream)) != NetworkEvent.Type.Empty) { if (cmd == NetworkEvent.Type.Data) { //if(debug) Debug.Log("Server received data from client"); netMsgController.ReceiveData(ref stream, this, i); } else if (cmd == NetworkEvent.Type.Disconnect) { if(debug) Debug.Log($"Client disconnected from server (ID {m_Connections[i].InternalId})"); GenericEvents.Trigger($"{eventStreamName}_ClientDisconnected", $"Client disconnected from server (ID {m_Connections[i].InternalId})"); m_Connections[i] = default(NetworkConnection); } } } } } }