Browse Source

Added base Sync Adjustment System

James Peret 2 years ago
parent
commit
f4637448d1

+ 4 - 0
Runtime/Client/ClientActivityMessages.cs

@@ -29,6 +29,8 @@ namespace KairoEngine.Multiplayer
             NetMsgEvents.StartListening($"{client.eventStreamName}_HandshakeAccepted", OnClientReceivedDataEvent);
             NetMsgEvents.StartListening($"{client.eventStreamName}_PeerConnected", OnClientReceivedDataEvent);
             NetMsgEvents.StartListening($"{client.eventStreamName}_PeerDisconnected", OnClientReceivedDataEvent);
+            NetMsgEvents.StartListening($"{client.eventStreamName}_PingReceived", OnClientReceivedDataEvent);
+            NetMsgEvents.StartListening($"{client.eventStreamName}_PongSent", OnClientReceivedDataEvent);
         }
 
         private void OnDestroy()
@@ -44,6 +46,8 @@ namespace KairoEngine.Multiplayer
             NetMsgEvents.StopListening($"{client.eventStreamName}_ServerDisconnect", OnClientReceivedDataEvent);
             NetMsgEvents.StopListening($"{client.eventStreamName}_SendingHandshake", OnClientReceivedDataEvent);
             NetMsgEvents.StopListening($"{client.eventStreamName}_HandshakeAccepted", OnClientReceivedDataEvent);
+            NetMsgEvents.StopListening($"{client.eventStreamName}_PingReceived", OnClientReceivedDataEvent);
+            NetMsgEvents.StopListening($"{client.eventStreamName}_PongSent", OnClientReceivedDataEvent);
         }
 
         public void OnClientEvent(string text)

+ 4 - 0
Runtime/Client/ClientStatus.cs

@@ -32,6 +32,8 @@ namespace KairoEngine.Multiplayer
             NetMsgEvents.StartListening($"{client.eventStreamName}_ServerDisconnect", OnReceivePacket);
             NetMsgEvents.StartListening($"{client.eventStreamName}_SendingHandshake", OnSentPacket);
             NetMsgEvents.StartListening($"{client.eventStreamName}_HandshakeAccepted", OnReceivePacket);
+            NetMsgEvents.StartListening($"{client.eventStreamName}_PingReceived", OnReceivePacket);
+            NetMsgEvents.StartListening($"{client.eventStreamName}_PongSent", OnSentPacket);
             GenericEvents.StartListening($"{client.eventStreamName}_OnTick", OnTick);
         }
 
@@ -42,6 +44,8 @@ namespace KairoEngine.Multiplayer
             NetMsgEvents.StopListening($"{client.eventStreamName}_ServerDisconnect", OnReceivePacket);
             NetMsgEvents.StopListening($"{client.eventStreamName}_SendingHandshake", OnSentPacket);
             NetMsgEvents.StopListening($"{client.eventStreamName}_HandshakeAccepted", OnReceivePacket);
+            NetMsgEvents.StopListening($"{client.eventStreamName}_PingReceived", OnReceivePacket);
+            NetMsgEvents.StopListening($"{client.eventStreamName}_PongSent", OnSentPacket);
         }
 
         private void Update()

+ 31 - 0
Runtime/Client/ClientSyncAjustmentSystem.cs

@@ -0,0 +1,31 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using KairoEngine.Core;
+using Sirenix.OdinInspector;
+
+namespace KairoEngine.Multiplayer
+{
+    [HideMonoScript]
+    public class ClientSyncAjustmentSystem : MonoBehaviour
+    {
+        public ClientBehaviour client;
+
+        private void OnEnable()
+        {
+            NetMsgEvents.StartListening($"{client.eventStreamName}_PingReceived", OnPingReceived);
+        }
+
+        private void OnDisable()
+        {
+            NetMsgEvents.StopListening($"{client.eventStreamName}_PingReceived", OnPingReceived);
+        }
+
+        private void OnPingReceived(string text, int clientId, uint code, NetMsg netMsg) 
+        {
+            NetPingMsg pingMsg = (NetPingMsg)netMsg;
+            NetPongMsg pongMsg = new NetPongMsg(pingMsg.pingId);
+            client.SendNetMsgToServer((uint)NetOpCode.Pong, pongMsg);
+        }
+    }
+}

+ 11 - 0
Runtime/Client/ClientSyncAjustmentSystem.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b2854aff3430f3e4fa7cc9978a581693
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 0
Runtime/ClientData.cs

@@ -11,6 +11,7 @@ namespace KairoEngine.Multiplayer
         [FoldoutGroup("@ToString()")] public int connectionId = -1;
         [FoldoutGroup("@ToString()")] public string playerName = "";
         [FoldoutGroup("@ToString()")] public bool isConnected = false;
+        [FoldoutGroup("@ToString()"), ReadOnly] public int latency = 0;
 
         public ClientData(int connectionId, string playerName, bool isConnected)
         {

+ 3 - 1
Runtime/NetOpCode.cs

@@ -8,7 +8,9 @@ namespace KairoEngine.Multiplayer
         AcceptHandshake = 3, // NetAcceptHandshakeMsg
         ClientConnected = 4, // NetClientConnectedMsg
         ClientDisconnected = 5, // NetClientDisconnectedMsg
-        ChatMessage = 6 //NetChatMsg
+        Ping = 6, // NetPingMsg
+        Pong = 7, // NetPongMsg
+        ChatMessage = 8 //NetChatMsg
     }
 }
 

+ 22 - 0
Runtime/PingData.cs

@@ -0,0 +1,22 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace KairoEngine.Multiplayer
+{
+    [System.Serializable]
+    public class PingData
+    {
+        public int clientId = -1;
+        public int pingId = -1;
+        public float startTime = 0f;
+        public float endTime = 0f;
+        public int elapsedTime() => Mathf.RoundToInt((endTime - startTime) * 1000);
+
+        public PingData(int clientId, int pingId)
+        {
+            this.clientId = clientId;
+            this.pingId = pingId;
+        }
+    }
+}

+ 11 - 0
Runtime/PingData.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e33a1e88f3b0db1469e0a4937988624a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Runtime/Server/ClientList.cs

@@ -52,5 +52,14 @@ namespace KairoEngine.Multiplayer
                 }
             }
         }
+
+        public ClientData GetClientData(int connectionId)
+        {
+            for (int i = 0; i < clients.Count; i++)
+            {
+                if(clients[i].connectionId == connectionId) return clients[i];
+            }
+            return null;
+        }
     }
 }

+ 4 - 0
Runtime/Server/ServerActivityMessages.cs

@@ -29,6 +29,8 @@ namespace KairoEngine.Multiplayer
             NetMsgEvents.StartListening($"{server.eventStreamName}_ClientDisconnect", OnServerReceivedDataEvent);
             NetMsgEvents.StartListening($"{server.eventStreamName}_HandshakeReceived", OnServerReceivedDataEvent);
             NetMsgEvents.StartListening($"{server.eventStreamName}_AcceptingHandshake", OnServerReceivedDataEvent);
+            NetMsgEvents.StartListening($"{server.eventStreamName}_PingSent", OnServerReceivedDataEvent);
+            NetMsgEvents.StartListening($"{server.eventStreamName}_PongReceived", OnServerReceivedDataEvent);
         }
 
         private void OnDestroy()
@@ -44,6 +46,8 @@ namespace KairoEngine.Multiplayer
             NetMsgEvents.StopListening($"{server.eventStreamName}_ClientDisconnect", OnServerReceivedDataEvent);
             NetMsgEvents.StopListening($"{server.eventStreamName}_HandshakeReceived", OnServerReceivedDataEvent);
             NetMsgEvents.StopListening($"{server.eventStreamName}_AcceptingHandshake", OnServerReceivedDataEvent);
+            NetMsgEvents.StopListening($"{server.eventStreamName}_PingSent", OnServerReceivedDataEvent);
+            NetMsgEvents.StopListening($"{server.eventStreamName}_PongReceived", OnServerReceivedDataEvent);
         }
 
         public void OnServerEvent(string text)

+ 2 - 0
Runtime/Server/ServerNetMsgController.cs

@@ -77,9 +77,11 @@ namespace KairoEngine.Multiplayer
 
         private void SendBufferedData()
         {
+            if(!server.m_Driver.IsCreated) return;
             for (int i = 0; i < sendMsgBuffer.Count; i++)
             {
                 if(sendMsgBuffer[i].Count == 0) continue;
+                if(!server.m_Connections[i].IsCreated) continue;
                 DataStreamWriter writer = new DataStreamWriter();
                 server.m_Driver.BeginSend(server.m_Connections[i], out writer);
                 writer.WriteUInt((uint)networkTick.tick); // Current Tick Header

+ 4 - 0
Runtime/Server/ServerStatus.cs

@@ -32,6 +32,8 @@ namespace KairoEngine.Multiplayer
             NetMsgEvents.StartListening($"{server.eventStreamName}_ClientDisconnect", OnReceivePacket);
             NetMsgEvents.StartListening($"{server.eventStreamName}_HandshakeReceived", OnReceivePacket);
             NetMsgEvents.StartListening($"{server.eventStreamName}_AcceptingHandshake", OnSentPacket);
+            NetMsgEvents.StartListening($"{server.eventStreamName}_PingSent", OnSentPacket);
+            NetMsgEvents.StartListening($"{server.eventStreamName}_PongReceived", OnReceivePacket);
             GenericEvents.StartListening($"{server.eventStreamName}_OnTick", OnTick);
         }
 
@@ -42,6 +44,8 @@ namespace KairoEngine.Multiplayer
             NetMsgEvents.StopListening($"{server.eventStreamName}_ClientDisconnect", OnReceivePacket);
             NetMsgEvents.StopListening($"{server.eventStreamName}_HandshakeReceived", OnReceivePacket);
             NetMsgEvents.StopListening($"{server.eventStreamName}_AcceptingHandshake", OnSentPacket);
+            NetMsgEvents.StopListening($"{server.eventStreamName}_PingSent", OnSentPacket);
+            NetMsgEvents.StopListening($"{server.eventStreamName}_PongReceived", OnReceivePacket);
             GenericEvents.StopListening($"{server.eventStreamName}_OnTick", OnTick);
         }
 

+ 135 - 0
Runtime/Server/ServerSyncAjustmentSystem.cs

@@ -0,0 +1,135 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using KairoEngine.Core;
+using Sirenix.OdinInspector;
+using UniRx;
+
+namespace KairoEngine.Multiplayer
+{
+    [HideMonoScript]
+    public class ServerSyncAjustmentSystem : MonoBehaviour
+    {
+        public ServerBehaviour server;
+        public NetworkTick networkTick;
+        public ClientList clientList;
+        public int adjustmentInterval = 3000;
+        public int adjustmentIterations = 10;
+
+        private List<PingData> pings = new List<PingData>();
+        private bool serverIsRunning = false;
+        private bool adjusting = false;
+        private int adjustmentIterationCount = 0;
+        private CompositeDisposable adjustmentLoop;
+
+        private void OnEnable()
+        {
+            GenericEvents.StartListening($"{server.eventStreamName}_Listening", OnServerStart);
+            GenericEvents.StartListening($"{server.eventStreamName}_Stopped", OnServerStop);
+            GenericEvents.StartListening($"{server.eventStreamName}_OnTick", OnTick);
+            NetMsgEvents.StartListening($"{server.eventStreamName}_PingSent", OnPingSent);
+            NetMsgEvents.StartListening($"{server.eventStreamName}_PongReceived", OnPongReceived);
+        }
+
+        private void OnDisable()
+        {
+            GenericEvents.StopListening($"{server.eventStreamName}_Listening", OnServerStart);
+            GenericEvents.StopListening($"{server.eventStreamName}_Stopped", OnServerStop);
+            GenericEvents.StopListening($"{server.eventStreamName}_OnTick", OnTick);
+            NetMsgEvents.StopListening($"{server.eventStreamName}_PingSent", OnPingSent);
+            NetMsgEvents.StopListening($"{server.eventStreamName}_PongReceived", OnPongReceived);
+        }
+
+        private void OnServerStart(string text) 
+        {
+            serverIsRunning = true;
+            schedualedjustmentCalculation();
+        }
+
+        private void OnServerStop(string text)
+        {
+            serverIsRunning = false;
+            adjustmentLoop.Dispose();
+        }
+
+        private void OnTick(int tick)
+        {
+            if(!serverIsRunning) return;
+            if(!adjusting) return;
+            for (int i = 0; i < clientList.clients.Count; i++)
+            {
+                if(clientList.clients[i].isConnected) PingClient(i);
+            }
+            adjustmentIterationCount += 1;
+            if(adjustmentIterationCount >= adjustmentIterations)
+            {
+                // done
+                adjusting = false;
+            }
+        }
+
+        private void schedualedjustmentCalculation()
+        {
+            adjustmentLoop = Timer.ExecuteRealTime(adjustmentInterval, () => {
+                if(!serverIsRunning) return;
+                adjusting = true;
+                adjustmentIterationCount = 0;
+                schedualedjustmentCalculation();
+            });
+        }
+
+        private void PingClient(int i)
+        {
+            ClientData client = clientList.clients[i];
+            NetPingMsg netPingMsg = new NetPingMsg((uint)networkTick.tick);
+            server.SendNetMsgToClient(client.connectionId, (uint)NetOpCode.Ping, netPingMsg);
+            PingData ping = new PingData(client.connectionId, networkTick.tick);
+            pings.Add(ping);
+        }
+
+        private void OnPingSent(string text, int clientId, uint code, NetMsg netMsg) 
+        {
+            NetPingMsg msg = (NetPingMsg)netMsg;
+            PingData ping = GetPingData(clientId, (int)msg.pingId);
+            if(ping != null) ping.startTime = Time.realtimeSinceStartup;
+        }
+
+        private void OnPongReceived(string text, int clientId, uint code, NetMsg netMsg) 
+        {
+            NetPongMsg msg = (NetPongMsg)netMsg;
+            PingData ping = GetPingData(clientId, (int)msg.pingId);
+            if(ping != null) 
+            {
+                ping.endTime = Time.realtimeSinceStartup;
+                ClientData clientData = clientList.GetClientData(ping.clientId);
+                if(clientData != null) clientData.latency = CalculateLatency(clientData.connectionId);
+            }
+            else Debug.LogWarning($"Could not find ping data ({(int)msg.pingId})");
+        }
+
+        public PingData GetPingData(int clientId, int pingId)
+        {
+            for (int i = 0; i < pings.Count; i++)
+            {
+                if(pings[i].clientId == clientId && pings[i].pingId == pingId) return pings[i];
+            }
+            return null;
+        }
+
+        public int CalculateLatency(int clientId)
+        {
+            int total = 0;
+            int count = 0;
+            for (int i = 0; i < pings.Count; i++)
+            {
+                if(pings[i].clientId == clientId && pings[i].endTime != 0)
+                {
+                    total += pings[i].elapsedTime();
+                    count +=1;
+                }
+            }
+            return total/count;
+        }
+        
+    }
+}

+ 11 - 0
Runtime/Server/ServerSyncAjustmentSystem.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 101e533c774c4254989c53fee02aee89
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 55 - 0
Runtime/_NetMsgs/NetPingMsg.cs

@@ -0,0 +1,55 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Unity.Networking.Transport;
+
+namespace KairoEngine.Multiplayer
+{
+    public class NetPingMsg : NetMsg
+    {
+        public override uint code { get { return (uint)NetOpCode.Ping; }}
+
+        public uint pingId;
+        public NetPingMsg() {}
+
+        public NetPingMsg(uint pingId) 
+        {
+            this.pingId = pingId;
+        }
+
+        public override void Serialize(ref DataStreamWriter writer)
+        {
+            writer.WriteByte((byte)code);
+            writer.WriteUInt(pingId);
+        }
+
+        public override void Deserialize(ref DataStreamReader reader)
+        {
+            pingId = reader.ReadUInt();
+        }
+
+        public override void SendMessage(ServerBehaviour server, NetMsg netMsg, ref DataStreamWriter writer,  int clientId) 
+        {
+            if(server == null) return;
+            NetPingMsg netPingMsg = (NetPingMsg)netMsg;
+            string text = $"Sending ping to client (ID {clientId}, PingId = {netPingMsg.pingId})";
+            if(server.debug) Debug.Log(text);
+            NetMsgEvents.Trigger($"{server.eventStreamName}_PingSent", text, clientId, code, netPingMsg);
+            netPingMsg.Serialize(ref writer);
+        }
+
+        public override NetMsg ReceiveMessage(ClientBehaviour client, ref DataStreamReader stream) 
+        {
+            NetPingMsg netPingMsg = new NetPingMsg();
+            netPingMsg.Deserialize(ref stream);
+            return netPingMsg;
+        }
+
+        public override void ReadMessage(ClientBehaviour client) 
+        {
+            string text = $"Server sent a ping (PingId = {pingId})";
+            if(client.debug) Debug.Log(text);
+            NetMsgEvents.Trigger($"{client.eventStreamName}_PingReceived", text, -1, code, this);
+        }
+    }
+}

+ 11 - 0
Runtime/_NetMsgs/NetPingMsg.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c2c9a296a233e6240bbaffac5e7dbb81
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 55 - 0
Runtime/_NetMsgs/NetPongMsg.cs

@@ -0,0 +1,55 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using Unity.Networking.Transport;
+
+namespace KairoEngine.Multiplayer
+{
+    public class NetPongMsg : NetMsg
+    {
+        public override uint code { get { return (uint)NetOpCode.Pong; }}
+        public uint pingId;
+        public NetPongMsg() {}
+        public NetPongMsg(uint pingId) 
+        {
+            this.pingId = pingId;
+        }
+
+        public override void Serialize(ref DataStreamWriter writer)
+        {
+            writer.WriteByte((byte)code);
+            writer.WriteUInt(pingId);
+        }
+
+        public override void Deserialize(ref DataStreamReader reader)
+        {
+            pingId = reader.ReadUInt();
+        }
+
+        public override void SendMessage(ClientBehaviour client, NetMsg netMsg, ref DataStreamWriter writer ) 
+        {
+            if(client == null) return;
+            NetPongMsg netPongMsg = (NetPongMsg)netMsg;
+            string text = $"Responding pong ({netPongMsg.pingId}))";
+            if(client.debug) Debug.Log(text);
+            NetMsgEvents.Trigger($"{client.eventStreamName}_PongSent", text, -1, code, netPongMsg);
+            netPongMsg.Serialize(ref writer);
+        }
+
+        public override NetMsg ReceiveMessage(ServerBehaviour server, ref DataStreamReader stream, int clientId = -1) 
+        {
+            NetPongMsg netPongMsg = new NetPongMsg();
+            netPongMsg.Deserialize(ref stream);
+            string text = $"Received pong from client (ID {clientId}, PingId = {netPongMsg.pingId})";
+            if(server.debug) Debug.Log(text);
+            NetMsgEvents.Trigger($"{server.eventStreamName}_PongReceived", text, clientId, code, netPongMsg);
+            return netPongMsg;
+        }
+
+        public override void ReadMessage(ServerBehaviour server, int clientId = -1) 
+        {
+            
+        }
+
+    }
+}

+ 11 - 0
Runtime/_NetMsgs/NetPongMsg.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e187b23a9642f33408d9be04bae3ac61
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: