using UnityEngine; using UnityEngine.Assertions; using Unity.Collections; using Unity.Networking.Transport; using KairoEngine.Core; namespace KairoEngine.Multiplayer { public class ServerBehaviour : MonoBehaviour { public ushort port = 9000; public int players = 4; public bool autoStart = true; public string eventStreamName = "ServerEvents"; public bool debug = false; public NetworkDriver m_Driver; public NativeList m_Connections; private bool locked = false; void Start () { if(autoStart) StartServer(); } public void StartServer() { if(locked) 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) { DataStreamWriter writer = new DataStreamWriter(); m_Driver.BeginSend(m_Connections[id], out writer); NetMsgController.Serialize(code, netMsg, ref writer, this, id); m_Driver.EndSend(writer); } 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"); uint code = stream.ReadByte(); NetMsgController.Deserialize(code, 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); } } } } } }