From b26e57728433f75a7fca162db055c5812d0fc223 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Mon, 12 Apr 2021 13:32:15 -0400 Subject: [PATCH 01/55] chore: MTT-635 Add a Snapshot class to the NetworkManager. First part --- .../Runtime/Core/NetworkBehaviour.cs | 27 +++++++++++---- .../Runtime/Core/NetworkManager.cs | 3 +- .../Runtime/Core/SnapshotSystem.cs | 33 +++++++++++++++++++ .../Runtime/Core/SnapshotSystem.cs.meta | 11 +++++++ 4 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs create mode 100644 com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 4d25dd0bdc..a9b1c6c6b9 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -42,6 +42,9 @@ public enum __NExec Client = 2 } + static private bool m_UseClassicDelta = true; + static private bool m_UseSnapshot = true; + #pragma warning disable 414 #pragma warning disable IDE1006 // disable naming rule violation check [NonSerialized] @@ -484,7 +487,7 @@ internal void InitializeVariables() private static ProfilerMarker s_NetworkBehaviourUpdate = new ProfilerMarker($"{nameof(NetworkBehaviour)}.{nameof(NetworkBehaviourUpdate)}"); #endif - internal static void NetworkBehaviourUpdate(NetworkManager networkManager) + internal static void NetworkBehaviourUpdate(NetworkManager networkManager, SnapshotSystem snapshot) { // Do not execute NetworkBehaviourUpdate more than once per network tick ushort tick = networkManager.NetworkTickSystem.GetTick(); @@ -513,7 +516,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager) // Sync just the variables for just the objects this client sees for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId); + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, snapshot); } } } @@ -534,7 +537,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager) { for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId); + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, snapshot); } } @@ -573,7 +576,7 @@ internal void PostNetworkVariableWrite() } } - internal void VariableUpdate(ulong clientId) + internal void VariableUpdate(ulong clientId, SnapshotSystem snapshot) { if (!m_VarInit) { @@ -581,21 +584,33 @@ internal void VariableUpdate(ulong clientId) } PreNetworkVariableWrite(); - NetworkVariableUpdate(clientId); + NetworkVariableUpdate(clientId, snapshot); } private readonly List m_NetworkVariableIndexesToReset = new List(); private readonly HashSet m_NetworkVariableIndexesToResetSet = new HashSet(); - private void NetworkVariableUpdate(ulong clientId) + private void NetworkVariableUpdate(ulong clientId, SnapshotSystem snapshot) { if (!CouldHaveDirtyNetworkVariables()) { return; } + if (m_UseSnapshot) + { + for (int k = 0; k < NetworkVariableFields.Count; k++) + { + snapshot.Store(NetworkObjectId, k, NetworkVariableFields[k]); + } + } + for (int j = 0; j < m_ChannelMappedNetworkVariableIndexes.Count; j++) { + if (!m_UseClassicDelta) + { + break; + } using (var buffer = PooledNetworkBuffer.Get()) { using (var writer = PooledNetworkWriter.Get(buffer)) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index a2258293d0..e2a6790880 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -54,6 +54,7 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem, IProfilableTr internal RpcQueueContainer RpcQueueContainer { get; private set; } internal NetworkTickSystem NetworkTickSystem { get; private set; } + internal SnapshotSystem SnapshotSystem { get; private set; } public NetworkPrefabHandler PrefabHandler { get; private set; } /// @@ -898,7 +899,7 @@ private void OnNetworkPreUpdate() if (NetworkConfig.EnableNetworkVariable) { // Do NetworkVariable updates - NetworkBehaviour.NetworkBehaviourUpdate(this); + NetworkBehaviour.NetworkBehaviourUpdate(this, SnapshotSystem); } if (!IsServer && NetworkConfig.EnableMessageBuffering) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs new file mode 100644 index 0000000000..3875d65b04 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -0,0 +1,33 @@ +using System; +using MLAPI.NetworkVariable; + +namespace MLAPI +{ + public class SnapshotSystem : INetworkUpdateSystem, IDisposable + { + public SnapshotSystem() + { + this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); + } + + public void Dispose() + { + this.UnregisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); + } + + public void NetworkUpdate(NetworkUpdateStage updateStage) + { + switch (updateStage) + { + case NetworkUpdateStage.EarlyUpdate: + break; + } + } + + public void Store(ulong networkObjectId, int index, INetworkVariable networkVariable) + { + + } + + } +} diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs.meta b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs.meta new file mode 100644 index 0000000000..27ffd35f4e --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c275febadb27c4d18b41218e3353b84b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 4a59e2eacb984bbc020205031c50a712422010d9 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 13 Apr 2021 15:55:08 -0400 Subject: [PATCH 02/55] chore: MTT-635 Some part of Snapshot system. Sending an empty snapshot every frame, having an empty NativeContainer. New channel and messagetype. --- .../Runtime/Configuration/NetworkConstants.cs | 3 +- .../Runtime/Core/NetworkManager.cs | 21 +++++- .../Runtime/Core/SnapshotSystem.cs | 68 +++++++++++++++++++ .../Runtime/Transports/NetworkTransport.cs | 2 + .../UnityConnectSettings.asset | 1 + 5 files changed, 93 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs index 4d1c125946..f899c72fbf 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs @@ -22,6 +22,7 @@ internal static class NetworkConstants internal const byte DESTROY_OBJECTS = 21; internal const byte NAMED_MESSAGE = 22; internal const byte SERVER_LOG = 23; + internal const byte SNAPSHOT_DATA = 25; internal const byte SERVER_RPC = 30; internal const byte CLIENT_RPC = 31; internal const byte INVALID = 32; @@ -53,7 +54,7 @@ internal static class NetworkConstants "NAMED_MESSAGE", "SERVER_LOG", "", - "", + "SNAPSHOT_DATA", "", "", "", diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index e2a6790880..661becf27d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -343,7 +343,16 @@ private void Init(bool server) return; } - // This 'if' should never enter + //This 'if' should never enter + if (SnapshotSystem != null) + { + SnapshotSystem.Dispose(); + SnapshotSystem = null; + } + + SnapshotSystem = new SnapshotSystem(); + + //This 'if' should never enter if (NetworkTickSystem != null) { NetworkTickSystem.Dispose(); @@ -765,6 +774,12 @@ public void Shutdown() RpcQueueContainer = null; } + if (SnapshotSystem != null) + { + SnapshotSystem.Dispose(); + SnapshotSystem = null; + } + if (NetworkTickSystem != null) { NetworkTickSystem.Dispose(); @@ -1157,6 +1172,10 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, switch (messageType) { + case NetworkConstants.SNAPSHOT_DATA: + UnityEngine.Debug.Log("Received Snapshot Data"); + + break; case NetworkConstants.CONNECTION_REQUEST: if (IsServer) { diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 3875d65b04..45959d3869 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -1,10 +1,33 @@ using System; +using MLAPI.Configuration; +using MLAPI.Messaging; using MLAPI.NetworkVariable; +using MLAPI.Serialization.Pooled; +using MLAPI.Transports; +using Unity.Collections; +using UnityEngine; namespace MLAPI { + internal struct Entry + { + public ulong m_NetworkObjectId; // the NetworkObjectId of the owning GameObject + public ushort m_Index; // the index of the variable in this GameObject + public ushort m_Position; // the offset in our m_Buffer + public ushort m_Length; // the length of the data in m_Buffer + + public const int k_NotFound = -1; + } + public class SnapshotSystem : INetworkUpdateSystem, IDisposable { + private NativeArray m_Entries = new NativeArray(64, Allocator.Persistent); + private NativeArray m_Buffer = new NativeArray(20000, Allocator.Persistent); + + private int m_LastEntry = 0; + private int m_Beg = 0; + private int m_End = 0; + public SnapshotSystem() { this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); @@ -20,13 +43,58 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) switch (updateStage) { case NetworkUpdateStage.EarlyUpdate: + + for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) + { + var clientId = NetworkManager.Singleton.ConnectedClientsList[i].ClientId; + // todo: code here that just sends keepalive unreliable packets + var buffer = PooledNetworkBuffer.Get(); + NetworkManager.Singleton.MessageSender.Send(clientId, NetworkConstants.SNAPSHOT_DATA, NetworkChannel.SnapshotExchange, buffer); + buffer.Dispose(); + // todo: code here that reads the native array and sends it + + } break; } } + public int Find(ulong networkObjectId, int index) + { + for(int i = 0; i < m_LastEntry; i++) + { + var entry = m_Entries[i]; + if (entry.m_NetworkObjectId == networkObjectId && entry.m_Index == index) + { + return i; + } + } + + return Entry.k_NotFound; + } + + public int AddEntry(ulong networkObjectId, int index) + { + int pos = m_LastEntry; + var entry = m_Entries[m_LastEntry++]; + + entry.m_NetworkObjectId = networkObjectId; + entry.m_Index = (ushort)index; + entry.m_Position = 0; + entry.m_Length = 0; + + return pos; + } + public void Store(ulong networkObjectId, int index, INetworkVariable networkVariable) { + int pos = Find(networkObjectId, index); + + if (pos == Entry.k_NotFound) + { + pos = AddEntry(networkObjectId, index); + } + // todo: write var into buffer, possibly adjusting entry's position and length } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs b/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs index a3a63d8934..53685f95e6 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs @@ -18,6 +18,7 @@ public enum NetworkChannel : byte NavAgentState, NavAgentCorrection, NetworkVariable, //todo: this channel will be used for snapshotting and should then go from reliable to unreliable + SnapshotExchange, ChannelUnused, // <<-- must be present, and must be last }; @@ -103,6 +104,7 @@ public TransportChannel[] MLAPI_CHANNELS // todo: Currently, fragmentation support needed to deal with oversize packets encounterable with current pre-snapshot code". // todo: once we have snapshotting able to deal with missing frame, this should be unreliable new TransportChannel(NetworkChannel.NetworkVariable, NetworkDelivery.ReliableFragmentedSequenced), + new TransportChannel(NetworkChannel.SnapshotExchange, NetworkDelivery.Unreliable), }; /// diff --git a/testproject/ProjectSettings/UnityConnectSettings.asset b/testproject/ProjectSettings/UnityConnectSettings.asset index fa0b146579..6125b308af 100644 --- a/testproject/ProjectSettings/UnityConnectSettings.asset +++ b/testproject/ProjectSettings/UnityConnectSettings.asset @@ -9,6 +9,7 @@ UnityConnectSettings: m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com m_TestInitMode: 0 CrashReportingSettings: m_EventUrl: https://perf-events.cloud.unity3d.com From 4c0e1136071ed4016b78585055872c8a5995d47d Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 15 Apr 2021 17:58:00 -0400 Subject: [PATCH 03/55] chore: MTT-635 Dropping native arrays for now, adding a bit of structure around writing and sending the snapshot --- .../Runtime/Core/NetworkManager.cs | 4 +- .../Runtime/Core/SnapshotSystem.cs | 89 ++++++++++++++++--- .../Messaging/InternalMessageHandler.cs | 6 ++ 3 files changed, 84 insertions(+), 15 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 661becf27d..053460ae84 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -364,7 +364,7 @@ private void Init(bool server) // This should never happen, but in the event that it does there should be (at a minimum) a unity error logged. if (RpcQueueContainer != null) { - UnityEngine.Debug.LogError("Init was invoked, but rpcQueueContainer was already initialized! (destroying previous instance)"); + Debug.LogError("Init was invoked, but rpcQueueContainer was already initialized! (destroying previous instance)"); RpcQueueContainer.Dispose(); RpcQueueContainer = null; } @@ -1174,7 +1174,7 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, { case NetworkConstants.SNAPSHOT_DATA: UnityEngine.Debug.Log("Received Snapshot Data"); - + InternalMessageHandler.HandleSnapshot(clientId, messageStream); break; case NetworkConstants.CONNECTION_REQUEST: if (IsServer) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 45959d3869..67139a88de 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -2,9 +2,9 @@ using MLAPI.Configuration; using MLAPI.Messaging; using MLAPI.NetworkVariable; +using MLAPI.Serialization; using MLAPI.Serialization.Pooled; using MLAPI.Transports; -using Unity.Collections; using UnityEngine; namespace MLAPI @@ -21,10 +21,10 @@ internal struct Entry public class SnapshotSystem : INetworkUpdateSystem, IDisposable { - private NativeArray m_Entries = new NativeArray(64, Allocator.Persistent); - private NativeArray m_Buffer = new NativeArray(20000, Allocator.Persistent); - + Entry[] m_Entries = new Entry[64]; private int m_LastEntry = 0; + + byte[] m_Buffer = new byte[20000]; private int m_Beg = 0; private int m_End = 0; @@ -47,23 +47,52 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) { var clientId = NetworkManager.Singleton.ConnectedClientsList[i].ClientId; - // todo: code here that just sends keepalive unreliable packets + + + // todo: code here that reads the buffer and sends it + // todo: demonstrates how to send a byte[] section var buffer = PooledNetworkBuffer.Get(); + + WriteIndex(buffer); + WriteBuffer(buffer); + NetworkManager.Singleton.MessageSender.Send(clientId, NetworkConstants.SNAPSHOT_DATA, NetworkChannel.SnapshotExchange, buffer); buffer.Dispose(); - // todo: code here that reads the native array and sends it - } + + DebugDisplayStore(m_Entries, m_LastEntry); break; } } + private void WriteIndex(NetworkBuffer buffer) + { + using (var writer = PooledNetworkWriter.Get(buffer)) + { + writer.WriteInt16((short)m_LastEntry); + + for (var i = 0; i < m_LastEntry; i++) + { + writer.WriteUInt64(m_Entries[i].m_NetworkObjectId); + writer.WriteUInt16(m_Entries[i].m_Index); + writer.WriteUInt16(m_Entries[i].m_Position); + writer.WriteUInt16(m_Entries[i].m_Length); + } + } + } + + private void WriteBuffer(NetworkBuffer buffer) + { + // todo: this sends the whole buffer + // we'll need to build a per-client list + buffer.Write(m_Buffer, 0, m_End); + } + public int Find(ulong networkObjectId, int index) { - for(int i = 0; i < m_LastEntry; i++) + for (int i = 0; i < m_LastEntry; i++) { - var entry = m_Entries[i]; - if (entry.m_NetworkObjectId == networkObjectId && entry.m_Index == index) + if (m_Entries[i].m_NetworkObjectId == networkObjectId && m_Entries[i].m_Index == index) { return i; } @@ -74,17 +103,28 @@ public int Find(ulong networkObjectId, int index) public int AddEntry(ulong networkObjectId, int index) { - int pos = m_LastEntry; - var entry = m_Entries[m_LastEntry++]; + var pos = m_LastEntry++; + var entry = m_Entries[pos]; entry.m_NetworkObjectId = networkObjectId; entry.m_Index = (ushort)index; entry.m_Position = 0; entry.m_Length = 0; + m_Entries[pos] = entry; return pos; } + public void AllocateEntry(int pos, long size) + { + // todo: deal with free space + // todo: deal with full buffer + + m_Entries[pos].m_Position = (ushort)m_Beg; + m_Entries[pos].m_Length = (ushort)size; + m_Beg += (int)size; + } + public void Store(ulong networkObjectId, int index, INetworkVariable networkVariable) { int pos = Find(networkObjectId, index); @@ -94,8 +134,31 @@ public void Store(ulong networkObjectId, int index, INetworkVariable networkVari pos = AddEntry(networkObjectId, index); } - // todo: write var into buffer, possibly adjusting entry's position and length + // write var into buffer, possibly adjusting entry's position and length + using (var varBuffer = PooledNetworkBuffer.Get()) + { + networkVariable.WriteDelta(varBuffer); + if (varBuffer.Length > m_Entries[pos].m_Length) + { + // allocate this Entry's buffer + AllocateEntry(pos, varBuffer.Length); + } + + // Copy the serialized NetworkVariable into our buffer + Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Buffer, m_Entries[pos].m_Position, (int)varBuffer.Length); + + } } + private static void DebugDisplayStore(Entry[] entries, int entryLength) + { + string table = "=== Snapshot table ===\n"; + for (int i = 0; i < entryLength; i++) + { + table += string.Format("NetworkObject {0}:{1} range [{2}, {3}]\n", entries[i].m_NetworkObjectId, + entries[i].m_Index, entries[i].m_Position, entries[i].m_Position + entries[i].m_Length); + } + Debug.Log(table); + } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index 232d9e09d4..ef9b3c4b36 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -572,5 +572,11 @@ public void HandleNetworkLog(ulong clientId, Stream stream) s_HandleNetworkLog.End(); #endif } + + internal static void HandleSnapshot(ulong clientId, Stream messageStream) + { + //todo + + } } } From 4cd30348de0c4db0f93d961a2eb8bdcf6284f055 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Sun, 18 Apr 2021 12:02:12 -0400 Subject: [PATCH 04/55] chore: MTT-635 Added EntryBlock. Used separate objects for the snapshot to send and the received one --- .../Runtime/Core/SnapshotSystem.cs | 148 ++++++++++++------ .../Messaging/InternalMessageHandler.cs | 3 +- 2 files changed, 100 insertions(+), 51 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 67139a88de..2ac16476ee 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.IO; using MLAPI.Configuration; using MLAPI.Messaging; using MLAPI.NetworkVariable; @@ -6,6 +8,7 @@ using MLAPI.Serialization.Pooled; using MLAPI.Transports; using UnityEngine; +using UnityEngine.UIElements; namespace MLAPI { @@ -19,10 +22,44 @@ internal struct Entry public const int k_NotFound = -1; } + internal class EntryBlock + { + public Entry[] m_Entries = new Entry[64]; + public int m_LastEntry = 0; + + public int Find(ulong networkObjectId, int index) + { + for (int i = 0; i < m_LastEntry; i++) + { + if (m_Entries[i].m_NetworkObjectId == networkObjectId && m_Entries[i].m_Index == index) + { + return i; + } + } + + return Entry.k_NotFound; + } + + public int AddEntry(ulong networkObjectId, int index) + { + var pos = m_LastEntry++; + var entry = m_Entries[pos]; + + entry.m_NetworkObjectId = networkObjectId; + entry.m_Index = (ushort)index; + entry.m_Position = 0; + entry.m_Length = 0; + m_Entries[pos] = entry; + + return pos; + } + + } + public class SnapshotSystem : INetworkUpdateSystem, IDisposable { - Entry[] m_Entries = new Entry[64]; - private int m_LastEntry = 0; + private EntryBlock m_Snapshot = new EntryBlock(); + private EntryBlock m_ReceivedSnapshot = new EntryBlock(); byte[] m_Buffer = new byte[20000]; private int m_Beg = 0; @@ -44,13 +81,13 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) { case NetworkUpdateStage.EarlyUpdate: + // todo: ConnectedClientsList is only valid on the host for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) { var clientId = NetworkManager.Singleton.ConnectedClientsList[i].ClientId; - // todo: code here that reads the buffer and sends it - // todo: demonstrates how to send a byte[] section + // Send the entry index and the buffer where the variables are serialized var buffer = PooledNetworkBuffer.Get(); WriteIndex(buffer); @@ -60,7 +97,8 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) buffer.Dispose(); } - DebugDisplayStore(m_Entries, m_LastEntry); + DebugDisplayStore(m_Snapshot.m_Entries, m_Snapshot.m_LastEntry, "Entries"); + DebugDisplayStore(m_Snapshot.m_Entries, m_Snapshot.m_LastEntry, "Received Entries"); break; } } @@ -69,14 +107,14 @@ private void WriteIndex(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteInt16((short)m_LastEntry); + writer.WriteInt16((short)m_Snapshot.m_LastEntry); - for (var i = 0; i < m_LastEntry; i++) + for (var i = 0; i < m_Snapshot.m_LastEntry; i++) { - writer.WriteUInt64(m_Entries[i].m_NetworkObjectId); - writer.WriteUInt16(m_Entries[i].m_Index); - writer.WriteUInt16(m_Entries[i].m_Position); - writer.WriteUInt16(m_Entries[i].m_Length); + writer.WriteUInt64(m_Snapshot.m_Entries[i].m_NetworkObjectId); + writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Index); + writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Position); + writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Length); } } } @@ -88,71 +126,83 @@ private void WriteBuffer(NetworkBuffer buffer) buffer.Write(m_Buffer, 0, m_End); } - public int Find(ulong networkObjectId, int index) - { - for (int i = 0; i < m_LastEntry; i++) - { - if (m_Entries[i].m_NetworkObjectId == networkObjectId && m_Entries[i].m_Index == index) - { - return i; - } - } - - return Entry.k_NotFound; - } - - public int AddEntry(ulong networkObjectId, int index) - { - var pos = m_LastEntry++; - var entry = m_Entries[pos]; - - entry.m_NetworkObjectId = networkObjectId; - entry.m_Index = (ushort)index; - entry.m_Position = 0; - entry.m_Length = 0; - m_Entries[pos] = entry; - - return pos; - } - - public void AllocateEntry(int pos, long size) + private void AllocateEntry(ref Entry entry, long size) { // todo: deal with free space // todo: deal with full buffer - m_Entries[pos].m_Position = (ushort)m_Beg; - m_Entries[pos].m_Length = (ushort)size; + entry.m_Position = (ushort)m_Beg; + entry.m_Length = (ushort)size; m_Beg += (int)size; } public void Store(ulong networkObjectId, int index, INetworkVariable networkVariable) { - int pos = Find(networkObjectId, index); - + int pos = m_Snapshot.Find(networkObjectId, index); if (pos == Entry.k_NotFound) { - pos = AddEntry(networkObjectId, index); + pos = m_Snapshot.AddEntry(networkObjectId, index); } // write var into buffer, possibly adjusting entry's position and length using (var varBuffer = PooledNetworkBuffer.Get()) { networkVariable.WriteDelta(varBuffer); - if (varBuffer.Length > m_Entries[pos].m_Length) + if (varBuffer.Length > m_Snapshot.m_Entries[pos].m_Length) { // allocate this Entry's buffer - AllocateEntry(pos, varBuffer.Length); + AllocateEntry(ref m_Snapshot.m_Entries[pos], varBuffer.Length); } // Copy the serialized NetworkVariable into our buffer - Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Buffer, m_Entries[pos].m_Position, (int)varBuffer.Length); + Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Buffer, m_Snapshot.m_Entries[pos].m_Position, (int)varBuffer.Length); + } + } + + public void ReadSnapshot(Stream snapshotStream) + { + // todo: this is sub-optimal, review + List entriesPositionToRead = new List(); + + using (var reader = PooledNetworkReader.Get(snapshotStream)) + { + Entry entry; + short entries = reader.ReadInt16(); + Debug.Log(string.Format("Got {0} entries", entries)); + + for (var i = 0; i < entries; i++) + { + entry.m_NetworkObjectId = reader.ReadUInt64(); + entry.m_Index = reader.ReadUInt16(); + entry.m_Position = reader.ReadUInt16(); + entry.m_Length = reader.ReadUInt16(); + + int pos = m_ReceivedSnapshot.Find(entry.m_NetworkObjectId, entry.m_Index); + if (pos == Entry.k_NotFound) + { + pos = m_ReceivedSnapshot.AddEntry(entry.m_NetworkObjectId, entry.m_Index); + } + + if (m_ReceivedSnapshot.m_Entries[pos].m_Length < entry.m_Length) + { + AllocateEntry(ref entry, entry.m_Length); + } + m_ReceivedSnapshot.m_Entries[pos] = entry; + + entriesPositionToRead.Add(pos); + } + } + + foreach (var pos in entriesPositionToRead) + { + snapshotStream.Read(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, m_ReceivedSnapshot.m_Entries[pos].m_Length); } } - private static void DebugDisplayStore(Entry[] entries, int entryLength) + private void DebugDisplayStore(Entry[] entries, int entryLength, string name) { - string table = "=== Snapshot table ===\n"; + string table = "=== Snapshot table === " + name + " ===\n"; for (int i = 0; i < entryLength; i++) { table += string.Format("NetworkObject {0}:{1} range [{2}, {3}]\n", entries[i].m_NetworkObjectId, diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index ef9b3c4b36..fe2972663d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -575,8 +575,7 @@ public void HandleNetworkLog(ulong clientId, Stream stream) internal static void HandleSnapshot(ulong clientId, Stream messageStream) { - //todo - + NetworkManager.Singleton.SnapshotSystem.ReadSnapshot(messageStream); } } } From 97c8200cd0a710dee8d3c8f8ab4998a2d40fa563 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Mon, 19 Apr 2021 21:15:56 -0400 Subject: [PATCH 05/55] chore: MTT-635 minor, constant for size, improved comment --- com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 2ac16476ee..c517318579 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -24,7 +24,9 @@ internal struct Entry internal class EntryBlock { - public Entry[] m_Entries = new Entry[64]; + private const int k_MaxVariables = 64; + + public Entry[] m_Entries = new Entry[k_MaxVariables]; public int m_LastEntry = 0; public int Find(ulong networkObjectId, int index) @@ -161,7 +163,7 @@ public void Store(ulong networkObjectId, int index, INetworkVariable networkVari public void ReadSnapshot(Stream snapshotStream) { - // todo: this is sub-optimal, review + // todo: this is sub-optimal, as it allocates. Review List entriesPositionToRead = new List(); From 25275059651cba5e1fb34302e21fdba86988ce42 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 21 Apr 2021 18:19:53 -0400 Subject: [PATCH 06/55] MTT-635 minor, adjusting code to minimize diffs, for a first commit --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs | 2 +- com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index a9b1c6c6b9..8030cdc54d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -43,7 +43,7 @@ public enum __NExec } static private bool m_UseClassicDelta = true; - static private bool m_UseSnapshot = true; + static private bool m_UseSnapshot = false; #pragma warning disable 414 #pragma warning disable IDE1006 // disable naming rule violation check diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 053460ae84..d8e45c9516 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -364,7 +364,7 @@ private void Init(bool server) // This should never happen, but in the event that it does there should be (at a minimum) a unity error logged. if (RpcQueueContainer != null) { - Debug.LogError("Init was invoked, but rpcQueueContainer was already initialized! (destroying previous instance)"); + UnityEngine.Debug.LogError("Init was invoked, but rpcQueueContainer was already initialized! (destroying previous instance)"); RpcQueueContainer.Dispose(); RpcQueueContainer = null; } From 600279f476a6f1305c1d8153c24ae332849b0a44 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 22 Apr 2021 10:44:01 -0400 Subject: [PATCH 07/55] feat: MTT-635 code review comments --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs | 2 ++ com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 8030cdc54d..f021e6da06 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -42,6 +42,8 @@ public enum __NExec Client = 2 } + // todo: transitional. For the next release, only Snapshot should remain + // The booleans allow iterative development and testing in the meantime static private bool m_UseClassicDelta = true; static private bool m_UseSnapshot = false; diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index c517318579..cfe538bec8 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -63,6 +63,7 @@ public class SnapshotSystem : INetworkUpdateSystem, IDisposable private EntryBlock m_Snapshot = new EntryBlock(); private EntryBlock m_ReceivedSnapshot = new EntryBlock(); + // todo: split this buffer into parts per EntryBlock. Move the beg and end marker into an associated class, too byte[] m_Buffer = new byte[20000]; private int m_Beg = 0; private int m_End = 0; From 068e1f9bcaf27d3f697fd45179bae7e401f86cc5 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 27 Apr 2021 21:32:24 -0400 Subject: [PATCH 08/55] feat: snapshot system, in-progress stuff before moving to a common branch --- .../Runtime/Core/NetworkBehaviour.cs | 14 +++---- .../Runtime/Core/SnapshotSystem.cs | 40 +++++++++++++------ 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index f021e6da06..750c3652c7 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -45,7 +45,7 @@ public enum __NExec // todo: transitional. For the next release, only Snapshot should remain // The booleans allow iterative development and testing in the meantime static private bool m_UseClassicDelta = true; - static private bool m_UseSnapshot = false; + static private bool m_UseSnapshot = true; #pragma warning disable 414 #pragma warning disable IDE1006 // disable naming rule violation check @@ -518,7 +518,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps // Sync just the variables for just the objects this client sees for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, k, snapshot); } } } @@ -539,7 +539,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps { for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, k, snapshot); } } @@ -578,7 +578,7 @@ internal void PostNetworkVariableWrite() } } - internal void VariableUpdate(ulong clientId, SnapshotSystem snapshot) + internal void VariableUpdate(ulong clientId, int behaviourIndex, SnapshotSystem snapshot) { if (!m_VarInit) { @@ -586,13 +586,13 @@ internal void VariableUpdate(ulong clientId, SnapshotSystem snapshot) } PreNetworkVariableWrite(); - NetworkVariableUpdate(clientId, snapshot); + NetworkVariableUpdate(clientId, behaviourIndex, snapshot); } private readonly List m_NetworkVariableIndexesToReset = new List(); private readonly HashSet m_NetworkVariableIndexesToResetSet = new HashSet(); - private void NetworkVariableUpdate(ulong clientId, SnapshotSystem snapshot) + private void NetworkVariableUpdate(ulong clientId, int behaviourIndex, SnapshotSystem snapshot) { if (!CouldHaveDirtyNetworkVariables()) { @@ -603,7 +603,7 @@ private void NetworkVariableUpdate(ulong clientId, SnapshotSystem snapshot) { for (int k = 0; k < NetworkVariableFields.Count; k++) { - snapshot.Store(NetworkObjectId, k, NetworkVariableFields[k]); + snapshot.Store(NetworkObjectId, behaviourIndex, k, NetworkVariableFields[k]); } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index cfe538bec8..ec81368358 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -15,7 +15,8 @@ namespace MLAPI internal struct Entry { public ulong m_NetworkObjectId; // the NetworkObjectId of the owning GameObject - public ushort m_Index; // the index of the variable in this GameObject + public ushort m_BehaviourIndex; // the index of the behaviour in this GameObject + public ushort m_VariableIndex; // the index of the variable in this NetworkBehaviour public ushort m_Position; // the offset in our m_Buffer public ushort m_Length; // the length of the data in m_Buffer @@ -33,7 +34,7 @@ public int Find(ulong networkObjectId, int index) { for (int i = 0; i < m_LastEntry; i++) { - if (m_Entries[i].m_NetworkObjectId == networkObjectId && m_Entries[i].m_Index == index) + if (m_Entries[i].m_NetworkObjectId == networkObjectId && m_Entries[i].m_VariableIndex == index) { return i; } @@ -42,13 +43,14 @@ public int Find(ulong networkObjectId, int index) return Entry.k_NotFound; } - public int AddEntry(ulong networkObjectId, int index) + public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex) { var pos = m_LastEntry++; var entry = m_Entries[pos]; entry.m_NetworkObjectId = networkObjectId; - entry.m_Index = (ushort)index; + entry.m_BehaviourIndex = (ushort)behaviourIndex; + entry.m_VariableIndex = (ushort)variableIndex; entry.m_Position = 0; entry.m_Length = 0; m_Entries[pos] = entry; @@ -115,7 +117,8 @@ private void WriteIndex(NetworkBuffer buffer) for (var i = 0; i < m_Snapshot.m_LastEntry; i++) { writer.WriteUInt64(m_Snapshot.m_Entries[i].m_NetworkObjectId); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Index); + writer.WriteUInt16(m_Snapshot.m_Entries[i].m_BehaviourIndex); + writer.WriteUInt16(m_Snapshot.m_Entries[i].m_VariableIndex); writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Position); writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Length); } @@ -139,12 +142,12 @@ private void AllocateEntry(ref Entry entry, long size) m_Beg += (int)size; } - public void Store(ulong networkObjectId, int index, INetworkVariable networkVariable) + public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { - int pos = m_Snapshot.Find(networkObjectId, index); + int pos = m_Snapshot.Find(networkObjectId, variableIndex); if (pos == Entry.k_NotFound) { - pos = m_Snapshot.AddEntry(networkObjectId, index); + pos = m_Snapshot.AddEntry(networkObjectId, behaviourIndex, variableIndex); } // write var into buffer, possibly adjusting entry's position and length @@ -177,14 +180,15 @@ public void ReadSnapshot(Stream snapshotStream) for (var i = 0; i < entries; i++) { entry.m_NetworkObjectId = reader.ReadUInt64(); - entry.m_Index = reader.ReadUInt16(); + entry.m_BehaviourIndex = reader.ReadUInt16(); + entry.m_VariableIndex = reader.ReadUInt16(); entry.m_Position = reader.ReadUInt16(); entry.m_Length = reader.ReadUInt16(); - int pos = m_ReceivedSnapshot.Find(entry.m_NetworkObjectId, entry.m_Index); + int pos = m_ReceivedSnapshot.Find(entry.m_NetworkObjectId, entry.m_VariableIndex); if (pos == Entry.k_NotFound) { - pos = m_ReceivedSnapshot.AddEntry(entry.m_NetworkObjectId, entry.m_Index); + pos = m_ReceivedSnapshot.AddEntry(entry.m_NetworkObjectId, entry.m_BehaviourIndex, entry.m_VariableIndex); } if (m_ReceivedSnapshot.m_Entries[pos].m_Length < entry.m_Length) @@ -200,6 +204,18 @@ public void ReadSnapshot(Stream snapshotStream) foreach (var pos in entriesPositionToRead) { snapshotStream.Read(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, m_ReceivedSnapshot.m_Entries[pos].m_Length); + + var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects; + + if (spawnedObjects.ContainsKey(m_ReceivedSnapshot.m_Entries[pos].m_NetworkObjectId)) + { + var behaviour = spawnedObjects[m_ReceivedSnapshot.m_Entries[pos].m_NetworkObjectId] + .GetNetworkBehaviourAtOrderIndex(m_ReceivedSnapshot.m_Entries[pos].m_BehaviourIndex); + var nv = behaviour.NetworkVariableFields[m_ReceivedSnapshot.m_Entries[pos].m_VariableIndex]; + + MemoryStream stream = new MemoryStream(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, m_ReceivedSnapshot.m_Entries[pos].m_Length); + nv.ReadDelta(stream, false, 0, 0); + } } } @@ -209,7 +225,7 @@ private void DebugDisplayStore(Entry[] entries, int entryLength, string name) for (int i = 0; i < entryLength; i++) { table += string.Format("NetworkObject {0}:{1} range [{2}, {3}]\n", entries[i].m_NetworkObjectId, - entries[i].m_Index, entries[i].m_Position, entries[i].m_Position + entries[i].m_Length); + entries[i].m_VariableIndex, entries[i].m_Position, entries[i].m_Position + entries[i].m_Length); } Debug.Log(table); } From 4c2987555e9d10d4f60e35374773b75709c66baf Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Fri, 30 Apr 2021 16:45:44 -0400 Subject: [PATCH 09/55] feat: snapshot, incremental progress --- .../Runtime/Core/NetworkBehaviour.cs | 2 +- .../Runtime/Core/SnapshotSystem.cs | 74 ++++++++++++------- testproject/Assets/Prefabs/PlayerCube.prefab | 6 +- 3 files changed, 52 insertions(+), 30 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 750c3652c7..ae79951b5b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -44,7 +44,7 @@ public enum __NExec // todo: transitional. For the next release, only Snapshot should remain // The booleans allow iterative development and testing in the meantime - static private bool m_UseClassicDelta = true; + static private bool m_UseClassicDelta = false; static private bool m_UseSnapshot = true; #pragma warning disable 414 diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index ec81368358..bbcd184ff7 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -12,11 +12,16 @@ namespace MLAPI { - internal struct Entry + internal struct Key { public ulong m_NetworkObjectId; // the NetworkObjectId of the owning GameObject public ushort m_BehaviourIndex; // the index of the behaviour in this GameObject public ushort m_VariableIndex; // the index of the variable in this NetworkBehaviour + } + internal struct Entry + { + public Key key; + public ushort m_TickWritten; // the network tick at which this variable was set public ushort m_Position; // the offset in our m_Buffer public ushort m_Length; // the length of the data in m_Buffer @@ -30,11 +35,13 @@ internal class EntryBlock public Entry[] m_Entries = new Entry[k_MaxVariables]; public int m_LastEntry = 0; - public int Find(ulong networkObjectId, int index) + public int Find(Key key) { for (int i = 0; i < m_LastEntry; i++) { - if (m_Entries[i].m_NetworkObjectId == networkObjectId && m_Entries[i].m_VariableIndex == index) + if (m_Entries[i].key.m_NetworkObjectId == key.m_NetworkObjectId && + m_Entries[i].key.m_BehaviourIndex == key.m_BehaviourIndex && + m_Entries[i].key.m_VariableIndex == key.m_VariableIndex) { return i; } @@ -48,9 +55,10 @@ public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex var pos = m_LastEntry++; var entry = m_Entries[pos]; - entry.m_NetworkObjectId = networkObjectId; - entry.m_BehaviourIndex = (ushort)behaviourIndex; - entry.m_VariableIndex = (ushort)variableIndex; + entry.key.m_NetworkObjectId = networkObjectId; + entry.key.m_BehaviourIndex = (ushort)behaviourIndex; + entry.key.m_VariableIndex = (ushort)variableIndex; + entry.m_TickWritten = 0; entry.m_Position = 0; entry.m_Length = 0; m_Entries[pos] = entry; @@ -116,9 +124,10 @@ private void WriteIndex(NetworkBuffer buffer) for (var i = 0; i < m_Snapshot.m_LastEntry; i++) { - writer.WriteUInt64(m_Snapshot.m_Entries[i].m_NetworkObjectId); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_BehaviourIndex); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_VariableIndex); + writer.WriteUInt64(m_Snapshot.m_Entries[i].key.m_NetworkObjectId); + writer.WriteUInt16(m_Snapshot.m_Entries[i].key.m_BehaviourIndex); + writer.WriteUInt16(m_Snapshot.m_Entries[i].key.m_VariableIndex); + writer.WriteUInt16(m_Snapshot.m_Entries[i].m_TickWritten); writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Position); writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Length); } @@ -144,7 +153,12 @@ private void AllocateEntry(ref Entry entry, long size) public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { - int pos = m_Snapshot.Find(networkObjectId, variableIndex); + Key k; + k.m_NetworkObjectId = networkObjectId; + k.m_BehaviourIndex = (ushort)behaviourIndex; + k.m_VariableIndex = (ushort)variableIndex; + + int pos = m_Snapshot.Find(k); if (pos == Entry.k_NotFound) { pos = m_Snapshot.AddEntry(networkObjectId, behaviourIndex, variableIndex); @@ -160,6 +174,7 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, AllocateEntry(ref m_Snapshot.m_Entries[pos], varBuffer.Length); } + m_Snapshot.m_Entries[pos].m_TickWritten = NetworkManager.Singleton.NetworkTickSystem.GetTick(); // todo: from here // Copy the serialized NetworkVariable into our buffer Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Buffer, m_Snapshot.m_Entries[pos].m_Position, (int)varBuffer.Length); } @@ -179,16 +194,17 @@ public void ReadSnapshot(Stream snapshotStream) for (var i = 0; i < entries; i++) { - entry.m_NetworkObjectId = reader.ReadUInt64(); - entry.m_BehaviourIndex = reader.ReadUInt16(); - entry.m_VariableIndex = reader.ReadUInt16(); + entry.key.m_NetworkObjectId = reader.ReadUInt64(); + entry.key.m_BehaviourIndex = reader.ReadUInt16(); + entry.key.m_VariableIndex = reader.ReadUInt16(); + entry.m_TickWritten = reader.ReadUInt16(); entry.m_Position = reader.ReadUInt16(); entry.m_Length = reader.ReadUInt16(); - int pos = m_ReceivedSnapshot.Find(entry.m_NetworkObjectId, entry.m_VariableIndex); + int pos = m_ReceivedSnapshot.Find(entry.key); if (pos == Entry.k_NotFound) { - pos = m_ReceivedSnapshot.AddEntry(entry.m_NetworkObjectId, entry.m_BehaviourIndex, entry.m_VariableIndex); + pos = m_ReceivedSnapshot.AddEntry(entry.key.m_NetworkObjectId, entry.key.m_BehaviourIndex, entry.key.m_VariableIndex); } if (m_ReceivedSnapshot.m_Entries[pos].m_Length < entry.m_Length) @@ -203,18 +219,24 @@ public void ReadSnapshot(Stream snapshotStream) foreach (var pos in entriesPositionToRead) { - snapshotStream.Read(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, m_ReceivedSnapshot.m_Entries[pos].m_Length); + if (m_ReceivedSnapshot.m_Entries[pos].m_TickWritten > 0) + { - var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects; + snapshotStream.Read(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, + m_ReceivedSnapshot.m_Entries[pos].m_Length); - if (spawnedObjects.ContainsKey(m_ReceivedSnapshot.m_Entries[pos].m_NetworkObjectId)) - { - var behaviour = spawnedObjects[m_ReceivedSnapshot.m_Entries[pos].m_NetworkObjectId] - .GetNetworkBehaviourAtOrderIndex(m_ReceivedSnapshot.m_Entries[pos].m_BehaviourIndex); - var nv = behaviour.NetworkVariableFields[m_ReceivedSnapshot.m_Entries[pos].m_VariableIndex]; + var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects; - MemoryStream stream = new MemoryStream(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, m_ReceivedSnapshot.m_Entries[pos].m_Length); - nv.ReadDelta(stream, false, 0, 0); + if (spawnedObjects.ContainsKey(m_ReceivedSnapshot.m_Entries[pos].key.m_NetworkObjectId)) + { + var behaviour = spawnedObjects[m_ReceivedSnapshot.m_Entries[pos].key.m_NetworkObjectId] + .GetNetworkBehaviourAtOrderIndex(m_ReceivedSnapshot.m_Entries[pos].key.m_BehaviourIndex); + var nv = behaviour.NetworkVariableFields[m_ReceivedSnapshot.m_Entries[pos].key.m_VariableIndex]; + + MemoryStream stream = new MemoryStream(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, + m_ReceivedSnapshot.m_Entries[pos].m_Length); + nv.ReadDelta(stream, false, 0, 0); + } } } } @@ -224,8 +246,8 @@ private void DebugDisplayStore(Entry[] entries, int entryLength, string name) string table = "=== Snapshot table === " + name + " ===\n"; for (int i = 0; i < entryLength; i++) { - table += string.Format("NetworkObject {0}:{1} range [{2}, {3}]\n", entries[i].m_NetworkObjectId, - entries[i].m_VariableIndex, entries[i].m_Position, entries[i].m_Position + entries[i].m_Length); + table += string.Format("NetworkObject {0}:{1} range [{2}, {3}]\n", entries[i].key.m_NetworkObjectId, + entries[i].key.m_VariableIndex, entries[i].m_Position, entries[i].m_Position + entries[i].m_Length); } Debug.Log(table); } diff --git a/testproject/Assets/Prefabs/PlayerCube.prefab b/testproject/Assets/Prefabs/PlayerCube.prefab index 22ac104e3f..9411b3e28b 100644 --- a/testproject/Assets/Prefabs/PlayerCube.prefab +++ b/testproject/Assets/Prefabs/PlayerCube.prefab @@ -144,7 +144,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: - GlobalObjectIdHash: 3264262838392684788 + GlobalObjectIdHash: 951099334 AlwaysReplicateAsRoot: 0 DontDestroyWithOwner: 0 --- !u!114 &3809075828520557319 @@ -278,7 +278,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8685790303553767886} - m_Enabled: 1 + m_Enabled: 0 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} m_Name: @@ -326,7 +326,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8685790303553767886} - m_Enabled: 0 + m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9548116c10df1486ea12b7329b77c5cf, type: 3} m_Name: From 4147f24b3890c8731f325e1186654b9dd4a8bb35 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Mon, 3 May 2021 16:25:59 -0400 Subject: [PATCH 10/55] feat: snapshot. Server vars now replicate on client's --- .../Runtime/Core/SnapshotSystem.cs | 103 +++++++++++------- .../NetworkVariable/NetworkVariable.cs | 2 +- 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index bbcd184ff7..6ecb97bb5e 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -31,6 +31,9 @@ internal struct Entry internal class EntryBlock { private const int k_MaxVariables = 64; + public byte[] m_Buffer = new byte[20000]; + public int m_Beg = 0; // todo: clarify usage. Right now, this is the beginning of the _free_ space. + public int m_End = 0; public Entry[] m_Entries = new Entry[k_MaxVariables]; public int m_LastEntry = 0; @@ -66,6 +69,15 @@ public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex return pos; } + public void AllocateEntry(ref Entry entry, long size) + { + // todo: deal with free space + // todo: deal with full buffer + + entry.m_Position = (ushort)m_Beg; + entry.m_Length = (ushort)size; + m_Beg += (int)size; + } } public class SnapshotSystem : INetworkUpdateSystem, IDisposable @@ -73,11 +85,6 @@ public class SnapshotSystem : INetworkUpdateSystem, IDisposable private EntryBlock m_Snapshot = new EntryBlock(); private EntryBlock m_ReceivedSnapshot = new EntryBlock(); - // todo: split this buffer into parts per EntryBlock. Move the beg and end marker into an associated class, too - byte[] m_Buffer = new byte[20000]; - private int m_Beg = 0; - private int m_End = 0; - public SnapshotSystem() { this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); @@ -110,8 +117,8 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) buffer.Dispose(); } - DebugDisplayStore(m_Snapshot.m_Entries, m_Snapshot.m_LastEntry, "Entries"); - DebugDisplayStore(m_Snapshot.m_Entries, m_Snapshot.m_LastEntry, "Received Entries"); + DebugDisplayStore(m_Snapshot, "Entries"); + DebugDisplayStore(m_ReceivedSnapshot, "Received Entries"); break; } } @@ -136,19 +143,15 @@ private void WriteIndex(NetworkBuffer buffer) private void WriteBuffer(NetworkBuffer buffer) { + using (var writer = PooledNetworkWriter.Get(buffer)) + { + writer.WriteUInt16((ushort)m_Snapshot.m_Beg); + } + Debug.Log(String.Format("Writing {0} bytes", m_Snapshot.m_Beg)); + // todo: this sends the whole buffer // we'll need to build a per-client list - buffer.Write(m_Buffer, 0, m_End); - } - - private void AllocateEntry(ref Entry entry, long size) - { - // todo: deal with free space - // todo: deal with full buffer - - entry.m_Position = (ushort)m_Beg; - entry.m_Length = (ushort)size; - m_Beg += (int)size; + buffer.Write(m_Snapshot.m_Buffer, 0, m_Snapshot.m_Beg); } public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) @@ -171,12 +174,12 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, if (varBuffer.Length > m_Snapshot.m_Entries[pos].m_Length) { // allocate this Entry's buffer - AllocateEntry(ref m_Snapshot.m_Entries[pos], varBuffer.Length); + m_Snapshot.AllocateEntry(ref m_Snapshot.m_Entries[pos], varBuffer.Length); } m_Snapshot.m_Entries[pos].m_TickWritten = NetworkManager.Singleton.NetworkTickSystem.GetTick(); // todo: from here // Copy the serialized NetworkVariable into our buffer - Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Buffer, m_Snapshot.m_Entries[pos].m_Position, (int)varBuffer.Length); + Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.m_Buffer, m_Snapshot.m_Entries[pos].m_Position, (int)varBuffer.Length); } } @@ -185,7 +188,7 @@ public void ReadSnapshot(Stream snapshotStream) // todo: this is sub-optimal, as it allocates. Review List entriesPositionToRead = new List(); - + int snapshotSize = 0; using (var reader = PooledNetworkReader.Get(snapshotStream)) { Entry entry; @@ -209,45 +212,69 @@ public void ReadSnapshot(Stream snapshotStream) if (m_ReceivedSnapshot.m_Entries[pos].m_Length < entry.m_Length) { - AllocateEntry(ref entry, entry.m_Length); + m_ReceivedSnapshot.AllocateEntry(ref entry, entry.m_Length); } m_ReceivedSnapshot.m_Entries[pos] = entry; entriesPositionToRead.Add(pos); } + + m_ReceivedSnapshot.m_LastEntry = entries; + snapshotSize = reader.ReadUInt16(); } + Debug.Log(String.Format("Reading {0} bytes", snapshotSize)); + snapshotStream.Read(m_ReceivedSnapshot.m_Buffer, 0, snapshotSize); + + + var offset = snapshotStream.Position; + foreach (var pos in entriesPositionToRead) { if (m_ReceivedSnapshot.m_Entries[pos].m_TickWritten > 0) { + Debug.Log("applied variable"); + + var nv = FindNetworkVar(m_ReceivedSnapshot.m_Entries[pos].key); - snapshotStream.Read(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, + MemoryStream stream = new MemoryStream(m_ReceivedSnapshot.m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, m_ReceivedSnapshot.m_Entries[pos].m_Length); + // todo : read another stream, dump the output for debugging + nv.ReadDelta(stream, false, 0, 0); + } + } + } - var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects; + INetworkVariable FindNetworkVar(Key key) + { + var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects; - if (spawnedObjects.ContainsKey(m_ReceivedSnapshot.m_Entries[pos].key.m_NetworkObjectId)) - { - var behaviour = spawnedObjects[m_ReceivedSnapshot.m_Entries[pos].key.m_NetworkObjectId] - .GetNetworkBehaviourAtOrderIndex(m_ReceivedSnapshot.m_Entries[pos].key.m_BehaviourIndex); - var nv = behaviour.NetworkVariableFields[m_ReceivedSnapshot.m_Entries[pos].key.m_VariableIndex]; + if (spawnedObjects.ContainsKey(key.m_NetworkObjectId)) + { + var behaviour = spawnedObjects[key.m_NetworkObjectId] + .GetNetworkBehaviourAtOrderIndex(key.m_BehaviourIndex); + var nv = behaviour.NetworkVariableFields[key.m_VariableIndex]; - MemoryStream stream = new MemoryStream(m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, - m_ReceivedSnapshot.m_Entries[pos].m_Length); - nv.ReadDelta(stream, false, 0, 0); - } - } + return nv; } + + return null; } - private void DebugDisplayStore(Entry[] entries, int entryLength, string name) + private void DebugDisplayStore(EntryBlock block, string name) { string table = "=== Snapshot table === " + name + " ===\n"; - for (int i = 0; i < entryLength; i++) + for (int i = 0; i < block.m_LastEntry; i++) { - table += string.Format("NetworkObject {0}:{1} range [{2}, {3}]\n", entries[i].key.m_NetworkObjectId, - entries[i].key.m_VariableIndex, entries[i].m_Position, entries[i].m_Position + entries[i].m_Length); + table += string.Format("NetworkObject {0}:{1}:{2} range [{3}, {4}] ", block.m_Entries[i].key.m_NetworkObjectId, block.m_Entries[i].key.m_BehaviourIndex, + block.m_Entries[i].key.m_VariableIndex, block.m_Entries[i].m_Position, block.m_Entries[i].m_Position + block.m_Entries[i].m_Length); + + for (int j = 0; j < block.m_Entries[i].m_Length && j < 4; j++) + { + table += block.m_Buffer[block.m_Entries[i].m_Position + j].ToString("X2") + " "; + } + + table += "\n"; } Debug.Log(table); } diff --git a/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs index 42598e064f..aee32b6b60 100644 --- a/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs @@ -203,7 +203,7 @@ public void ReadDelta(Stream stream, bool keepDirtyDelta, ushort localTick, usho } OnValueChanged?.Invoke(previousValue, m_InternalValue); - } + } } /// From adc1d4bf3112b182391f8ab840619feca54f336c Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 4 May 2021 10:02:26 -0400 Subject: [PATCH 11/55] feat: snapshot. Removed small memory allocation for list of variables received --- .../Runtime/Core/SnapshotSystem.cs | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 6ecb97bb5e..ebdc544a6d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -24,6 +24,7 @@ internal struct Entry public ushort m_TickWritten; // the network tick at which this variable was set public ushort m_Position; // the offset in our m_Buffer public ushort m_Length; // the length of the data in m_Buffer + public bool m_Fresh; // indicates entries that were just received public const int k_NotFound = -1; } @@ -64,6 +65,7 @@ public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex entry.m_TickWritten = 0; entry.m_Position = 0; entry.m_Length = 0; + entry.m_Fresh = false; m_Entries[pos] = entry; return pos; @@ -185,9 +187,6 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, public void ReadSnapshot(Stream snapshotStream) { - // todo: this is sub-optimal, as it allocates. Review - List entriesPositionToRead = new List(); - int snapshotSize = 0; using (var reader = PooledNetworkReader.Get(snapshotStream)) { @@ -203,6 +202,7 @@ public void ReadSnapshot(Stream snapshotStream) entry.m_TickWritten = reader.ReadUInt16(); entry.m_Position = reader.ReadUInt16(); entry.m_Length = reader.ReadUInt16(); + entry.m_Fresh = true; int pos = m_ReceivedSnapshot.Find(entry.key); if (pos == Entry.k_NotFound) @@ -215,33 +215,29 @@ public void ReadSnapshot(Stream snapshotStream) m_ReceivedSnapshot.AllocateEntry(ref entry, entry.m_Length); } m_ReceivedSnapshot.m_Entries[pos] = entry; - - entriesPositionToRead.Add(pos); } - m_ReceivedSnapshot.m_LastEntry = entries; snapshotSize = reader.ReadUInt16(); } Debug.Log(String.Format("Reading {0} bytes", snapshotSize)); snapshotStream.Read(m_ReceivedSnapshot.m_Buffer, 0, snapshotSize); - - var offset = snapshotStream.Position; - - foreach (var pos in entriesPositionToRead) + for (var i = 0; i < m_ReceivedSnapshot.m_LastEntry; i++) { - if (m_ReceivedSnapshot.m_Entries[pos].m_TickWritten > 0) + if (m_ReceivedSnapshot.m_Entries[i].m_Fresh && m_ReceivedSnapshot.m_Entries[i].m_TickWritten > 0) { Debug.Log("applied variable"); - var nv = FindNetworkVar(m_ReceivedSnapshot.m_Entries[pos].key); + var nv = FindNetworkVar(m_ReceivedSnapshot.m_Entries[i].key); - MemoryStream stream = new MemoryStream(m_ReceivedSnapshot.m_Buffer, m_ReceivedSnapshot.m_Entries[pos].m_Position, - m_ReceivedSnapshot.m_Entries[pos].m_Length); + MemoryStream stream = new MemoryStream(m_ReceivedSnapshot.m_Buffer, m_ReceivedSnapshot.m_Entries[i].m_Position, + m_ReceivedSnapshot.m_Entries[i].m_Length); // todo : read another stream, dump the output for debugging nv.ReadDelta(stream, false, 0, 0); } + + m_ReceivedSnapshot.m_Entries[i].m_Fresh = false; } } From 5f800b0614e3578e048e57c95d458eed8f03fb32 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 4 May 2021 12:51:31 -0400 Subject: [PATCH 12/55] feat: snapshot. Format and old comments removal --- .../Runtime/Core/SnapshotSystem.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index ebdc544a6d..5dc113cee8 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -149,7 +149,7 @@ private void WriteBuffer(NetworkBuffer buffer) { writer.WriteUInt16((ushort)m_Snapshot.m_Beg); } - Debug.Log(String.Format("Writing {0} bytes", m_Snapshot.m_Beg)); + Debug.Log(string.Format("Writing {0} bytes", m_Snapshot.m_Beg)); // todo: this sends the whole buffer // we'll need to build a per-client list @@ -179,7 +179,7 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, m_Snapshot.AllocateEntry(ref m_Snapshot.m_Entries[pos], varBuffer.Length); } - m_Snapshot.m_Entries[pos].m_TickWritten = NetworkManager.Singleton.NetworkTickSystem.GetTick(); // todo: from here + m_Snapshot.m_Entries[pos].m_TickWritten = NetworkManager.Singleton.NetworkTickSystem.GetTick(); // Copy the serialized NetworkVariable into our buffer Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.m_Buffer, m_Snapshot.m_Entries[pos].m_Position, (int)varBuffer.Length); } @@ -220,7 +220,7 @@ public void ReadSnapshot(Stream snapshotStream) snapshotSize = reader.ReadUInt16(); } - Debug.Log(String.Format("Reading {0} bytes", snapshotSize)); + Debug.Log(string.Format("Reading {0} bytes", snapshotSize)); snapshotStream.Read(m_ReceivedSnapshot.m_Buffer, 0, snapshotSize); for (var i = 0; i < m_ReceivedSnapshot.m_LastEntry; i++) @@ -231,9 +231,9 @@ public void ReadSnapshot(Stream snapshotStream) var nv = FindNetworkVar(m_ReceivedSnapshot.m_Entries[i].key); - MemoryStream stream = new MemoryStream(m_ReceivedSnapshot.m_Buffer, m_ReceivedSnapshot.m_Entries[i].m_Position, + var stream = new MemoryStream(m_ReceivedSnapshot.m_Buffer, m_ReceivedSnapshot.m_Entries[i].m_Position, m_ReceivedSnapshot.m_Entries[i].m_Length); - // todo : read another stream, dump the output for debugging + nv.ReadDelta(stream, false, 0, 0); } @@ -241,7 +241,7 @@ public void ReadSnapshot(Stream snapshotStream) } } - INetworkVariable FindNetworkVar(Key key) + private INetworkVariable FindNetworkVar(Key key) { var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects; From a431032af2a174d081bbb491324353145207425e Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 4 May 2021 13:52:32 -0400 Subject: [PATCH 13/55] feat: snapshot. sending the snapshot from client to server too --- .../Runtime/Core/SnapshotSystem.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 5dc113cee8..22ed85b7db 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -102,26 +102,38 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) switch (updateStage) { case NetworkUpdateStage.EarlyUpdate: - - // todo: ConnectedClientsList is only valid on the host - for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) + { + if (NetworkManager.Singleton.IsServer) { - var clientId = NetworkManager.Singleton.ConnectedClientsList[i].ClientId; - - - // Send the entry index and the buffer where the variables are serialized - var buffer = PooledNetworkBuffer.Get(); - - WriteIndex(buffer); - WriteBuffer(buffer); - - NetworkManager.Singleton.MessageSender.Send(clientId, NetworkConstants.SNAPSHOT_DATA, NetworkChannel.SnapshotExchange, buffer); - buffer.Dispose(); + for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) + { + var clientId = NetworkManager.Singleton.ConnectedClientsList[i].ClientId; + SendSnapshot(clientId); + } + } + else + { + SendSnapshot(NetworkManager.Singleton.ServerClientId); } DebugDisplayStore(m_Snapshot, "Entries"); DebugDisplayStore(m_ReceivedSnapshot, "Received Entries"); break; + } + } + } + + private void SendSnapshot(ulong clientId) + { + // Send the entry index and the buffer where the variables are serialized + using (var buffer = PooledNetworkBuffer.Get()) + { + WriteIndex(buffer); + WriteBuffer(buffer); + + NetworkManager.Singleton.MessageSender.Send(clientId, NetworkConstants.SNAPSHOT_DATA, + NetworkChannel.SnapshotExchange, buffer); + buffer.Dispose(); } } From 5e83049b5b5b941e893dbf83f81ef3baad0ff7b7 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 4 May 2021 15:07:03 -0400 Subject: [PATCH 14/55] feat: snapshot. Storing ticks so 1 server and 2 client have all vars updated on all machines --- com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 22ed85b7db..ff7bcaa2fa 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -246,7 +246,8 @@ public void ReadSnapshot(Stream snapshotStream) var stream = new MemoryStream(m_ReceivedSnapshot.m_Buffer, m_ReceivedSnapshot.m_Entries[i].m_Position, m_ReceivedSnapshot.m_Entries[i].m_Length); - nv.ReadDelta(stream, false, 0, 0); + // todo: Review whether tick still belong in netvar or in the snapshot table. + nv.ReadDelta(stream, NetworkManager.Singleton.IsServer, NetworkManager.Singleton.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.m_Entries[i].m_TickWritten); } m_ReceivedSnapshot.m_Entries[i].m_Fresh = false; From aabefbfdebcaee11be1f7e8fcb4d5c0b7b2df2c5 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 4 May 2021 17:37:52 -0400 Subject: [PATCH 15/55] style: 'if' instead of 'switch' for NetworkUpdateStage.EarlyUpdate --- .../Runtime/Core/SnapshotSystem.cs | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index ff7bcaa2fa..b425bf97ae 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -99,27 +99,23 @@ public void Dispose() public void NetworkUpdate(NetworkUpdateStage updateStage) { - switch (updateStage) + if (updateStage == NetworkUpdateStage.EarlyUpdate) { - case NetworkUpdateStage.EarlyUpdate: + if (NetworkManager.Singleton.IsServer) { - if (NetworkManager.Singleton.IsServer) + for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) { - for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) - { - var clientId = NetworkManager.Singleton.ConnectedClientsList[i].ClientId; - SendSnapshot(clientId); - } + var clientId = NetworkManager.Singleton.ConnectedClientsList[i].ClientId; + SendSnapshot(clientId); } - else - { - SendSnapshot(NetworkManager.Singleton.ServerClientId); - } - - DebugDisplayStore(m_Snapshot, "Entries"); - DebugDisplayStore(m_ReceivedSnapshot, "Received Entries"); - break; } + else + { + SendSnapshot(NetworkManager.Singleton.ServerClientId); + } + + DebugDisplayStore(m_Snapshot, "Entries"); + DebugDisplayStore(m_ReceivedSnapshot, "Received Entries"); } } From 6a97b7283c7bfb20d5e6b580d7fa1ea3e89ff0c8 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 5 May 2021 09:17:11 -0400 Subject: [PATCH 16/55] feat: snapshot. Removed a memory allocation, used a member var for the network manager --- .../Runtime/Core/SnapshotSystem.cs | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index b425bf97ae..8fb25741ec 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -32,12 +32,20 @@ internal struct Entry internal class EntryBlock { private const int k_MaxVariables = 64; - public byte[] m_Buffer = new byte[20000]; + private const int k_BufferSize = 20000; + + public byte[] m_Buffer = new byte[k_BufferSize]; public int m_Beg = 0; // todo: clarify usage. Right now, this is the beginning of the _free_ space. public int m_End = 0; public Entry[] m_Entries = new Entry[k_MaxVariables]; public int m_LastEntry = 0; + public MemoryStream m_Stream; + + public EntryBlock() + { + m_Stream = new MemoryStream(m_Buffer, 0, k_BufferSize); + } public int Find(Key key) { @@ -86,6 +94,7 @@ public class SnapshotSystem : INetworkUpdateSystem, IDisposable { private EntryBlock m_Snapshot = new EntryBlock(); private EntryBlock m_ReceivedSnapshot = new EntryBlock(); + private NetworkManager m_NetworkManager = NetworkManager.Singleton; public SnapshotSystem() { @@ -101,17 +110,17 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) { if (updateStage == NetworkUpdateStage.EarlyUpdate) { - if (NetworkManager.Singleton.IsServer) + if (m_NetworkManager.IsServer) { - for (int i = 0; i < NetworkManager.Singleton.ConnectedClientsList.Count; i++) + for (int i = 0; i < m_NetworkManager.ConnectedClientsList.Count; i++) { - var clientId = NetworkManager.Singleton.ConnectedClientsList[i].ClientId; + var clientId = m_NetworkManager.ConnectedClientsList[i].ClientId; SendSnapshot(clientId); } } else { - SendSnapshot(NetworkManager.Singleton.ServerClientId); + SendSnapshot(m_NetworkManager.ServerClientId); } DebugDisplayStore(m_Snapshot, "Entries"); @@ -127,7 +136,7 @@ private void SendSnapshot(ulong clientId) WriteIndex(buffer); WriteBuffer(buffer); - NetworkManager.Singleton.MessageSender.Send(clientId, NetworkConstants.SNAPSHOT_DATA, + m_NetworkManager.MessageSender.Send(clientId, NetworkConstants.SNAPSHOT_DATA, NetworkChannel.SnapshotExchange, buffer); buffer.Dispose(); } @@ -157,7 +166,6 @@ private void WriteBuffer(NetworkBuffer buffer) { writer.WriteUInt16((ushort)m_Snapshot.m_Beg); } - Debug.Log(string.Format("Writing {0} bytes", m_Snapshot.m_Beg)); // todo: this sends the whole buffer // we'll need to build a per-client list @@ -187,7 +195,7 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, m_Snapshot.AllocateEntry(ref m_Snapshot.m_Entries[pos], varBuffer.Length); } - m_Snapshot.m_Entries[pos].m_TickWritten = NetworkManager.Singleton.NetworkTickSystem.GetTick(); + m_Snapshot.m_Entries[pos].m_TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); // Copy the serialized NetworkVariable into our buffer Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.m_Buffer, m_Snapshot.m_Entries[pos].m_Position, (int)varBuffer.Length); } @@ -200,7 +208,6 @@ public void ReadSnapshot(Stream snapshotStream) { Entry entry; short entries = reader.ReadInt16(); - Debug.Log(string.Format("Got {0} entries", entries)); for (var i = 0; i < entries; i++) { @@ -228,22 +235,18 @@ public void ReadSnapshot(Stream snapshotStream) snapshotSize = reader.ReadUInt16(); } - Debug.Log(string.Format("Reading {0} bytes", snapshotSize)); snapshotStream.Read(m_ReceivedSnapshot.m_Buffer, 0, snapshotSize); for (var i = 0; i < m_ReceivedSnapshot.m_LastEntry; i++) { if (m_ReceivedSnapshot.m_Entries[i].m_Fresh && m_ReceivedSnapshot.m_Entries[i].m_TickWritten > 0) { - Debug.Log("applied variable"); - var nv = FindNetworkVar(m_ReceivedSnapshot.m_Entries[i].key); - var stream = new MemoryStream(m_ReceivedSnapshot.m_Buffer, m_ReceivedSnapshot.m_Entries[i].m_Position, - m_ReceivedSnapshot.m_Entries[i].m_Length); + m_ReceivedSnapshot.m_Stream.Seek(m_ReceivedSnapshot.m_Entries[i].m_Position, SeekOrigin.Begin); // todo: Review whether tick still belong in netvar or in the snapshot table. - nv.ReadDelta(stream, NetworkManager.Singleton.IsServer, NetworkManager.Singleton.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.m_Entries[i].m_TickWritten); + nv.ReadDelta(m_ReceivedSnapshot.m_Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.m_Entries[i].m_TickWritten); } m_ReceivedSnapshot.m_Entries[i].m_Fresh = false; @@ -252,7 +255,7 @@ public void ReadSnapshot(Stream snapshotStream) private INetworkVariable FindNetworkVar(Key key) { - var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects; + var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; if (spawnedObjects.ContainsKey(key.m_NetworkObjectId)) { From 7807636d81c5628f89e77d7151302552857c80c9 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 5 May 2021 10:14:33 -0400 Subject: [PATCH 17/55] feat: snapshot. Disabling snapshot for the M1 branch, to allow merge to develop --- .../Runtime/Core/NetworkBehaviour.cs | 4 ++-- testproject/Assets/Prefabs/PlayerCube.prefab | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index ae79951b5b..029575a43d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -44,8 +44,8 @@ public enum __NExec // todo: transitional. For the next release, only Snapshot should remain // The booleans allow iterative development and testing in the meantime - static private bool m_UseClassicDelta = false; - static private bool m_UseSnapshot = true; + static private bool m_UseClassicDelta = true; + static private bool m_UseSnapshot = false; #pragma warning disable 414 #pragma warning disable IDE1006 // disable naming rule violation check diff --git a/testproject/Assets/Prefabs/PlayerCube.prefab b/testproject/Assets/Prefabs/PlayerCube.prefab index 9411b3e28b..22ac104e3f 100644 --- a/testproject/Assets/Prefabs/PlayerCube.prefab +++ b/testproject/Assets/Prefabs/PlayerCube.prefab @@ -144,7 +144,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: - GlobalObjectIdHash: 951099334 + GlobalObjectIdHash: 3264262838392684788 AlwaysReplicateAsRoot: 0 DontDestroyWithOwner: 0 --- !u!114 &3809075828520557319 @@ -278,7 +278,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8685790303553767886} - m_Enabled: 0 + m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} m_Name: @@ -326,7 +326,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8685790303553767886} - m_Enabled: 1 + m_Enabled: 0 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9548116c10df1486ea12b7329b77c5cf, type: 3} m_Name: From 1638fa369e8d1a6c26a93f984f9f349d7f9d1a13 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 5 May 2021 15:18:35 -0400 Subject: [PATCH 18/55] style: Fix for coding style --- .../Runtime/Core/NetworkBehaviour.cs | 8 +- .../Runtime/Core/SnapshotSystem.cs | 166 +++++++++--------- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 029575a43d..9f687d8343 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -44,8 +44,8 @@ public enum __NExec // todo: transitional. For the next release, only Snapshot should remain // The booleans allow iterative development and testing in the meantime - static private bool m_UseClassicDelta = true; - static private bool m_UseSnapshot = false; + static private bool s_UseClassicDelta = true; + static private bool s_UseSnapshot = false; #pragma warning disable 414 #pragma warning disable IDE1006 // disable naming rule violation check @@ -599,7 +599,7 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex, SnapshotS return; } - if (m_UseSnapshot) + if (s_UseSnapshot) { for (int k = 0; k < NetworkVariableFields.Count; k++) { @@ -609,7 +609,7 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex, SnapshotS for (int j = 0; j < m_ChannelMappedNetworkVariableIndexes.Count; j++) { - if (!m_UseClassicDelta) + if (!s_UseClassicDelta) { break; } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 8fb25741ec..9a1c289128 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -14,19 +14,19 @@ namespace MLAPI { internal struct Key { - public ulong m_NetworkObjectId; // the NetworkObjectId of the owning GameObject - public ushort m_BehaviourIndex; // the index of the behaviour in this GameObject - public ushort m_VariableIndex; // the index of the variable in this NetworkBehaviour + public ulong NetworkObjectId; // the NetworkObjectId of the owning GameObject + public ushort BehaviourIndex; // the index of the behaviour in this GameObject + public ushort VariableIndex; // the index of the variable in this NetworkBehaviour } internal struct Entry { - public Key key; - public ushort m_TickWritten; // the network tick at which this variable was set - public ushort m_Position; // the offset in our m_Buffer - public ushort m_Length; // the length of the data in m_Buffer - public bool m_Fresh; // indicates entries that were just received + public Key Key; + public ushort TickWritten; // the network tick at which this variable was set + public ushort Position; // the offset in our Buffer + public ushort Length; // the length of the data in Buffer + public bool Fresh; // indicates entries that were just received - public const int k_NotFound = -1; + public const int NotFound = -1; } internal class EntryBlock @@ -34,47 +34,47 @@ internal class EntryBlock private const int k_MaxVariables = 64; private const int k_BufferSize = 20000; - public byte[] m_Buffer = new byte[k_BufferSize]; - public int m_Beg = 0; // todo: clarify usage. Right now, this is the beginning of the _free_ space. - public int m_End = 0; + public byte[] Buffer = new byte[k_BufferSize]; + public int Beg = 0; // todo: clarify usage. Right now, this is the beginning of the _free_ space. + public int End = 0; - public Entry[] m_Entries = new Entry[k_MaxVariables]; - public int m_LastEntry = 0; - public MemoryStream m_Stream; + public Entry[] Entries = new Entry[k_MaxVariables]; + public int LastEntry = 0; + public MemoryStream Stream; public EntryBlock() { - m_Stream = new MemoryStream(m_Buffer, 0, k_BufferSize); + Stream = new MemoryStream(Buffer, 0, k_BufferSize); } public int Find(Key key) { - for (int i = 0; i < m_LastEntry; i++) + for (int i = 0; i < LastEntry; i++) { - if (m_Entries[i].key.m_NetworkObjectId == key.m_NetworkObjectId && - m_Entries[i].key.m_BehaviourIndex == key.m_BehaviourIndex && - m_Entries[i].key.m_VariableIndex == key.m_VariableIndex) + if (Entries[i].Key.NetworkObjectId == key.NetworkObjectId && + Entries[i].Key.BehaviourIndex == key.BehaviourIndex && + Entries[i].Key.VariableIndex == key.VariableIndex) { return i; } } - return Entry.k_NotFound; + return Entry.NotFound; } public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex) { - var pos = m_LastEntry++; - var entry = m_Entries[pos]; - - entry.key.m_NetworkObjectId = networkObjectId; - entry.key.m_BehaviourIndex = (ushort)behaviourIndex; - entry.key.m_VariableIndex = (ushort)variableIndex; - entry.m_TickWritten = 0; - entry.m_Position = 0; - entry.m_Length = 0; - entry.m_Fresh = false; - m_Entries[pos] = entry; + var pos = LastEntry++; + var entry = Entries[pos]; + + entry.Key.NetworkObjectId = networkObjectId; + entry.Key.BehaviourIndex = (ushort)behaviourIndex; + entry.Key.VariableIndex = (ushort)variableIndex; + entry.TickWritten = 0; + entry.Position = 0; + entry.Length = 0; + entry.Fresh = false; + Entries[pos] = entry; return pos; } @@ -84,9 +84,9 @@ public void AllocateEntry(ref Entry entry, long size) // todo: deal with free space // todo: deal with full buffer - entry.m_Position = (ushort)m_Beg; - entry.m_Length = (ushort)size; - m_Beg += (int)size; + entry.Position = (ushort)Beg; + entry.Length = (ushort)size; + Beg += (int)size; } } @@ -146,16 +146,16 @@ private void WriteIndex(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteInt16((short)m_Snapshot.m_LastEntry); + writer.WriteInt16((short)m_Snapshot.LastEntry); - for (var i = 0; i < m_Snapshot.m_LastEntry; i++) + for (var i = 0; i < m_Snapshot.LastEntry; i++) { - writer.WriteUInt64(m_Snapshot.m_Entries[i].key.m_NetworkObjectId); - writer.WriteUInt16(m_Snapshot.m_Entries[i].key.m_BehaviourIndex); - writer.WriteUInt16(m_Snapshot.m_Entries[i].key.m_VariableIndex); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_TickWritten); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Position); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Length); + writer.WriteUInt64(m_Snapshot.Entries[i].Key.NetworkObjectId); + writer.WriteUInt16(m_Snapshot.Entries[i].Key.BehaviourIndex); + writer.WriteUInt16(m_Snapshot.Entries[i].Key.VariableIndex); + writer.WriteUInt16(m_Snapshot.Entries[i].TickWritten); + writer.WriteUInt16(m_Snapshot.Entries[i].Position); + writer.WriteUInt16(m_Snapshot.Entries[i].Length); } } } @@ -164,23 +164,23 @@ private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteUInt16((ushort)m_Snapshot.m_Beg); + writer.WriteUInt16((ushort)m_Snapshot.Beg); } // todo: this sends the whole buffer // we'll need to build a per-client list - buffer.Write(m_Snapshot.m_Buffer, 0, m_Snapshot.m_Beg); + buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Beg); } public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { Key k; - k.m_NetworkObjectId = networkObjectId; - k.m_BehaviourIndex = (ushort)behaviourIndex; - k.m_VariableIndex = (ushort)variableIndex; + k.NetworkObjectId = networkObjectId; + k.BehaviourIndex = (ushort)behaviourIndex; + k.VariableIndex = (ushort)variableIndex; int pos = m_Snapshot.Find(k); - if (pos == Entry.k_NotFound) + if (pos == Entry.NotFound) { pos = m_Snapshot.AddEntry(networkObjectId, behaviourIndex, variableIndex); } @@ -189,15 +189,15 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, using (var varBuffer = PooledNetworkBuffer.Get()) { networkVariable.WriteDelta(varBuffer); - if (varBuffer.Length > m_Snapshot.m_Entries[pos].m_Length) + if (varBuffer.Length > m_Snapshot.Entries[pos].Length) { // allocate this Entry's buffer - m_Snapshot.AllocateEntry(ref m_Snapshot.m_Entries[pos], varBuffer.Length); + m_Snapshot.AllocateEntry(ref m_Snapshot.Entries[pos], varBuffer.Length); } - m_Snapshot.m_Entries[pos].m_TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); + m_Snapshot.Entries[pos].TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); // Copy the serialized NetworkVariable into our buffer - Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.m_Buffer, m_Snapshot.m_Entries[pos].m_Position, (int)varBuffer.Length); + Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.Buffer, m_Snapshot.Entries[pos].Position, (int)varBuffer.Length); } } @@ -211,45 +211,45 @@ public void ReadSnapshot(Stream snapshotStream) for (var i = 0; i < entries; i++) { - entry.key.m_NetworkObjectId = reader.ReadUInt64(); - entry.key.m_BehaviourIndex = reader.ReadUInt16(); - entry.key.m_VariableIndex = reader.ReadUInt16(); - entry.m_TickWritten = reader.ReadUInt16(); - entry.m_Position = reader.ReadUInt16(); - entry.m_Length = reader.ReadUInt16(); - entry.m_Fresh = true; - - int pos = m_ReceivedSnapshot.Find(entry.key); - if (pos == Entry.k_NotFound) + entry.Key.NetworkObjectId = reader.ReadUInt64(); + entry.Key.BehaviourIndex = reader.ReadUInt16(); + entry.Key.VariableIndex = reader.ReadUInt16(); + entry.TickWritten = reader.ReadUInt16(); + entry.Position = reader.ReadUInt16(); + entry.Length = reader.ReadUInt16(); + entry.Fresh = true; + + int pos = m_ReceivedSnapshot.Find(entry.Key); + if (pos == Entry.NotFound) { - pos = m_ReceivedSnapshot.AddEntry(entry.key.m_NetworkObjectId, entry.key.m_BehaviourIndex, entry.key.m_VariableIndex); + pos = m_ReceivedSnapshot.AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, entry.Key.VariableIndex); } - if (m_ReceivedSnapshot.m_Entries[pos].m_Length < entry.m_Length) + if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) { - m_ReceivedSnapshot.AllocateEntry(ref entry, entry.m_Length); + m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); } - m_ReceivedSnapshot.m_Entries[pos] = entry; + m_ReceivedSnapshot.Entries[pos] = entry; } snapshotSize = reader.ReadUInt16(); } - snapshotStream.Read(m_ReceivedSnapshot.m_Buffer, 0, snapshotSize); + snapshotStream.Read(m_ReceivedSnapshot.Buffer, 0, snapshotSize); - for (var i = 0; i < m_ReceivedSnapshot.m_LastEntry; i++) + for (var i = 0; i < m_ReceivedSnapshot.LastEntry; i++) { - if (m_ReceivedSnapshot.m_Entries[i].m_Fresh && m_ReceivedSnapshot.m_Entries[i].m_TickWritten > 0) + if (m_ReceivedSnapshot.Entries[i].Fresh && m_ReceivedSnapshot.Entries[i].TickWritten > 0) { - var nv = FindNetworkVar(m_ReceivedSnapshot.m_Entries[i].key); + var nv = FindNetworkVar(m_ReceivedSnapshot.Entries[i].Key); - m_ReceivedSnapshot.m_Stream.Seek(m_ReceivedSnapshot.m_Entries[i].m_Position, SeekOrigin.Begin); + m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); // todo: Review whether tick still belong in netvar or in the snapshot table. - nv.ReadDelta(m_ReceivedSnapshot.m_Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.m_Entries[i].m_TickWritten); + nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); } - m_ReceivedSnapshot.m_Entries[i].m_Fresh = false; + m_ReceivedSnapshot.Entries[i].Fresh = false; } } @@ -257,11 +257,11 @@ private INetworkVariable FindNetworkVar(Key key) { var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; - if (spawnedObjects.ContainsKey(key.m_NetworkObjectId)) + if (spawnedObjects.ContainsKey(key.NetworkObjectId)) { - var behaviour = spawnedObjects[key.m_NetworkObjectId] - .GetNetworkBehaviourAtOrderIndex(key.m_BehaviourIndex); - var nv = behaviour.NetworkVariableFields[key.m_VariableIndex]; + var behaviour = spawnedObjects[key.NetworkObjectId] + .GetNetworkBehaviourAtOrderIndex(key.BehaviourIndex); + var nv = behaviour.NetworkVariableFields[key.VariableIndex]; return nv; } @@ -272,14 +272,14 @@ private INetworkVariable FindNetworkVar(Key key) private void DebugDisplayStore(EntryBlock block, string name) { string table = "=== Snapshot table === " + name + " ===\n"; - for (int i = 0; i < block.m_LastEntry; i++) + for (int i = 0; i < block.LastEntry; i++) { - table += string.Format("NetworkObject {0}:{1}:{2} range [{3}, {4}] ", block.m_Entries[i].key.m_NetworkObjectId, block.m_Entries[i].key.m_BehaviourIndex, - block.m_Entries[i].key.m_VariableIndex, block.m_Entries[i].m_Position, block.m_Entries[i].m_Position + block.m_Entries[i].m_Length); + table += string.Format("NetworkObject {0}:{1}:{2} range [{3}, {4}] ", block.Entries[i].Key.NetworkObjectId, block.Entries[i].Key.BehaviourIndex, + block.Entries[i].Key.VariableIndex, block.Entries[i].Position, block.Entries[i].Position + block.Entries[i].Length); - for (int j = 0; j < block.m_Entries[i].m_Length && j < 4; j++) + for (int j = 0; j < block.Entries[i].Length && j < 4; j++) { - table += block.m_Buffer[block.m_Entries[i].m_Position + j].ToString("X2") + " "; + table += block.Buffer[block.Entries[i].Position + j].ToString("X2") + " "; } table += "\n"; From 082bd75411e8c269d85b997a27204a47b9b4af22 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 5 May 2021 15:18:35 -0400 Subject: [PATCH 19/55] style: Fix for coding style --- .../Runtime/Core/NetworkBehaviour.cs | 8 +- .../Runtime/Core/SnapshotSystem.cs | 166 +++++++++--------- 2 files changed, 87 insertions(+), 87 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index ae79951b5b..0d730ca6ad 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -44,8 +44,8 @@ public enum __NExec // todo: transitional. For the next release, only Snapshot should remain // The booleans allow iterative development and testing in the meantime - static private bool m_UseClassicDelta = false; - static private bool m_UseSnapshot = true; + static private bool s_UseClassicDelta = false; + static private bool s_UseSnapshot = true; #pragma warning disable 414 #pragma warning disable IDE1006 // disable naming rule violation check @@ -599,7 +599,7 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex, SnapshotS return; } - if (m_UseSnapshot) + if (s_UseSnapshot) { for (int k = 0; k < NetworkVariableFields.Count; k++) { @@ -609,7 +609,7 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex, SnapshotS for (int j = 0; j < m_ChannelMappedNetworkVariableIndexes.Count; j++) { - if (!m_UseClassicDelta) + if (!s_UseClassicDelta) { break; } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 8fb25741ec..9a1c289128 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -14,19 +14,19 @@ namespace MLAPI { internal struct Key { - public ulong m_NetworkObjectId; // the NetworkObjectId of the owning GameObject - public ushort m_BehaviourIndex; // the index of the behaviour in this GameObject - public ushort m_VariableIndex; // the index of the variable in this NetworkBehaviour + public ulong NetworkObjectId; // the NetworkObjectId of the owning GameObject + public ushort BehaviourIndex; // the index of the behaviour in this GameObject + public ushort VariableIndex; // the index of the variable in this NetworkBehaviour } internal struct Entry { - public Key key; - public ushort m_TickWritten; // the network tick at which this variable was set - public ushort m_Position; // the offset in our m_Buffer - public ushort m_Length; // the length of the data in m_Buffer - public bool m_Fresh; // indicates entries that were just received + public Key Key; + public ushort TickWritten; // the network tick at which this variable was set + public ushort Position; // the offset in our Buffer + public ushort Length; // the length of the data in Buffer + public bool Fresh; // indicates entries that were just received - public const int k_NotFound = -1; + public const int NotFound = -1; } internal class EntryBlock @@ -34,47 +34,47 @@ internal class EntryBlock private const int k_MaxVariables = 64; private const int k_BufferSize = 20000; - public byte[] m_Buffer = new byte[k_BufferSize]; - public int m_Beg = 0; // todo: clarify usage. Right now, this is the beginning of the _free_ space. - public int m_End = 0; + public byte[] Buffer = new byte[k_BufferSize]; + public int Beg = 0; // todo: clarify usage. Right now, this is the beginning of the _free_ space. + public int End = 0; - public Entry[] m_Entries = new Entry[k_MaxVariables]; - public int m_LastEntry = 0; - public MemoryStream m_Stream; + public Entry[] Entries = new Entry[k_MaxVariables]; + public int LastEntry = 0; + public MemoryStream Stream; public EntryBlock() { - m_Stream = new MemoryStream(m_Buffer, 0, k_BufferSize); + Stream = new MemoryStream(Buffer, 0, k_BufferSize); } public int Find(Key key) { - for (int i = 0; i < m_LastEntry; i++) + for (int i = 0; i < LastEntry; i++) { - if (m_Entries[i].key.m_NetworkObjectId == key.m_NetworkObjectId && - m_Entries[i].key.m_BehaviourIndex == key.m_BehaviourIndex && - m_Entries[i].key.m_VariableIndex == key.m_VariableIndex) + if (Entries[i].Key.NetworkObjectId == key.NetworkObjectId && + Entries[i].Key.BehaviourIndex == key.BehaviourIndex && + Entries[i].Key.VariableIndex == key.VariableIndex) { return i; } } - return Entry.k_NotFound; + return Entry.NotFound; } public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex) { - var pos = m_LastEntry++; - var entry = m_Entries[pos]; - - entry.key.m_NetworkObjectId = networkObjectId; - entry.key.m_BehaviourIndex = (ushort)behaviourIndex; - entry.key.m_VariableIndex = (ushort)variableIndex; - entry.m_TickWritten = 0; - entry.m_Position = 0; - entry.m_Length = 0; - entry.m_Fresh = false; - m_Entries[pos] = entry; + var pos = LastEntry++; + var entry = Entries[pos]; + + entry.Key.NetworkObjectId = networkObjectId; + entry.Key.BehaviourIndex = (ushort)behaviourIndex; + entry.Key.VariableIndex = (ushort)variableIndex; + entry.TickWritten = 0; + entry.Position = 0; + entry.Length = 0; + entry.Fresh = false; + Entries[pos] = entry; return pos; } @@ -84,9 +84,9 @@ public void AllocateEntry(ref Entry entry, long size) // todo: deal with free space // todo: deal with full buffer - entry.m_Position = (ushort)m_Beg; - entry.m_Length = (ushort)size; - m_Beg += (int)size; + entry.Position = (ushort)Beg; + entry.Length = (ushort)size; + Beg += (int)size; } } @@ -146,16 +146,16 @@ private void WriteIndex(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteInt16((short)m_Snapshot.m_LastEntry); + writer.WriteInt16((short)m_Snapshot.LastEntry); - for (var i = 0; i < m_Snapshot.m_LastEntry; i++) + for (var i = 0; i < m_Snapshot.LastEntry; i++) { - writer.WriteUInt64(m_Snapshot.m_Entries[i].key.m_NetworkObjectId); - writer.WriteUInt16(m_Snapshot.m_Entries[i].key.m_BehaviourIndex); - writer.WriteUInt16(m_Snapshot.m_Entries[i].key.m_VariableIndex); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_TickWritten); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Position); - writer.WriteUInt16(m_Snapshot.m_Entries[i].m_Length); + writer.WriteUInt64(m_Snapshot.Entries[i].Key.NetworkObjectId); + writer.WriteUInt16(m_Snapshot.Entries[i].Key.BehaviourIndex); + writer.WriteUInt16(m_Snapshot.Entries[i].Key.VariableIndex); + writer.WriteUInt16(m_Snapshot.Entries[i].TickWritten); + writer.WriteUInt16(m_Snapshot.Entries[i].Position); + writer.WriteUInt16(m_Snapshot.Entries[i].Length); } } } @@ -164,23 +164,23 @@ private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteUInt16((ushort)m_Snapshot.m_Beg); + writer.WriteUInt16((ushort)m_Snapshot.Beg); } // todo: this sends the whole buffer // we'll need to build a per-client list - buffer.Write(m_Snapshot.m_Buffer, 0, m_Snapshot.m_Beg); + buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Beg); } public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { Key k; - k.m_NetworkObjectId = networkObjectId; - k.m_BehaviourIndex = (ushort)behaviourIndex; - k.m_VariableIndex = (ushort)variableIndex; + k.NetworkObjectId = networkObjectId; + k.BehaviourIndex = (ushort)behaviourIndex; + k.VariableIndex = (ushort)variableIndex; int pos = m_Snapshot.Find(k); - if (pos == Entry.k_NotFound) + if (pos == Entry.NotFound) { pos = m_Snapshot.AddEntry(networkObjectId, behaviourIndex, variableIndex); } @@ -189,15 +189,15 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, using (var varBuffer = PooledNetworkBuffer.Get()) { networkVariable.WriteDelta(varBuffer); - if (varBuffer.Length > m_Snapshot.m_Entries[pos].m_Length) + if (varBuffer.Length > m_Snapshot.Entries[pos].Length) { // allocate this Entry's buffer - m_Snapshot.AllocateEntry(ref m_Snapshot.m_Entries[pos], varBuffer.Length); + m_Snapshot.AllocateEntry(ref m_Snapshot.Entries[pos], varBuffer.Length); } - m_Snapshot.m_Entries[pos].m_TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); + m_Snapshot.Entries[pos].TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); // Copy the serialized NetworkVariable into our buffer - Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.m_Buffer, m_Snapshot.m_Entries[pos].m_Position, (int)varBuffer.Length); + Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.Buffer, m_Snapshot.Entries[pos].Position, (int)varBuffer.Length); } } @@ -211,45 +211,45 @@ public void ReadSnapshot(Stream snapshotStream) for (var i = 0; i < entries; i++) { - entry.key.m_NetworkObjectId = reader.ReadUInt64(); - entry.key.m_BehaviourIndex = reader.ReadUInt16(); - entry.key.m_VariableIndex = reader.ReadUInt16(); - entry.m_TickWritten = reader.ReadUInt16(); - entry.m_Position = reader.ReadUInt16(); - entry.m_Length = reader.ReadUInt16(); - entry.m_Fresh = true; - - int pos = m_ReceivedSnapshot.Find(entry.key); - if (pos == Entry.k_NotFound) + entry.Key.NetworkObjectId = reader.ReadUInt64(); + entry.Key.BehaviourIndex = reader.ReadUInt16(); + entry.Key.VariableIndex = reader.ReadUInt16(); + entry.TickWritten = reader.ReadUInt16(); + entry.Position = reader.ReadUInt16(); + entry.Length = reader.ReadUInt16(); + entry.Fresh = true; + + int pos = m_ReceivedSnapshot.Find(entry.Key); + if (pos == Entry.NotFound) { - pos = m_ReceivedSnapshot.AddEntry(entry.key.m_NetworkObjectId, entry.key.m_BehaviourIndex, entry.key.m_VariableIndex); + pos = m_ReceivedSnapshot.AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, entry.Key.VariableIndex); } - if (m_ReceivedSnapshot.m_Entries[pos].m_Length < entry.m_Length) + if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) { - m_ReceivedSnapshot.AllocateEntry(ref entry, entry.m_Length); + m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); } - m_ReceivedSnapshot.m_Entries[pos] = entry; + m_ReceivedSnapshot.Entries[pos] = entry; } snapshotSize = reader.ReadUInt16(); } - snapshotStream.Read(m_ReceivedSnapshot.m_Buffer, 0, snapshotSize); + snapshotStream.Read(m_ReceivedSnapshot.Buffer, 0, snapshotSize); - for (var i = 0; i < m_ReceivedSnapshot.m_LastEntry; i++) + for (var i = 0; i < m_ReceivedSnapshot.LastEntry; i++) { - if (m_ReceivedSnapshot.m_Entries[i].m_Fresh && m_ReceivedSnapshot.m_Entries[i].m_TickWritten > 0) + if (m_ReceivedSnapshot.Entries[i].Fresh && m_ReceivedSnapshot.Entries[i].TickWritten > 0) { - var nv = FindNetworkVar(m_ReceivedSnapshot.m_Entries[i].key); + var nv = FindNetworkVar(m_ReceivedSnapshot.Entries[i].Key); - m_ReceivedSnapshot.m_Stream.Seek(m_ReceivedSnapshot.m_Entries[i].m_Position, SeekOrigin.Begin); + m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); // todo: Review whether tick still belong in netvar or in the snapshot table. - nv.ReadDelta(m_ReceivedSnapshot.m_Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.m_Entries[i].m_TickWritten); + nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); } - m_ReceivedSnapshot.m_Entries[i].m_Fresh = false; + m_ReceivedSnapshot.Entries[i].Fresh = false; } } @@ -257,11 +257,11 @@ private INetworkVariable FindNetworkVar(Key key) { var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; - if (spawnedObjects.ContainsKey(key.m_NetworkObjectId)) + if (spawnedObjects.ContainsKey(key.NetworkObjectId)) { - var behaviour = spawnedObjects[key.m_NetworkObjectId] - .GetNetworkBehaviourAtOrderIndex(key.m_BehaviourIndex); - var nv = behaviour.NetworkVariableFields[key.m_VariableIndex]; + var behaviour = spawnedObjects[key.NetworkObjectId] + .GetNetworkBehaviourAtOrderIndex(key.BehaviourIndex); + var nv = behaviour.NetworkVariableFields[key.VariableIndex]; return nv; } @@ -272,14 +272,14 @@ private INetworkVariable FindNetworkVar(Key key) private void DebugDisplayStore(EntryBlock block, string name) { string table = "=== Snapshot table === " + name + " ===\n"; - for (int i = 0; i < block.m_LastEntry; i++) + for (int i = 0; i < block.LastEntry; i++) { - table += string.Format("NetworkObject {0}:{1}:{2} range [{3}, {4}] ", block.m_Entries[i].key.m_NetworkObjectId, block.m_Entries[i].key.m_BehaviourIndex, - block.m_Entries[i].key.m_VariableIndex, block.m_Entries[i].m_Position, block.m_Entries[i].m_Position + block.m_Entries[i].m_Length); + table += string.Format("NetworkObject {0}:{1}:{2} range [{3}, {4}] ", block.Entries[i].Key.NetworkObjectId, block.Entries[i].Key.BehaviourIndex, + block.Entries[i].Key.VariableIndex, block.Entries[i].Position, block.Entries[i].Position + block.Entries[i].Length); - for (int j = 0; j < block.m_Entries[i].m_Length && j < 4; j++) + for (int j = 0; j < block.Entries[i].Length && j < 4; j++) { - table += block.m_Buffer[block.m_Entries[i].m_Position + j].ToString("X2") + " "; + table += block.Buffer[block.Entries[i].Position + j].ToString("X2") + " "; } table += "\n"; From 1312f9c5d18befbd042ef7cc56911d8847a89f3a Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 5 May 2021 17:32:21 -0400 Subject: [PATCH 20/55] refactor: snapshot. Moved matching 'read' and 'write' together for maintainability --- .../Runtime/Core/SnapshotSystem.cs | 132 ++++++++++-------- 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 9a1c289128..537835df61 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -147,19 +147,63 @@ private void WriteIndex(NetworkBuffer buffer) using (var writer = PooledNetworkWriter.Get(buffer)) { writer.WriteInt16((short)m_Snapshot.LastEntry); - for (var i = 0; i < m_Snapshot.LastEntry; i++) { - writer.WriteUInt64(m_Snapshot.Entries[i].Key.NetworkObjectId); - writer.WriteUInt16(m_Snapshot.Entries[i].Key.BehaviourIndex); - writer.WriteUInt16(m_Snapshot.Entries[i].Key.VariableIndex); - writer.WriteUInt16(m_Snapshot.Entries[i].TickWritten); - writer.WriteUInt16(m_Snapshot.Entries[i].Position); - writer.WriteUInt16(m_Snapshot.Entries[i].Length); + WriteEntry(writer, in m_Snapshot.Entries[i]); } } } + private void ReadIndex(NetworkReader reader) + { + Entry entry; + short entries = reader.ReadInt16(); + + for (var i = 0; i < entries; i++) + { + entry = ReadEntry(reader); + entry.Fresh = true; + + int pos = m_ReceivedSnapshot.Find(entry.Key); + if (pos == Entry.NotFound) + { + pos = m_ReceivedSnapshot.AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, + entry.Key.VariableIndex); + } + + if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) + { + m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); + } + + m_ReceivedSnapshot.Entries[pos] = entry; + } + } + + private void WriteEntry(NetworkWriter writer, in Entry entry) + { + writer.WriteUInt64(entry.Key.NetworkObjectId); + writer.WriteUInt16(entry.Key.BehaviourIndex); + writer.WriteUInt16(entry.Key.VariableIndex); + writer.WriteUInt16(entry.TickWritten); + writer.WriteUInt16(entry.Position); + writer.WriteUInt16(entry.Length); + } + + private Entry ReadEntry(NetworkReader reader) + { + Entry entry; + entry.Key.NetworkObjectId = reader.ReadUInt64(); + entry.Key.BehaviourIndex = reader.ReadUInt16(); + entry.Key.VariableIndex = reader.ReadUInt16(); + entry.TickWritten = reader.ReadUInt16(); + entry.Position = reader.ReadUInt16(); + entry.Length = reader.ReadUInt16(); + entry.Fresh = false; + + return entry; + } + private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) @@ -172,6 +216,29 @@ private void WriteBuffer(NetworkBuffer buffer) buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Beg); } + private void ReadBuffer(NetworkReader reader, Stream snapshotStream) + { + int snapshotSize = reader.ReadUInt16(); + + snapshotStream.Read(m_ReceivedSnapshot.Buffer, 0, snapshotSize); + + for (var i = 0; i < m_ReceivedSnapshot.LastEntry; i++) + { + if (m_ReceivedSnapshot.Entries[i].Fresh && m_ReceivedSnapshot.Entries[i].TickWritten > 0) + { + var nv = FindNetworkVar(m_ReceivedSnapshot.Entries[i].Key); + + m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); + + // todo: Review whether tick still belong in netvar or in the snapshot table. + nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, + m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); + } + + m_ReceivedSnapshot.Entries[i].Fresh = false; + } + } + public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { Key k; @@ -203,53 +270,10 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, public void ReadSnapshot(Stream snapshotStream) { - int snapshotSize = 0; using (var reader = PooledNetworkReader.Get(snapshotStream)) { - Entry entry; - short entries = reader.ReadInt16(); - - for (var i = 0; i < entries; i++) - { - entry.Key.NetworkObjectId = reader.ReadUInt64(); - entry.Key.BehaviourIndex = reader.ReadUInt16(); - entry.Key.VariableIndex = reader.ReadUInt16(); - entry.TickWritten = reader.ReadUInt16(); - entry.Position = reader.ReadUInt16(); - entry.Length = reader.ReadUInt16(); - entry.Fresh = true; - - int pos = m_ReceivedSnapshot.Find(entry.Key); - if (pos == Entry.NotFound) - { - pos = m_ReceivedSnapshot.AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, entry.Key.VariableIndex); - } - - if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) - { - m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); - } - m_ReceivedSnapshot.Entries[pos] = entry; - } - - snapshotSize = reader.ReadUInt16(); - } - - snapshotStream.Read(m_ReceivedSnapshot.Buffer, 0, snapshotSize); - - for (var i = 0; i < m_ReceivedSnapshot.LastEntry; i++) - { - if (m_ReceivedSnapshot.Entries[i].Fresh && m_ReceivedSnapshot.Entries[i].TickWritten > 0) - { - var nv = FindNetworkVar(m_ReceivedSnapshot.Entries[i].Key); - - m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); - - // todo: Review whether tick still belong in netvar or in the snapshot table. - nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); - } - - m_ReceivedSnapshot.Entries[i].Fresh = false; + ReadIndex(reader); + ReadBuffer(reader, snapshotStream); } } @@ -261,9 +285,7 @@ private INetworkVariable FindNetworkVar(Key key) { var behaviour = spawnedObjects[key.NetworkObjectId] .GetNetworkBehaviourAtOrderIndex(key.BehaviourIndex); - var nv = behaviour.NetworkVariableFields[key.VariableIndex]; - - return nv; + return behaviour.NetworkVariableFields[key.VariableIndex]; } return null; From 79a020ecb79296f022a50fd05b39905d8bec3e4f Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 5 May 2021 17:32:21 -0400 Subject: [PATCH 21/55] refactor: snapshot. Moved matching 'read' and 'write' together for maintainability --- .../Runtime/Core/SnapshotSystem.cs | 132 ++++++++++-------- 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 9a1c289128..537835df61 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -147,19 +147,63 @@ private void WriteIndex(NetworkBuffer buffer) using (var writer = PooledNetworkWriter.Get(buffer)) { writer.WriteInt16((short)m_Snapshot.LastEntry); - for (var i = 0; i < m_Snapshot.LastEntry; i++) { - writer.WriteUInt64(m_Snapshot.Entries[i].Key.NetworkObjectId); - writer.WriteUInt16(m_Snapshot.Entries[i].Key.BehaviourIndex); - writer.WriteUInt16(m_Snapshot.Entries[i].Key.VariableIndex); - writer.WriteUInt16(m_Snapshot.Entries[i].TickWritten); - writer.WriteUInt16(m_Snapshot.Entries[i].Position); - writer.WriteUInt16(m_Snapshot.Entries[i].Length); + WriteEntry(writer, in m_Snapshot.Entries[i]); } } } + private void ReadIndex(NetworkReader reader) + { + Entry entry; + short entries = reader.ReadInt16(); + + for (var i = 0; i < entries; i++) + { + entry = ReadEntry(reader); + entry.Fresh = true; + + int pos = m_ReceivedSnapshot.Find(entry.Key); + if (pos == Entry.NotFound) + { + pos = m_ReceivedSnapshot.AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, + entry.Key.VariableIndex); + } + + if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) + { + m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); + } + + m_ReceivedSnapshot.Entries[pos] = entry; + } + } + + private void WriteEntry(NetworkWriter writer, in Entry entry) + { + writer.WriteUInt64(entry.Key.NetworkObjectId); + writer.WriteUInt16(entry.Key.BehaviourIndex); + writer.WriteUInt16(entry.Key.VariableIndex); + writer.WriteUInt16(entry.TickWritten); + writer.WriteUInt16(entry.Position); + writer.WriteUInt16(entry.Length); + } + + private Entry ReadEntry(NetworkReader reader) + { + Entry entry; + entry.Key.NetworkObjectId = reader.ReadUInt64(); + entry.Key.BehaviourIndex = reader.ReadUInt16(); + entry.Key.VariableIndex = reader.ReadUInt16(); + entry.TickWritten = reader.ReadUInt16(); + entry.Position = reader.ReadUInt16(); + entry.Length = reader.ReadUInt16(); + entry.Fresh = false; + + return entry; + } + private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) @@ -172,6 +216,29 @@ private void WriteBuffer(NetworkBuffer buffer) buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Beg); } + private void ReadBuffer(NetworkReader reader, Stream snapshotStream) + { + int snapshotSize = reader.ReadUInt16(); + + snapshotStream.Read(m_ReceivedSnapshot.Buffer, 0, snapshotSize); + + for (var i = 0; i < m_ReceivedSnapshot.LastEntry; i++) + { + if (m_ReceivedSnapshot.Entries[i].Fresh && m_ReceivedSnapshot.Entries[i].TickWritten > 0) + { + var nv = FindNetworkVar(m_ReceivedSnapshot.Entries[i].Key); + + m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); + + // todo: Review whether tick still belong in netvar or in the snapshot table. + nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, + m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); + } + + m_ReceivedSnapshot.Entries[i].Fresh = false; + } + } + public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { Key k; @@ -203,53 +270,10 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, public void ReadSnapshot(Stream snapshotStream) { - int snapshotSize = 0; using (var reader = PooledNetworkReader.Get(snapshotStream)) { - Entry entry; - short entries = reader.ReadInt16(); - - for (var i = 0; i < entries; i++) - { - entry.Key.NetworkObjectId = reader.ReadUInt64(); - entry.Key.BehaviourIndex = reader.ReadUInt16(); - entry.Key.VariableIndex = reader.ReadUInt16(); - entry.TickWritten = reader.ReadUInt16(); - entry.Position = reader.ReadUInt16(); - entry.Length = reader.ReadUInt16(); - entry.Fresh = true; - - int pos = m_ReceivedSnapshot.Find(entry.Key); - if (pos == Entry.NotFound) - { - pos = m_ReceivedSnapshot.AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, entry.Key.VariableIndex); - } - - if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) - { - m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); - } - m_ReceivedSnapshot.Entries[pos] = entry; - } - - snapshotSize = reader.ReadUInt16(); - } - - snapshotStream.Read(m_ReceivedSnapshot.Buffer, 0, snapshotSize); - - for (var i = 0; i < m_ReceivedSnapshot.LastEntry; i++) - { - if (m_ReceivedSnapshot.Entries[i].Fresh && m_ReceivedSnapshot.Entries[i].TickWritten > 0) - { - var nv = FindNetworkVar(m_ReceivedSnapshot.Entries[i].Key); - - m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); - - // todo: Review whether tick still belong in netvar or in the snapshot table. - nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); - } - - m_ReceivedSnapshot.Entries[i].Fresh = false; + ReadIndex(reader); + ReadBuffer(reader, snapshotStream); } } @@ -261,9 +285,7 @@ private INetworkVariable FindNetworkVar(Key key) { var behaviour = spawnedObjects[key.NetworkObjectId] .GetNetworkBehaviourAtOrderIndex(key.BehaviourIndex); - var nv = behaviour.NetworkVariableFields[key.VariableIndex]; - - return nv; + return behaviour.NetworkVariableFields[key.VariableIndex]; } return null; From 41934c8a83d54c323e78d1411bb481079e4ca726 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 09:58:29 -0400 Subject: [PATCH 22/55] feat: snapshot. Adding tick to snapshot frame. Improving DebugDisplay of snapshot table --- .../Runtime/Core/SnapshotSystem.cs | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 537835df61..1b8ad95063 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -96,6 +96,8 @@ public class SnapshotSystem : INetworkUpdateSystem, IDisposable private EntryBlock m_ReceivedSnapshot = new EntryBlock(); private NetworkManager m_NetworkManager = NetworkManager.Singleton; + private ushort m_CurrentTick = 0; + public SnapshotSystem() { this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); @@ -110,21 +112,27 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) { if (updateStage == NetworkUpdateStage.EarlyUpdate) { - if (m_NetworkManager.IsServer) + var tick = m_NetworkManager.NetworkTickSystem.GetTick(); + + if (tick != m_CurrentTick) { - for (int i = 0; i < m_NetworkManager.ConnectedClientsList.Count; i++) + m_CurrentTick = tick; + if (m_NetworkManager.IsServer) { - var clientId = m_NetworkManager.ConnectedClientsList[i].ClientId; - SendSnapshot(clientId); + for (int i = 0; i < m_NetworkManager.ConnectedClientsList.Count; i++) + { + var clientId = m_NetworkManager.ConnectedClientsList[i].ClientId; + SendSnapshot(clientId); + } + } + else + { + SendSnapshot(m_NetworkManager.ServerClientId); } - } - else - { - SendSnapshot(m_NetworkManager.ServerClientId); - } - DebugDisplayStore(m_Snapshot, "Entries"); - DebugDisplayStore(m_ReceivedSnapshot, "Received Entries"); + DebugDisplayStore(m_Snapshot, "Entries"); + DebugDisplayStore(m_ReceivedSnapshot, "Received Entries"); + } } } @@ -133,6 +141,11 @@ private void SendSnapshot(ulong clientId) // Send the entry index and the buffer where the variables are serialized using (var buffer = PooledNetworkBuffer.Get()) { + using (var writer = PooledNetworkWriter.Get(buffer)) + { + writer.WriteUInt16(m_CurrentTick); + } + WriteIndex(buffer); WriteBuffer(buffer); @@ -272,6 +285,8 @@ public void ReadSnapshot(Stream snapshotStream) { using (var reader = PooledNetworkReader.Get(snapshotStream)) { + ushort snapshotTick = reader.ReadUInt16(); + ReadIndex(reader); ReadBuffer(reader, snapshotStream); } @@ -296,8 +311,8 @@ private void DebugDisplayStore(EntryBlock block, string name) string table = "=== Snapshot table === " + name + " ===\n"; for (int i = 0; i < block.LastEntry; i++) { - table += string.Format("NetworkObject {0}:{1}:{2} range [{3}, {4}] ", block.Entries[i].Key.NetworkObjectId, block.Entries[i].Key.BehaviourIndex, - block.Entries[i].Key.VariableIndex, block.Entries[i].Position, block.Entries[i].Position + block.Entries[i].Length); + table += string.Format("NetworkVariable {0}:{1}:{2} written {5}, range [{3}, {4}] ", block.Entries[i].Key.NetworkObjectId, block.Entries[i].Key.BehaviourIndex, + block.Entries[i].Key.VariableIndex, block.Entries[i].Position, block.Entries[i].Position + block.Entries[i].Length, block.Entries[i].TickWritten); for (int j = 0; j < block.Entries[i].Length && j < 4; j++) { From 4969fec5ad255f5ca3ccc20fdf6841473e2e53f8 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 10:08:54 -0400 Subject: [PATCH 23/55] feat: snapshot. Review comment, using the behaviour member id instead of passing its value around --- .../Runtime/Core/NetworkBehaviour.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 9f687d8343..05777f8a97 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -518,7 +518,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps // Sync just the variables for just the objects this client sees for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, k, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, snapshot); } } } @@ -539,7 +539,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps { for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, k, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, snapshot); } } @@ -578,7 +578,7 @@ internal void PostNetworkVariableWrite() } } - internal void VariableUpdate(ulong clientId, int behaviourIndex, SnapshotSystem snapshot) + internal void VariableUpdate(ulong clientId, SnapshotSystem snapshot) { if (!m_VarInit) { @@ -586,7 +586,7 @@ internal void VariableUpdate(ulong clientId, int behaviourIndex, SnapshotSystem } PreNetworkVariableWrite(); - NetworkVariableUpdate(clientId, behaviourIndex, snapshot); + NetworkVariableUpdate(clientId, NetworkBehaviourId, snapshot); } private readonly List m_NetworkVariableIndexesToReset = new List(); From bd466d80d187ee366c172fa93a6a8140f4444f49 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 10:08:54 -0400 Subject: [PATCH 24/55] feat: snapshot. Review comment, using the behaviour member id instead of passing its value around --- .../Runtime/Core/NetworkBehaviour.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 0d730ca6ad..e2c54ce938 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -518,7 +518,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps // Sync just the variables for just the objects this client sees for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, k, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, snapshot); } } } @@ -539,7 +539,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps { for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, k, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, snapshot); } } @@ -578,7 +578,7 @@ internal void PostNetworkVariableWrite() } } - internal void VariableUpdate(ulong clientId, int behaviourIndex, SnapshotSystem snapshot) + internal void VariableUpdate(ulong clientId, SnapshotSystem snapshot) { if (!m_VarInit) { @@ -586,7 +586,7 @@ internal void VariableUpdate(ulong clientId, int behaviourIndex, SnapshotSystem } PreNetworkVariableWrite(); - NetworkVariableUpdate(clientId, behaviourIndex, snapshot); + NetworkVariableUpdate(clientId, NetworkBehaviourId, snapshot); } private readonly List m_NetworkVariableIndexesToReset = new List(); From 8a206ec42f83e4db9f9d879c50393499c760f5a6 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 14:46:44 -0400 Subject: [PATCH 25/55] style: Adding comments/documentation --- .../Runtime/Core/SnapshotSystem.cs | 104 +++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 537835df61..5d84f2cb71 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -12,12 +12,18 @@ namespace MLAPI { + // Structure that acts as a key for a NetworkVariable + // Allows telling which variable we're talking about. + // Might include tick in a future milestone, to address past variable value internal struct Key { public ulong NetworkObjectId; // the NetworkObjectId of the owning GameObject public ushort BehaviourIndex; // the index of the behaviour in this GameObject public ushort VariableIndex; // the index of the variable in this NetworkBehaviour } + + // Index for a NetworkVariable in our table of variables + // Store when a variable was written and where the variable is serialized internal struct Entry { public Key Key; @@ -29,8 +35,14 @@ internal struct Entry public const int NotFound = -1; } + // A table of NetworkVariables that constitutes a Snapshot. + // Stores serialized NetworkVariables + // todo --M1-- + // The EntryBlock will change for M1b with memory management, instead of just Beg, there will be data structure + // around available buffer, etc. internal class EntryBlock { + // todo --M1-- functionality to grow these will be needed in a later milestone private const int k_MaxVariables = 64; private const int k_BufferSize = 20000; @@ -42,11 +54,21 @@ internal class EntryBlock public int LastEntry = 0; public MemoryStream Stream; + /// + /// Constructor + /// Allocated a MemoryStream to be reused for this EntryBlock + /// public EntryBlock() { Stream = new MemoryStream(Buffer, 0, k_BufferSize); } + // todo --M1-- + // Find will change to be efficient in a future milestone + /// + /// Finds the position of a given NetworkVariable, given its key + /// + /// The key we're looking for public int Find(Key key) { for (int i = 0; i < LastEntry; i++) @@ -62,6 +84,10 @@ public int Find(Key key) return Entry.NotFound; } + /// + /// Adds an entry in the table for a new key + /// + // todo: change the params to take a Key instead public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex) { var pos = LastEntry++; @@ -79,8 +105,15 @@ public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex return pos; } + /// + /// Allocate memory from the buffer for the Entry and update it to point to the right location + /// + /// The entry to allocate for + /// The need size in bytes public void AllocateEntry(ref Entry entry, long size) { + // todo --M1-- + // this will change once we start reusing the snapshot buffer memory // todo: deal with free space // todo: deal with full buffer @@ -96,11 +129,19 @@ public class SnapshotSystem : INetworkUpdateSystem, IDisposable private EntryBlock m_ReceivedSnapshot = new EntryBlock(); private NetworkManager m_NetworkManager = NetworkManager.Singleton; + /// + /// Constructor + /// + /// Registers the snapshot system for early updates public SnapshotSystem() { this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); } + /// + /// Dispose + /// + /// Unregisters the snapshot system from early updates public void Dispose() { this.UnregisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); @@ -128,6 +169,13 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) } } + // todo --M1-- + // for now, the full snapshot is always sent + // this will change significantly + /// + /// Send the snapshot to a specific client + /// + /// The client index to send to private void SendSnapshot(ulong clientId) { // Send the entry index and the buffer where the variables are serialized @@ -142,6 +190,10 @@ private void SendSnapshot(ulong clientId) } } + /// + /// Write the snapshot index to a buffer + /// + /// The buffer to write the index to private void WriteIndex(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) @@ -154,6 +206,11 @@ private void WriteIndex(NetworkBuffer buffer) } } + /// + /// Read the snapshot index from a buffer + /// Stores the entry. Allocates memory if needed. The actual buffer will be read later + /// + /// The reader to read the index from private void ReadIndex(NetworkReader reader) { Entry entry; @@ -180,6 +237,11 @@ private void ReadIndex(NetworkReader reader) } } + /// + /// Write an Entry to send + /// Must match ReadEntry + /// + /// The writer to write the entry to private void WriteEntry(NetworkWriter writer, in Entry entry) { writer.WriteUInt64(entry.Key.NetworkObjectId); @@ -190,6 +252,11 @@ private void WriteEntry(NetworkWriter writer, in Entry entry) writer.WriteUInt16(entry.Length); } + /// + /// Read a received Entry + /// Must match WriteEntry + /// + /// The readed to read the entry from private Entry ReadEntry(NetworkReader reader) { Entry entry; @@ -204,6 +271,11 @@ private Entry ReadEntry(NetworkReader reader) return entry; } + /// + /// Write the buffer of a snapshot + /// Must match ReadBuffer + /// + /// The NetworkBuffer to write our buffer of variables to private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) @@ -211,11 +283,19 @@ private void WriteBuffer(NetworkBuffer buffer) writer.WriteUInt16((ushort)m_Snapshot.Beg); } - // todo: this sends the whole buffer + // todo --M1-- + // // this sends the whole buffer // we'll need to build a per-client list buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Beg); } + /// + /// Read the buffer part of a snapshot + /// Must match WriteBuffer + /// The stream is actually a memory stream and we seek to each variable position as we deserialize them + /// + /// The NetworkReader to read our buffer of variables from + /// The stream to read our buffer of variables from private void ReadBuffer(NetworkReader reader, Stream snapshotStream) { int snapshotSize = reader.ReadUInt16(); @@ -230,7 +310,8 @@ private void ReadBuffer(NetworkReader reader, Stream snapshotStream) m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); - // todo: Review whether tick still belong in netvar or in the snapshot table. + // todo --M1-- + // Review whether tick still belong in netvar or in the snapshot table. nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); } @@ -239,6 +320,12 @@ private void ReadBuffer(NetworkReader reader, Stream snapshotStream) } } + // todo: consider using a Key, instead of 3 ints, if it can be exposed + /// + /// Called by the rest of MLAPI when a NetworkVariable changed and need to go in our snapshot + /// Might not happen for all variable on every frame. Might even happen more than once. + /// + /// The NetworkVariable to write, or rather, its INetworkVariable public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { Key k; @@ -268,6 +355,11 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, } } + /// + /// Entry point when a Snapshot is received + /// This is where we read and store the received snapshot + /// + /// The stream to read from public void ReadSnapshot(Stream snapshotStream) { using (var reader = PooledNetworkReader.Get(snapshotStream)) @@ -277,6 +369,11 @@ public void ReadSnapshot(Stream snapshotStream) } } + /// + /// Helper function to find the NetworkVariable object from a key + /// This will look into all spawned objects + /// + /// The key to search for private INetworkVariable FindNetworkVar(Key key) { var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; @@ -291,6 +388,9 @@ private INetworkVariable FindNetworkVar(Key key) return null; } + // todo --M1-- + // This is temporary debugging code. Once the feature is complete, we can consider removing it + // But we could also leave it in in debug to help developers private void DebugDisplayStore(EntryBlock block, string name) { string table = "=== Snapshot table === " + name + " ===\n"; From ce3f2d29bd72dd14017cb191179232657de87771 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 14:46:44 -0400 Subject: [PATCH 26/55] style: Adding comments/documentation --- .../Runtime/Core/SnapshotSystem.cs | 104 +++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 1b8ad95063..4f9d678048 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -12,12 +12,18 @@ namespace MLAPI { + // Structure that acts as a key for a NetworkVariable + // Allows telling which variable we're talking about. + // Might include tick in a future milestone, to address past variable value internal struct Key { public ulong NetworkObjectId; // the NetworkObjectId of the owning GameObject public ushort BehaviourIndex; // the index of the behaviour in this GameObject public ushort VariableIndex; // the index of the variable in this NetworkBehaviour } + + // Index for a NetworkVariable in our table of variables + // Store when a variable was written and where the variable is serialized internal struct Entry { public Key Key; @@ -29,8 +35,14 @@ internal struct Entry public const int NotFound = -1; } + // A table of NetworkVariables that constitutes a Snapshot. + // Stores serialized NetworkVariables + // todo --M1-- + // The EntryBlock will change for M1b with memory management, instead of just Beg, there will be data structure + // around available buffer, etc. internal class EntryBlock { + // todo --M1-- functionality to grow these will be needed in a later milestone private const int k_MaxVariables = 64; private const int k_BufferSize = 20000; @@ -42,11 +54,21 @@ internal class EntryBlock public int LastEntry = 0; public MemoryStream Stream; + /// + /// Constructor + /// Allocated a MemoryStream to be reused for this EntryBlock + /// public EntryBlock() { Stream = new MemoryStream(Buffer, 0, k_BufferSize); } + // todo --M1-- + // Find will change to be efficient in a future milestone + /// + /// Finds the position of a given NetworkVariable, given its key + /// + /// The key we're looking for public int Find(Key key) { for (int i = 0; i < LastEntry; i++) @@ -62,6 +84,10 @@ public int Find(Key key) return Entry.NotFound; } + /// + /// Adds an entry in the table for a new key + /// + // todo: change the params to take a Key instead public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex) { var pos = LastEntry++; @@ -79,8 +105,15 @@ public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex return pos; } + /// + /// Allocate memory from the buffer for the Entry and update it to point to the right location + /// + /// The entry to allocate for + /// The need size in bytes public void AllocateEntry(ref Entry entry, long size) { + // todo --M1-- + // this will change once we start reusing the snapshot buffer memory // todo: deal with free space // todo: deal with full buffer @@ -98,11 +131,19 @@ public class SnapshotSystem : INetworkUpdateSystem, IDisposable private ushort m_CurrentTick = 0; + /// + /// Constructor + /// + /// Registers the snapshot system for early updates public SnapshotSystem() { this.RegisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); } + /// + /// Dispose + /// + /// Unregisters the snapshot system from early updates public void Dispose() { this.UnregisterNetworkUpdate(NetworkUpdateStage.EarlyUpdate); @@ -136,6 +177,13 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) } } + // todo --M1-- + // for now, the full snapshot is always sent + // this will change significantly + /// + /// Send the snapshot to a specific client + /// + /// The client index to send to private void SendSnapshot(ulong clientId) { // Send the entry index and the buffer where the variables are serialized @@ -155,6 +203,10 @@ private void SendSnapshot(ulong clientId) } } + /// + /// Write the snapshot index to a buffer + /// + /// The buffer to write the index to private void WriteIndex(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) @@ -167,6 +219,11 @@ private void WriteIndex(NetworkBuffer buffer) } } + /// + /// Read the snapshot index from a buffer + /// Stores the entry. Allocates memory if needed. The actual buffer will be read later + /// + /// The reader to read the index from private void ReadIndex(NetworkReader reader) { Entry entry; @@ -193,6 +250,11 @@ private void ReadIndex(NetworkReader reader) } } + /// + /// Write an Entry to send + /// Must match ReadEntry + /// + /// The writer to write the entry to private void WriteEntry(NetworkWriter writer, in Entry entry) { writer.WriteUInt64(entry.Key.NetworkObjectId); @@ -203,6 +265,11 @@ private void WriteEntry(NetworkWriter writer, in Entry entry) writer.WriteUInt16(entry.Length); } + /// + /// Read a received Entry + /// Must match WriteEntry + /// + /// The readed to read the entry from private Entry ReadEntry(NetworkReader reader) { Entry entry; @@ -217,6 +284,11 @@ private Entry ReadEntry(NetworkReader reader) return entry; } + /// + /// Write the buffer of a snapshot + /// Must match ReadBuffer + /// + /// The NetworkBuffer to write our buffer of variables to private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) @@ -224,11 +296,19 @@ private void WriteBuffer(NetworkBuffer buffer) writer.WriteUInt16((ushort)m_Snapshot.Beg); } - // todo: this sends the whole buffer + // todo --M1-- + // // this sends the whole buffer // we'll need to build a per-client list buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Beg); } + /// + /// Read the buffer part of a snapshot + /// Must match WriteBuffer + /// The stream is actually a memory stream and we seek to each variable position as we deserialize them + /// + /// The NetworkReader to read our buffer of variables from + /// The stream to read our buffer of variables from private void ReadBuffer(NetworkReader reader, Stream snapshotStream) { int snapshotSize = reader.ReadUInt16(); @@ -243,7 +323,8 @@ private void ReadBuffer(NetworkReader reader, Stream snapshotStream) m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); - // todo: Review whether tick still belong in netvar or in the snapshot table. + // todo --M1-- + // Review whether tick still belong in netvar or in the snapshot table. nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); } @@ -252,6 +333,12 @@ private void ReadBuffer(NetworkReader reader, Stream snapshotStream) } } + // todo: consider using a Key, instead of 3 ints, if it can be exposed + /// + /// Called by the rest of MLAPI when a NetworkVariable changed and need to go in our snapshot + /// Might not happen for all variable on every frame. Might even happen more than once. + /// + /// The NetworkVariable to write, or rather, its INetworkVariable public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { Key k; @@ -281,6 +368,11 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, } } + /// + /// Entry point when a Snapshot is received + /// This is where we read and store the received snapshot + /// + /// The stream to read from public void ReadSnapshot(Stream snapshotStream) { using (var reader = PooledNetworkReader.Get(snapshotStream)) @@ -292,6 +384,11 @@ public void ReadSnapshot(Stream snapshotStream) } } + /// + /// Helper function to find the NetworkVariable object from a key + /// This will look into all spawned objects + /// + /// The key to search for private INetworkVariable FindNetworkVar(Key key) { var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; @@ -306,6 +403,9 @@ private INetworkVariable FindNetworkVar(Key key) return null; } + // todo --M1-- + // This is temporary debugging code. Once the feature is complete, we can consider removing it + // But we could also leave it in in debug to help developers private void DebugDisplayStore(EntryBlock block, string name) { string table = "=== Snapshot table === " + name + " ===\n"; From b349d6f98c80442a9d1a4f4c05b8f3476b4c0128 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 17:56:01 -0400 Subject: [PATCH 27/55] feat: snapshot. Code review comments --- .../Runtime/Core/NetworkBehaviour.cs | 135 +++++++++--------- .../Runtime/Core/NetworkManager.cs | 5 +- .../Runtime/Core/SnapshotSystem.cs | 36 ++--- .../NetworkVariable/NetworkVariable.cs | 2 +- 4 files changed, 90 insertions(+), 88 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 05777f8a97..9b7fabbd68 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -489,7 +489,7 @@ internal void InitializeVariables() private static ProfilerMarker s_NetworkBehaviourUpdate = new ProfilerMarker($"{nameof(NetworkBehaviour)}.{nameof(NetworkBehaviourUpdate)}"); #endif - internal static void NetworkBehaviourUpdate(NetworkManager networkManager, SnapshotSystem snapshot) + internal static void NetworkBehaviourUpdate(NetworkManager networkManager) { // Do not execute NetworkBehaviourUpdate more than once per network tick ushort tick = networkManager.NetworkTickSystem.GetTick(); @@ -518,7 +518,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps // Sync just the variables for just the objects this client sees for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, networkManager.SnapshotSystem); } } } @@ -539,7 +539,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps { for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, networkManager.SnapshotSystem); } } @@ -607,94 +607,97 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex, SnapshotS } } - for (int j = 0; j < m_ChannelMappedNetworkVariableIndexes.Count; j++) + if (s_UseClassicDelta) { - if (!s_UseClassicDelta) + for (int j = 0; j < m_ChannelMappedNetworkVariableIndexes.Count; j++) { - break; - } - using (var buffer = PooledNetworkBuffer.Get()) - { - using (var writer = PooledNetworkWriter.Get(buffer)) + using (var buffer = PooledNetworkBuffer.Get()) { - writer.WriteUInt64Packed(NetworkObjectId); - writer.WriteUInt16Packed(NetworkObject.GetNetworkBehaviourOrderIndex(this)); - - // Write the current tick frame - // todo: this is currently done per channel, per tick. The snapshot system might improve on this - writer.WriteUInt16Packed(CurrentTick); - - bool writtenAny = false; - for (int k = 0; k < NetworkVariableFields.Count; k++) + using (var writer = PooledNetworkWriter.Get(buffer)) { - if (!m_ChannelMappedNetworkVariableIndexes[j].Contains(k)) - { - // This var does not belong to the currently iterating channel group. - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - writer.WriteUInt16Packed(0); - } - else - { - writer.WriteBool(false); - } - - continue; - } + writer.WriteUInt64Packed(NetworkObjectId); + writer.WriteUInt16Packed(NetworkObject.GetNetworkBehaviourOrderIndex(this)); - bool isDirty = NetworkVariableFields[k].IsDirty(); // cache this here. You never know what operations users will do in the dirty methods + // Write the current tick frame + // todo: this is currently done per channel, per tick. The snapshot system might improve on this + writer.WriteUInt16Packed(CurrentTick); - // if I'm dirty AND a client, write (server always has all permissions) - // if I'm dirty AND the server AND the client can read me, send. - bool shouldWrite = isDirty && (!IsServer || NetworkVariableFields[k].CanClientRead(clientId)); - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + bool writtenAny = false; + for (int k = 0; k < NetworkVariableFields.Count; k++) { - if (!shouldWrite) + if (!m_ChannelMappedNetworkVariableIndexes[j].Contains(k)) { - writer.WriteUInt16Packed(0); + // This var does not belong to the currently iterating channel group. + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + writer.WriteUInt16Packed(0); + } + else + { + writer.WriteBool(false); + } + + continue; } - } - else - { - writer.WriteBool(shouldWrite); - } - if (shouldWrite) - { - writtenAny = true; + bool isDirty = + NetworkVariableFields[k] + .IsDirty(); // cache this here. You never know what operations users will do in the dirty methods - // write the network tick at which this NetworkVariable was modified remotely - // this will allow lag-compensation - writer.WriteUInt16Packed(NetworkVariableFields[k].RemoteTick); + // if I'm dirty AND a client, write (server always has all permissions) + // if I'm dirty AND the server AND the client can read me, send. + bool shouldWrite = isDirty && + (!IsServer || NetworkVariableFields[k].CanClientRead(clientId)); if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { - using (var varBuffer = PooledNetworkBuffer.Get()) + if (!shouldWrite) { - NetworkVariableFields[k].WriteDelta(varBuffer); - varBuffer.PadBuffer(); - - writer.WriteUInt16Packed((ushort)varBuffer.Length); - buffer.CopyFrom(varBuffer); + writer.WriteUInt16Packed(0); } } else { - NetworkVariableFields[k].WriteDelta(buffer); + writer.WriteBool(shouldWrite); } - if (!m_NetworkVariableIndexesToResetSet.Contains(k)) + if (shouldWrite) { - m_NetworkVariableIndexesToResetSet.Add(k); - m_NetworkVariableIndexesToReset.Add(k); + writtenAny = true; + + // write the network tick at which this NetworkVariable was modified remotely + // this will allow lag-compensation + writer.WriteUInt16Packed(NetworkVariableFields[k].RemoteTick); + + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + using (var varBuffer = PooledNetworkBuffer.Get()) + { + NetworkVariableFields[k].WriteDelta(varBuffer); + varBuffer.PadBuffer(); + + writer.WriteUInt16Packed((ushort) varBuffer.Length); + buffer.CopyFrom(varBuffer); + } + } + else + { + NetworkVariableFields[k].WriteDelta(buffer); + } + + if (!m_NetworkVariableIndexesToResetSet.Contains(k)) + { + m_NetworkVariableIndexesToResetSet.Add(k); + m_NetworkVariableIndexesToReset.Add(k); + } } } - } - if (writtenAny) - { - NetworkManager.MessageSender.Send(clientId, NetworkConstants.NETWORK_VARIABLE_DELTA, m_ChannelsForNetworkVariableGroups[j], buffer); + if (writtenAny) + { + NetworkManager.MessageSender.Send(clientId, NetworkConstants.NETWORK_VARIABLE_DELTA, + m_ChannelsForNetworkVariableGroups[j], buffer); + } } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index a5bc7995df..88762d64b8 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -912,7 +912,7 @@ private void OnNetworkPreUpdate() if (NetworkConfig.EnableNetworkVariable) { // Do NetworkVariable updates - NetworkBehaviour.NetworkBehaviourUpdate(this, SnapshotSystem); + NetworkBehaviour.NetworkBehaviourUpdate(this); } if (!IsServer && NetworkConfig.EnableMessageBuffering) @@ -1171,7 +1171,6 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, switch (messageType) { case NetworkConstants.SNAPSHOT_DATA: - UnityEngine.Debug.Log("Received Snapshot Data"); InternalMessageHandler.HandleSnapshot(clientId, messageStream); break; case NetworkConstants.CONNECTION_REQUEST: @@ -1281,7 +1280,7 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, { MessageHandler.HandleAllClientsSwitchSceneCompleted(clientId, messageStream); } - + break; case NetworkConstants.SERVER_LOG: if (IsServer && NetworkConfig.EnableNetworkLogs) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 5d84f2cb71..6514ffd7fa 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -15,7 +15,7 @@ namespace MLAPI // Structure that acts as a key for a NetworkVariable // Allows telling which variable we're talking about. // Might include tick in a future milestone, to address past variable value - internal struct Key + internal struct VariableKey { public ulong NetworkObjectId; // the NetworkObjectId of the owning GameObject public ushort BehaviourIndex; // the index of the behaviour in this GameObject @@ -26,7 +26,7 @@ internal struct Key // Store when a variable was written and where the variable is serialized internal struct Entry { - public Key Key; + public VariableKey Key; public ushort TickWritten; // the network tick at which this variable was set public ushort Position; // the offset in our Buffer public ushort Length; // the length of the data in Buffer @@ -38,17 +38,16 @@ internal struct Entry // A table of NetworkVariables that constitutes a Snapshot. // Stores serialized NetworkVariables // todo --M1-- - // The EntryBlock will change for M1b with memory management, instead of just Beg, there will be data structure + // The Snapshot will change for M1b with memory management, instead of just FreeMemoryPosition, there will be data structure // around available buffer, etc. - internal class EntryBlock + internal class Snapshot { // todo --M1-- functionality to grow these will be needed in a later milestone private const int k_MaxVariables = 64; private const int k_BufferSize = 20000; public byte[] Buffer = new byte[k_BufferSize]; - public int Beg = 0; // todo: clarify usage. Right now, this is the beginning of the _free_ space. - public int End = 0; + public int FreeMemoryPosition = 0; public Entry[] Entries = new Entry[k_MaxVariables]; public int LastEntry = 0; @@ -56,9 +55,9 @@ internal class EntryBlock /// /// Constructor - /// Allocated a MemoryStream to be reused for this EntryBlock + /// Allocated a MemoryStream to be reused for this Snapshot /// - public EntryBlock() + public Snapshot() { Stream = new MemoryStream(Buffer, 0, k_BufferSize); } @@ -69,7 +68,7 @@ public EntryBlock() /// Finds the position of a given NetworkVariable, given its key /// /// The key we're looking for - public int Find(Key key) + public int Find(VariableKey key) { for (int i = 0; i < LastEntry; i++) { @@ -117,16 +116,16 @@ public void AllocateEntry(ref Entry entry, long size) // todo: deal with free space // todo: deal with full buffer - entry.Position = (ushort)Beg; + entry.Position = (ushort)FreeMemoryPosition; entry.Length = (ushort)size; - Beg += (int)size; + FreeMemoryPosition += (int)size; } } public class SnapshotSystem : INetworkUpdateSystem, IDisposable { - private EntryBlock m_Snapshot = new EntryBlock(); - private EntryBlock m_ReceivedSnapshot = new EntryBlock(); + private Snapshot m_Snapshot = new Snapshot(); + private Snapshot m_ReceivedSnapshot = new Snapshot(); private NetworkManager m_NetworkManager = NetworkManager.Singleton; /// @@ -228,6 +227,7 @@ private void ReadIndex(NetworkReader reader) entry.Key.VariableIndex); } + // if we need to allocate more memory (the variable grew in size) if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) { m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); @@ -280,13 +280,13 @@ private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteUInt16((ushort)m_Snapshot.Beg); + writer.WriteUInt16((ushort)m_Snapshot.FreeMemoryPosition); } // todo --M1-- // // this sends the whole buffer // we'll need to build a per-client list - buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Beg); + buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.FreeMemoryPosition); } /// @@ -328,7 +328,7 @@ private void ReadBuffer(NetworkReader reader, Stream snapshotStream) /// The NetworkVariable to write, or rather, its INetworkVariable public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { - Key k; + VariableKey k; k.NetworkObjectId = networkObjectId; k.BehaviourIndex = (ushort)behaviourIndex; k.VariableIndex = (ushort)variableIndex; @@ -374,7 +374,7 @@ public void ReadSnapshot(Stream snapshotStream) /// This will look into all spawned objects /// /// The key to search for - private INetworkVariable FindNetworkVar(Key key) + private INetworkVariable FindNetworkVar(VariableKey key) { var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; @@ -391,7 +391,7 @@ private INetworkVariable FindNetworkVar(Key key) // todo --M1-- // This is temporary debugging code. Once the feature is complete, we can consider removing it // But we could also leave it in in debug to help developers - private void DebugDisplayStore(EntryBlock block, string name) + private void DebugDisplayStore(Snapshot block, string name) { string table = "=== Snapshot table === " + name + " ===\n"; for (int i = 0; i < block.LastEntry; i++) diff --git a/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs index aee32b6b60..42598e064f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs @@ -203,7 +203,7 @@ public void ReadDelta(Stream stream, bool keepDirtyDelta, ushort localTick, usho } OnValueChanged?.Invoke(previousValue, m_InternalValue); - } + } } /// From 3d76b73ae6afc48d1b919a0d19b95e891418adf1 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 17:56:01 -0400 Subject: [PATCH 28/55] feat: snapshot. Code review comments --- .../Runtime/Core/NetworkBehaviour.cs | 135 +++++++++--------- .../Runtime/Core/NetworkManager.cs | 5 +- .../Runtime/Core/SnapshotSystem.cs | 36 ++--- .../NetworkVariable/NetworkVariable.cs | 2 +- 4 files changed, 90 insertions(+), 88 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index e2c54ce938..fa2c11020c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -489,7 +489,7 @@ internal void InitializeVariables() private static ProfilerMarker s_NetworkBehaviourUpdate = new ProfilerMarker($"{nameof(NetworkBehaviour)}.{nameof(NetworkBehaviourUpdate)}"); #endif - internal static void NetworkBehaviourUpdate(NetworkManager networkManager, SnapshotSystem snapshot) + internal static void NetworkBehaviourUpdate(NetworkManager networkManager) { // Do not execute NetworkBehaviourUpdate more than once per network tick ushort tick = networkManager.NetworkTickSystem.GetTick(); @@ -518,7 +518,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps // Sync just the variables for just the objects this client sees for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, networkManager.SnapshotSystem); } } } @@ -539,7 +539,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager, Snaps { for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, snapshot); + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, networkManager.SnapshotSystem); } } @@ -607,94 +607,97 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex, SnapshotS } } - for (int j = 0; j < m_ChannelMappedNetworkVariableIndexes.Count; j++) + if (s_UseClassicDelta) { - if (!s_UseClassicDelta) + for (int j = 0; j < m_ChannelMappedNetworkVariableIndexes.Count; j++) { - break; - } - using (var buffer = PooledNetworkBuffer.Get()) - { - using (var writer = PooledNetworkWriter.Get(buffer)) + using (var buffer = PooledNetworkBuffer.Get()) { - writer.WriteUInt64Packed(NetworkObjectId); - writer.WriteUInt16Packed(NetworkObject.GetNetworkBehaviourOrderIndex(this)); - - // Write the current tick frame - // todo: this is currently done per channel, per tick. The snapshot system might improve on this - writer.WriteUInt16Packed(CurrentTick); - - bool writtenAny = false; - for (int k = 0; k < NetworkVariableFields.Count; k++) + using (var writer = PooledNetworkWriter.Get(buffer)) { - if (!m_ChannelMappedNetworkVariableIndexes[j].Contains(k)) - { - // This var does not belong to the currently iterating channel group. - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) - { - writer.WriteUInt16Packed(0); - } - else - { - writer.WriteBool(false); - } - - continue; - } + writer.WriteUInt64Packed(NetworkObjectId); + writer.WriteUInt16Packed(NetworkObject.GetNetworkBehaviourOrderIndex(this)); - bool isDirty = NetworkVariableFields[k].IsDirty(); // cache this here. You never know what operations users will do in the dirty methods + // Write the current tick frame + // todo: this is currently done per channel, per tick. The snapshot system might improve on this + writer.WriteUInt16Packed(CurrentTick); - // if I'm dirty AND a client, write (server always has all permissions) - // if I'm dirty AND the server AND the client can read me, send. - bool shouldWrite = isDirty && (!IsServer || NetworkVariableFields[k].CanClientRead(clientId)); - - if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + bool writtenAny = false; + for (int k = 0; k < NetworkVariableFields.Count; k++) { - if (!shouldWrite) + if (!m_ChannelMappedNetworkVariableIndexes[j].Contains(k)) { - writer.WriteUInt16Packed(0); + // This var does not belong to the currently iterating channel group. + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + writer.WriteUInt16Packed(0); + } + else + { + writer.WriteBool(false); + } + + continue; } - } - else - { - writer.WriteBool(shouldWrite); - } - if (shouldWrite) - { - writtenAny = true; + bool isDirty = + NetworkVariableFields[k] + .IsDirty(); // cache this here. You never know what operations users will do in the dirty methods - // write the network tick at which this NetworkVariable was modified remotely - // this will allow lag-compensation - writer.WriteUInt16Packed(NetworkVariableFields[k].RemoteTick); + // if I'm dirty AND a client, write (server always has all permissions) + // if I'm dirty AND the server AND the client can read me, send. + bool shouldWrite = isDirty && + (!IsServer || NetworkVariableFields[k].CanClientRead(clientId)); if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { - using (var varBuffer = PooledNetworkBuffer.Get()) + if (!shouldWrite) { - NetworkVariableFields[k].WriteDelta(varBuffer); - varBuffer.PadBuffer(); - - writer.WriteUInt16Packed((ushort)varBuffer.Length); - buffer.CopyFrom(varBuffer); + writer.WriteUInt16Packed(0); } } else { - NetworkVariableFields[k].WriteDelta(buffer); + writer.WriteBool(shouldWrite); } - if (!m_NetworkVariableIndexesToResetSet.Contains(k)) + if (shouldWrite) { - m_NetworkVariableIndexesToResetSet.Add(k); - m_NetworkVariableIndexesToReset.Add(k); + writtenAny = true; + + // write the network tick at which this NetworkVariable was modified remotely + // this will allow lag-compensation + writer.WriteUInt16Packed(NetworkVariableFields[k].RemoteTick); + + if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) + { + using (var varBuffer = PooledNetworkBuffer.Get()) + { + NetworkVariableFields[k].WriteDelta(varBuffer); + varBuffer.PadBuffer(); + + writer.WriteUInt16Packed((ushort) varBuffer.Length); + buffer.CopyFrom(varBuffer); + } + } + else + { + NetworkVariableFields[k].WriteDelta(buffer); + } + + if (!m_NetworkVariableIndexesToResetSet.Contains(k)) + { + m_NetworkVariableIndexesToResetSet.Add(k); + m_NetworkVariableIndexesToReset.Add(k); + } } } - } - if (writtenAny) - { - NetworkManager.MessageSender.Send(clientId, NetworkConstants.NETWORK_VARIABLE_DELTA, m_ChannelsForNetworkVariableGroups[j], buffer); + if (writtenAny) + { + NetworkManager.MessageSender.Send(clientId, NetworkConstants.NETWORK_VARIABLE_DELTA, + m_ChannelsForNetworkVariableGroups[j], buffer); + } } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index a5bc7995df..88762d64b8 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -912,7 +912,7 @@ private void OnNetworkPreUpdate() if (NetworkConfig.EnableNetworkVariable) { // Do NetworkVariable updates - NetworkBehaviour.NetworkBehaviourUpdate(this, SnapshotSystem); + NetworkBehaviour.NetworkBehaviourUpdate(this); } if (!IsServer && NetworkConfig.EnableMessageBuffering) @@ -1171,7 +1171,6 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, switch (messageType) { case NetworkConstants.SNAPSHOT_DATA: - UnityEngine.Debug.Log("Received Snapshot Data"); InternalMessageHandler.HandleSnapshot(clientId, messageStream); break; case NetworkConstants.CONNECTION_REQUEST: @@ -1281,7 +1280,7 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, { MessageHandler.HandleAllClientsSwitchSceneCompleted(clientId, messageStream); } - + break; case NetworkConstants.SERVER_LOG: if (IsServer && NetworkConfig.EnableNetworkLogs) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 4f9d678048..fb583d84ed 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -15,7 +15,7 @@ namespace MLAPI // Structure that acts as a key for a NetworkVariable // Allows telling which variable we're talking about. // Might include tick in a future milestone, to address past variable value - internal struct Key + internal struct VariableKey { public ulong NetworkObjectId; // the NetworkObjectId of the owning GameObject public ushort BehaviourIndex; // the index of the behaviour in this GameObject @@ -26,7 +26,7 @@ internal struct Key // Store when a variable was written and where the variable is serialized internal struct Entry { - public Key Key; + public VariableKey Key; public ushort TickWritten; // the network tick at which this variable was set public ushort Position; // the offset in our Buffer public ushort Length; // the length of the data in Buffer @@ -38,17 +38,16 @@ internal struct Entry // A table of NetworkVariables that constitutes a Snapshot. // Stores serialized NetworkVariables // todo --M1-- - // The EntryBlock will change for M1b with memory management, instead of just Beg, there will be data structure + // The Snapshot will change for M1b with memory management, instead of just FreeMemoryPosition, there will be data structure // around available buffer, etc. - internal class EntryBlock + internal class Snapshot { // todo --M1-- functionality to grow these will be needed in a later milestone private const int k_MaxVariables = 64; private const int k_BufferSize = 20000; public byte[] Buffer = new byte[k_BufferSize]; - public int Beg = 0; // todo: clarify usage. Right now, this is the beginning of the _free_ space. - public int End = 0; + public int FreeMemoryPosition = 0; public Entry[] Entries = new Entry[k_MaxVariables]; public int LastEntry = 0; @@ -56,9 +55,9 @@ internal class EntryBlock /// /// Constructor - /// Allocated a MemoryStream to be reused for this EntryBlock + /// Allocated a MemoryStream to be reused for this Snapshot /// - public EntryBlock() + public Snapshot() { Stream = new MemoryStream(Buffer, 0, k_BufferSize); } @@ -69,7 +68,7 @@ public EntryBlock() /// Finds the position of a given NetworkVariable, given its key /// /// The key we're looking for - public int Find(Key key) + public int Find(VariableKey key) { for (int i = 0; i < LastEntry; i++) { @@ -117,16 +116,16 @@ public void AllocateEntry(ref Entry entry, long size) // todo: deal with free space // todo: deal with full buffer - entry.Position = (ushort)Beg; + entry.Position = (ushort)FreeMemoryPosition; entry.Length = (ushort)size; - Beg += (int)size; + FreeMemoryPosition += (int)size; } } public class SnapshotSystem : INetworkUpdateSystem, IDisposable { - private EntryBlock m_Snapshot = new EntryBlock(); - private EntryBlock m_ReceivedSnapshot = new EntryBlock(); + private Snapshot m_Snapshot = new Snapshot(); + private Snapshot m_ReceivedSnapshot = new Snapshot(); private NetworkManager m_NetworkManager = NetworkManager.Singleton; private ushort m_CurrentTick = 0; @@ -241,6 +240,7 @@ private void ReadIndex(NetworkReader reader) entry.Key.VariableIndex); } + // if we need to allocate more memory (the variable grew in size) if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) { m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); @@ -293,13 +293,13 @@ private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteUInt16((ushort)m_Snapshot.Beg); + writer.WriteUInt16((ushort)m_Snapshot.FreeMemoryPosition); } // todo --M1-- // // this sends the whole buffer // we'll need to build a per-client list - buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Beg); + buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.FreeMemoryPosition); } /// @@ -341,7 +341,7 @@ private void ReadBuffer(NetworkReader reader, Stream snapshotStream) /// The NetworkVariable to write, or rather, its INetworkVariable public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, INetworkVariable networkVariable) { - Key k; + VariableKey k; k.NetworkObjectId = networkObjectId; k.BehaviourIndex = (ushort)behaviourIndex; k.VariableIndex = (ushort)variableIndex; @@ -389,7 +389,7 @@ public void ReadSnapshot(Stream snapshotStream) /// This will look into all spawned objects /// /// The key to search for - private INetworkVariable FindNetworkVar(Key key) + private INetworkVariable FindNetworkVar(VariableKey key) { var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; @@ -406,7 +406,7 @@ private INetworkVariable FindNetworkVar(Key key) // todo --M1-- // This is temporary debugging code. Once the feature is complete, we can consider removing it // But we could also leave it in in debug to help developers - private void DebugDisplayStore(EntryBlock block, string name) + private void DebugDisplayStore(Snapshot block, string name) { string table = "=== Snapshot table === " + name + " ===\n"; for (int i = 0; i < block.LastEntry; i++) diff --git a/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs index aee32b6b60..42598e064f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs @@ -203,7 +203,7 @@ public void ReadDelta(Stream stream, bool keepDirtyDelta, ushort localTick, usho } OnValueChanged?.Invoke(previousValue, m_InternalValue); - } + } } /// From 1cfde0521cc246fd9e436db32641e4de0d325dab Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 18:30:45 -0400 Subject: [PATCH 29/55] Update com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs Co-authored-by: Matt Walsh <69258106+mattwalsh-unity@users.noreply.github.com> --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 9b7fabbd68..db064793df 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -592,7 +592,7 @@ internal void VariableUpdate(ulong clientId, SnapshotSystem snapshot) private readonly List m_NetworkVariableIndexesToReset = new List(); private readonly HashSet m_NetworkVariableIndexesToResetSet = new HashSet(); - private void NetworkVariableUpdate(ulong clientId, int behaviourIndex, SnapshotSystem snapshot) + private void NetworkVariableUpdate(ulong clientId, int behaviourIndex) { if (!CouldHaveDirtyNetworkVariables()) { From 166b7d81dce49e33e54c573683d2b0b5f925fa29 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 18:30:56 -0400 Subject: [PATCH 30/55] Update com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs Co-authored-by: Matt Walsh <69258106+mattwalsh-unity@users.noreply.github.com> --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index db064793df..47ea927d93 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -603,7 +603,7 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex) { for (int k = 0; k < NetworkVariableFields.Count; k++) { - snapshot.Store(NetworkObjectId, behaviourIndex, k, NetworkVariableFields[k]); + NetworkManager.SnapshotSystem.Store(NetworkObjectId, behaviourIndex, k, NetworkVariableFields[k]); } } From 3411d95cca627b9c2fec7578b76febeec9749dc0 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 6 May 2021 18:34:15 -0400 Subject: [PATCH 31/55] fix: commit suggestion required another small matching change --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 47ea927d93..e4df1d9ad9 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -586,7 +586,7 @@ internal void VariableUpdate(ulong clientId, SnapshotSystem snapshot) } PreNetworkVariableWrite(); - NetworkVariableUpdate(clientId, NetworkBehaviourId, snapshot); + NetworkVariableUpdate(clientId, NetworkBehaviourId); } private readonly List m_NetworkVariableIndexesToReset = new List(); From 95a06aec9bcdc529dc2e4e1fd32691aa10586cd9 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Fri, 7 May 2021 15:27:25 -0400 Subject: [PATCH 32/55] feat: snapshot. removing unneeded change --- .../Runtime/Core/NetworkBehaviour.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index ce235d9e4a..3adcd12959 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -518,7 +518,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager) // Sync just the variables for just the objects this client sees for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId, networkManager.SnapshotSystem); + sobj.ChildNetworkBehaviours[k].VariableUpdate(client.ClientId); } } } @@ -539,7 +539,7 @@ internal static void NetworkBehaviourUpdate(NetworkManager networkManager) { for (int k = 0; k < sobj.ChildNetworkBehaviours.Count; k++) { - sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId, networkManager.SnapshotSystem); + sobj.ChildNetworkBehaviours[k].VariableUpdate(networkManager.ServerClientId); } } @@ -578,7 +578,7 @@ internal void PostNetworkVariableWrite() } } - internal void VariableUpdate(ulong clientId, SnapshotSystem snapshot) + internal void VariableUpdate(ulong clientId) { if (!m_VarInit) { From bb14ff03a0d8259e9e6d492331e335bb27b87cda Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 12 May 2021 18:04:10 -0400 Subject: [PATCH 33/55] feat: snapshot. Adding acknowledgment message. Making the Snapshot know its NetworkManager. Moving reading and writing of Entries and buffer into the Snapshot class. Keeping a receive snapshot per client --- .../Runtime/Configuration/NetworkConstants.cs | 3 +- .../Runtime/Core/NetworkManager.cs | 4 + .../Runtime/Core/SnapshotSystem.cs | 280 ++++++++++-------- .../Messaging/InternalMessageHandler.cs | 7 +- 4 files changed, 173 insertions(+), 121 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs index 6afbf53b41..1d75596581 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConstants.cs @@ -24,6 +24,7 @@ internal static class NetworkConstants internal const byte NAMED_MESSAGE = 22; internal const byte SERVER_LOG = 23; internal const byte SNAPSHOT_DATA = 25; + internal const byte SNAPSHOT_ACK = 26; internal const byte SERVER_RPC = 30; internal const byte CLIENT_RPC = 31; internal const byte INVALID = 32; @@ -56,7 +57,7 @@ internal static class NetworkConstants "SERVER_LOG", "", "SNAPSHOT_DATA", - "", + "SNAPSHOT_ACK", "", "", "", diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 88762d64b8..b7eb9a508a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -1173,6 +1173,10 @@ internal void HandleIncomingData(ulong clientId, NetworkChannel networkChannel, case NetworkConstants.SNAPSHOT_DATA: InternalMessageHandler.HandleSnapshot(clientId, messageStream); break; + case NetworkConstants.SNAPSHOT_ACK: + InternalMessageHandler.HandleAck(clientId, messageStream); + break; + case NetworkConstants.CONNECTION_REQUEST: if (IsServer) { diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index fb583d84ed..c89637bf03 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -53,13 +53,16 @@ internal class Snapshot public int LastEntry = 0; public MemoryStream Stream; + private NetworkManager m_NetworkManager; + /// /// Constructor /// Allocated a MemoryStream to be reused for this Snapshot /// - public Snapshot() + public Snapshot(NetworkManager networkManager) { Stream = new MemoryStream(Buffer, 0, k_BufferSize); + m_NetworkManager = networkManager; } // todo --M1-- @@ -74,7 +77,8 @@ public int Find(VariableKey key) { if (Entries[i].Key.NetworkObjectId == key.NetworkObjectId && Entries[i].Key.BehaviourIndex == key.BehaviourIndex && - Entries[i].Key.VariableIndex == key.VariableIndex) + Entries[i].Key.VariableIndex == key.VariableIndex/* && + Entries[i].Key.TickWritten == key.TickWritten*/) { return i; } @@ -104,6 +108,40 @@ public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex return pos; } + /// + /// Write an Entry to send + /// Must match ReadEntry + /// + /// The writer to write the entry to + internal void WriteEntry(NetworkWriter writer, in Entry entry) + { + writer.WriteUInt64(entry.Key.NetworkObjectId); + writer.WriteUInt16(entry.Key.BehaviourIndex); + writer.WriteUInt16(entry.Key.VariableIndex); + writer.WriteUInt16(entry.TickWritten); + writer.WriteUInt16(entry.Position); + writer.WriteUInt16(entry.Length); + } + + /// + /// Read a received Entry + /// Must match WriteEntry + /// + /// The readed to read the entry from + internal Entry ReadEntry(NetworkReader reader) + { + Entry entry; + entry.Key.NetworkObjectId = reader.ReadUInt64(); + entry.Key.BehaviourIndex = reader.ReadUInt16(); + entry.Key.VariableIndex = reader.ReadUInt16(); + entry.TickWritten = reader.ReadUInt16(); + entry.Position = reader.ReadUInt16(); + entry.Length = reader.ReadUInt16(); + entry.Fresh = false; + + return entry; + } + /// /// Allocate memory from the buffer for the Entry and update it to point to the right location /// @@ -120,13 +158,94 @@ public void AllocateEntry(ref Entry entry, long size) entry.Length = (ushort)size; FreeMemoryPosition += (int)size; } + + /// + /// Read the buffer part of a snapshot + /// Must match WriteBuffer + /// The stream is actually a memory stream and we seek to each variable position as we deserialize them + /// + /// The NetworkReader to read our buffer of variables from + /// The stream to read our buffer of variables from + internal void ReadBuffer(NetworkReader reader, Stream snapshotStream) + { + int snapshotSize = reader.ReadUInt16(); + + snapshotStream.Read(Buffer, 0, snapshotSize); + + for (var i = 0; i < LastEntry; i++) + { + if (Entries[i].Fresh && Entries[i].TickWritten > 0) + { + var nv = FindNetworkVar(Entries[i].Key); + + Stream.Seek(Entries[i].Position, SeekOrigin.Begin); + + // todo --M1-- + // Review whether tick still belong in netvar or in the snapshot table. + nv.ReadDelta(Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), Entries[i].TickWritten); + } + + Entries[i].Fresh = false; + } + } + + /// + /// Read the snapshot index from a buffer + /// Stores the entry. Allocates memory if needed. The actual buffer will be read later + /// + /// The reader to read the index from + internal void ReadIndex(NetworkReader reader) + { + Entry entry; + short entries = reader.ReadInt16(); + + for (var i = 0; i < entries; i++) + { + entry = ReadEntry(reader); + entry.Fresh = true; + + int pos = Find(entry.Key); + if (pos == Entry.NotFound) + { + pos = AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, + entry.Key.VariableIndex); + } + + // if we need to allocate more memory (the variable grew in size) + if (Entries[pos].Length < entry.Length) + { + AllocateEntry(ref entry, entry.Length); + } + + Entries[pos] = entry; + } + } + + /// + /// Helper function to find the NetworkVariable object from a key + /// This will look into all spawned objects + /// + /// The key to search for + private INetworkVariable FindNetworkVar(VariableKey key) + { + var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; + + if (spawnedObjects.ContainsKey(key.NetworkObjectId)) + { + var behaviour = spawnedObjects[key.NetworkObjectId] + .GetNetworkBehaviourAtOrderIndex(key.BehaviourIndex); + return behaviour.NetworkVariableFields[key.VariableIndex]; + } + + return null; + } } public class SnapshotSystem : INetworkUpdateSystem, IDisposable { - private Snapshot m_Snapshot = new Snapshot(); - private Snapshot m_ReceivedSnapshot = new Snapshot(); private NetworkManager m_NetworkManager = NetworkManager.Singleton; + private Snapshot m_Snapshot = new Snapshot(NetworkManager.Singleton); + private Dictionary m_ClientReceivedSnapshot = new Dictionary(); private ushort m_CurrentTick = 0; @@ -171,7 +290,11 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) } DebugDisplayStore(m_Snapshot, "Entries"); - DebugDisplayStore(m_ReceivedSnapshot, "Received Entries"); + + foreach(var item in m_ClientReceivedSnapshot) + { + DebugDisplayStore(item.Value, "Received Entries " + item.Key); + } } } } @@ -213,77 +336,11 @@ private void WriteIndex(NetworkBuffer buffer) writer.WriteInt16((short)m_Snapshot.LastEntry); for (var i = 0; i < m_Snapshot.LastEntry; i++) { - WriteEntry(writer, in m_Snapshot.Entries[i]); + m_Snapshot.WriteEntry(writer, in m_Snapshot.Entries[i]); } } } - /// - /// Read the snapshot index from a buffer - /// Stores the entry. Allocates memory if needed. The actual buffer will be read later - /// - /// The reader to read the index from - private void ReadIndex(NetworkReader reader) - { - Entry entry; - short entries = reader.ReadInt16(); - - for (var i = 0; i < entries; i++) - { - entry = ReadEntry(reader); - entry.Fresh = true; - - int pos = m_ReceivedSnapshot.Find(entry.Key); - if (pos == Entry.NotFound) - { - pos = m_ReceivedSnapshot.AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, - entry.Key.VariableIndex); - } - - // if we need to allocate more memory (the variable grew in size) - if (m_ReceivedSnapshot.Entries[pos].Length < entry.Length) - { - m_ReceivedSnapshot.AllocateEntry(ref entry, entry.Length); - } - - m_ReceivedSnapshot.Entries[pos] = entry; - } - } - - /// - /// Write an Entry to send - /// Must match ReadEntry - /// - /// The writer to write the entry to - private void WriteEntry(NetworkWriter writer, in Entry entry) - { - writer.WriteUInt64(entry.Key.NetworkObjectId); - writer.WriteUInt16(entry.Key.BehaviourIndex); - writer.WriteUInt16(entry.Key.VariableIndex); - writer.WriteUInt16(entry.TickWritten); - writer.WriteUInt16(entry.Position); - writer.WriteUInt16(entry.Length); - } - - /// - /// Read a received Entry - /// Must match WriteEntry - /// - /// The readed to read the entry from - private Entry ReadEntry(NetworkReader reader) - { - Entry entry; - entry.Key.NetworkObjectId = reader.ReadUInt64(); - entry.Key.BehaviourIndex = reader.ReadUInt16(); - entry.Key.VariableIndex = reader.ReadUInt16(); - entry.TickWritten = reader.ReadUInt16(); - entry.Position = reader.ReadUInt16(); - entry.Length = reader.ReadUInt16(); - entry.Fresh = false; - - return entry; - } - /// /// Write the buffer of a snapshot /// Must match ReadBuffer @@ -302,37 +359,6 @@ private void WriteBuffer(NetworkBuffer buffer) buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.FreeMemoryPosition); } - /// - /// Read the buffer part of a snapshot - /// Must match WriteBuffer - /// The stream is actually a memory stream and we seek to each variable position as we deserialize them - /// - /// The NetworkReader to read our buffer of variables from - /// The stream to read our buffer of variables from - private void ReadBuffer(NetworkReader reader, Stream snapshotStream) - { - int snapshotSize = reader.ReadUInt16(); - - snapshotStream.Read(m_ReceivedSnapshot.Buffer, 0, snapshotSize); - - for (var i = 0; i < m_ReceivedSnapshot.LastEntry; i++) - { - if (m_ReceivedSnapshot.Entries[i].Fresh && m_ReceivedSnapshot.Entries[i].TickWritten > 0) - { - var nv = FindNetworkVar(m_ReceivedSnapshot.Entries[i].Key); - - m_ReceivedSnapshot.Stream.Seek(m_ReceivedSnapshot.Entries[i].Position, SeekOrigin.Begin); - - // todo --M1-- - // Review whether tick still belong in netvar or in the snapshot table. - nv.ReadDelta(m_ReceivedSnapshot.Stream, m_NetworkManager.IsServer, - m_NetworkManager.NetworkTickSystem.GetTick(), m_ReceivedSnapshot.Entries[i].TickWritten); - } - - m_ReceivedSnapshot.Entries[i].Fresh = false; - } - } - // todo: consider using a Key, instead of 3 ints, if it can be exposed /// /// Called by the rest of MLAPI when a NetworkVariable changed and need to go in our snapshot @@ -372,35 +398,51 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, /// Entry point when a Snapshot is received /// This is where we read and store the received snapshot /// + /// /// The stream to read from - public void ReadSnapshot(Stream snapshotStream) + public void ReadSnapshot(ulong clientId, Stream snapshotStream) { + ushort snapshotTick = default; + using (var reader = PooledNetworkReader.Get(snapshotStream)) { - ushort snapshotTick = reader.ReadUInt16(); + snapshotTick = reader.ReadUInt16(); - ReadIndex(reader); - ReadBuffer(reader, snapshotStream); + if (!m_ClientReceivedSnapshot.ContainsKey(clientId)) + { + m_ClientReceivedSnapshot[clientId] = new Snapshot(m_NetworkManager); + } + var snapshot = m_ClientReceivedSnapshot[clientId]; + snapshot.ReadIndex(reader); + snapshot.ReadBuffer(reader, snapshotStream); } + + SendAck(clientId, snapshotTick); } - /// - /// Helper function to find the NetworkVariable object from a key - /// This will look into all spawned objects - /// - /// The key to search for - private INetworkVariable FindNetworkVar(VariableKey key) + public void ReadAck(ulong clientId, Stream snapshotStream) { - var spawnedObjects = m_NetworkManager.SpawnManager.SpawnedObjects; + using (var reader = PooledNetworkReader.Get(snapshotStream)) + { + var ackTick = reader.ReadUInt16(); + Debug.Log(string.Format("Receive ack {0} from client {1}", ackTick, clientId)); + } + } - if (spawnedObjects.ContainsKey(key.NetworkObjectId)) + public void SendAck(ulong clientId, ushort tick) + { + using (var buffer = PooledNetworkBuffer.Get()) { - var behaviour = spawnedObjects[key.NetworkObjectId] - .GetNetworkBehaviourAtOrderIndex(key.BehaviourIndex); - return behaviour.NetworkVariableFields[key.VariableIndex]; + using (var writer = PooledNetworkWriter.Get(buffer)) + { + writer.WriteUInt16(tick); + } + + m_NetworkManager.MessageSender.Send(clientId, NetworkConstants.SNAPSHOT_ACK, + NetworkChannel.SnapshotExchange, buffer); + buffer.Dispose(); } - return null; } // todo --M1-- diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index fac353ab96..8afff0086e 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -576,7 +576,12 @@ public void HandleNetworkLog(ulong clientId, Stream stream) internal static void HandleSnapshot(ulong clientId, Stream messageStream) { - NetworkManager.Singleton.SnapshotSystem.ReadSnapshot(messageStream); + NetworkManager.Singleton.SnapshotSystem.ReadSnapshot(clientId, messageStream); + } + + internal static void HandleAck(ulong clientId, Stream messageStream) + { + NetworkManager.Singleton.SnapshotSystem.ReadAck(clientId, messageStream); } public void HandleAllClientsSwitchSceneCompleted(ulong clientId, Stream stream) From a6854681a06d123b767aca633828268e47eb44df Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 13 May 2021 09:29:06 -0400 Subject: [PATCH 34/55] feat: snapshot. Moving the tick into the key. Temporarily comparing tciks modulo 4, until we properly expire old entries --- .../Runtime/Core/SnapshotSystem.cs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index c89637bf03..9de4d71286 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -20,6 +20,7 @@ internal struct VariableKey public ulong NetworkObjectId; // the NetworkObjectId of the owning GameObject public ushort BehaviourIndex; // the index of the behaviour in this GameObject public ushort VariableIndex; // the index of the variable in this NetworkBehaviour + public ushort TickWritten; // the network tick at which this variable was set } // Index for a NetworkVariable in our table of variables @@ -27,7 +28,6 @@ internal struct VariableKey internal struct Entry { public VariableKey Key; - public ushort TickWritten; // the network tick at which this variable was set public ushort Position; // the offset in our Buffer public ushort Length; // the length of the data in Buffer public bool Fresh; // indicates entries that were just received @@ -77,8 +77,9 @@ public int Find(VariableKey key) { if (Entries[i].Key.NetworkObjectId == key.NetworkObjectId && Entries[i].Key.BehaviourIndex == key.BehaviourIndex && - Entries[i].Key.VariableIndex == key.VariableIndex/* && - Entries[i].Key.TickWritten == key.TickWritten*/) + Entries[i].Key.VariableIndex == key.VariableIndex && + // TODO: TEMPORARY + Entries[i].Key.TickWritten % 4 == key.TickWritten % 4) { return i; } @@ -91,15 +92,12 @@ public int Find(VariableKey key) /// Adds an entry in the table for a new key /// // todo: change the params to take a Key instead - public int AddEntry(ulong networkObjectId, int behaviourIndex, int variableIndex) + public int AddEntry(VariableKey k) { var pos = LastEntry++; var entry = Entries[pos]; - entry.Key.NetworkObjectId = networkObjectId; - entry.Key.BehaviourIndex = (ushort)behaviourIndex; - entry.Key.VariableIndex = (ushort)variableIndex; - entry.TickWritten = 0; + entry.Key = k; entry.Position = 0; entry.Length = 0; entry.Fresh = false; @@ -118,7 +116,7 @@ internal void WriteEntry(NetworkWriter writer, in Entry entry) writer.WriteUInt64(entry.Key.NetworkObjectId); writer.WriteUInt16(entry.Key.BehaviourIndex); writer.WriteUInt16(entry.Key.VariableIndex); - writer.WriteUInt16(entry.TickWritten); + writer.WriteUInt16(entry.Key.TickWritten); writer.WriteUInt16(entry.Position); writer.WriteUInt16(entry.Length); } @@ -134,7 +132,7 @@ internal Entry ReadEntry(NetworkReader reader) entry.Key.NetworkObjectId = reader.ReadUInt64(); entry.Key.BehaviourIndex = reader.ReadUInt16(); entry.Key.VariableIndex = reader.ReadUInt16(); - entry.TickWritten = reader.ReadUInt16(); + entry.Key.TickWritten = reader.ReadUInt16(); entry.Position = reader.ReadUInt16(); entry.Length = reader.ReadUInt16(); entry.Fresh = false; @@ -174,7 +172,7 @@ internal void ReadBuffer(NetworkReader reader, Stream snapshotStream) for (var i = 0; i < LastEntry; i++) { - if (Entries[i].Fresh && Entries[i].TickWritten > 0) + if (Entries[i].Fresh && Entries[i].Key.TickWritten > 0) { var nv = FindNetworkVar(Entries[i].Key); @@ -182,7 +180,7 @@ internal void ReadBuffer(NetworkReader reader, Stream snapshotStream) // todo --M1-- // Review whether tick still belong in netvar or in the snapshot table. - nv.ReadDelta(Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), Entries[i].TickWritten); + nv.ReadDelta(Stream, m_NetworkManager.IsServer, m_NetworkManager.NetworkTickSystem.GetTick(), Entries[i].Key.TickWritten); } Entries[i].Fresh = false; @@ -207,8 +205,7 @@ internal void ReadIndex(NetworkReader reader) int pos = Find(entry.Key); if (pos == Entry.NotFound) { - pos = AddEntry(entry.Key.NetworkObjectId, entry.Key.BehaviourIndex, - entry.Key.VariableIndex); + pos = AddEntry(entry.Key); } // if we need to allocate more memory (the variable grew in size) @@ -289,12 +286,12 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) SendSnapshot(m_NetworkManager.ServerClientId); } - DebugDisplayStore(m_Snapshot, "Entries"); + /*DebugDisplayStore(m_Snapshot, "Entries"); foreach(var item in m_ClientReceivedSnapshot) { DebugDisplayStore(item.Value, "Received Entries " + item.Key); - } + }*/ } } } @@ -371,11 +368,12 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, k.NetworkObjectId = networkObjectId; k.BehaviourIndex = (ushort)behaviourIndex; k.VariableIndex = (ushort)variableIndex; + k.TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); int pos = m_Snapshot.Find(k); if (pos == Entry.NotFound) { - pos = m_Snapshot.AddEntry(networkObjectId, behaviourIndex, variableIndex); + pos = m_Snapshot.AddEntry(k); } // write var into buffer, possibly adjusting entry's position and length @@ -388,7 +386,7 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, m_Snapshot.AllocateEntry(ref m_Snapshot.Entries[pos], varBuffer.Length); } - m_Snapshot.Entries[pos].TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); +// m_Snapshot.Entries[pos].Key.TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); // Copy the serialized NetworkVariable into our buffer Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.Buffer, m_Snapshot.Entries[pos].Position, (int)varBuffer.Length); } @@ -454,7 +452,7 @@ private void DebugDisplayStore(Snapshot block, string name) for (int i = 0; i < block.LastEntry; i++) { table += string.Format("NetworkVariable {0}:{1}:{2} written {5}, range [{3}, {4}] ", block.Entries[i].Key.NetworkObjectId, block.Entries[i].Key.BehaviourIndex, - block.Entries[i].Key.VariableIndex, block.Entries[i].Position, block.Entries[i].Position + block.Entries[i].Length, block.Entries[i].TickWritten); + block.Entries[i].Key.VariableIndex, block.Entries[i].Position, block.Entries[i].Position + block.Entries[i].Length, block.Entries[i].Key.TickWritten); for (int j = 0; j < block.Entries[i].Length && j < 4; j++) { From 21b7ae243d36b3b28f23a485837c0e36da180e60 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Mon, 17 May 2021 15:13:03 -0400 Subject: [PATCH 35/55] feat: snapshot. Using a separate IndexAllocator class to manage buffer allocations. Still very WIP --- .../Runtime/Core/SnapshotSystem.cs | 19 +++---- .../Tests/Editor/IndexAllocatorTests.cs | 49 +++++++++++++++++++ .../Tests/Editor/IndexAllocatorTests.cs.meta | 11 +++++ 3 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs create mode 100644 com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 9de4d71286..add48c14f2 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -47,7 +47,7 @@ internal class Snapshot private const int k_BufferSize = 20000; public byte[] Buffer = new byte[k_BufferSize]; - public int FreeMemoryPosition = 0; + internal IndexAllocator m_Allocator = new IndexAllocator(20000); public Entry[] Entries = new Entry[k_MaxVariables]; public int LastEntry = 0; @@ -91,7 +91,6 @@ public int Find(VariableKey key) /// /// Adds an entry in the table for a new key /// - // todo: change the params to take a Key instead public int AddEntry(VariableKey k) { var pos = LastEntry++; @@ -145,16 +144,17 @@ internal Entry ReadEntry(NetworkReader reader) /// /// The entry to allocate for /// The need size in bytes - public void AllocateEntry(ref Entry entry, long size) + public void AllocateEntry(ref Entry entry, int index, int size) { // todo --M1-- // this will change once we start reusing the snapshot buffer memory // todo: deal with free space // todo: deal with full buffer - entry.Position = (ushort)FreeMemoryPosition; + int pos; + m_Allocator.Allocate(index, size, out pos); + entry.Position = (ushort)pos; entry.Length = (ushort)size; - FreeMemoryPosition += (int)size; } /// @@ -211,7 +211,7 @@ internal void ReadIndex(NetworkReader reader) // if we need to allocate more memory (the variable grew in size) if (Entries[pos].Length < entry.Length) { - AllocateEntry(ref entry, entry.Length); + AllocateEntry(ref entry, pos, entry.Length); } Entries[pos] = entry; @@ -286,6 +286,7 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) SendSnapshot(m_NetworkManager.ServerClientId); } + m_Snapshot.m_Allocator.DebugDisplay(); /*DebugDisplayStore(m_Snapshot, "Entries"); foreach(var item in m_ClientReceivedSnapshot) @@ -347,13 +348,13 @@ private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteUInt16((ushort)m_Snapshot.FreeMemoryPosition); + writer.WriteUInt16((ushort)m_Snapshot.m_Allocator.Range); } // todo --M1-- // // this sends the whole buffer // we'll need to build a per-client list - buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.FreeMemoryPosition); + buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.m_Allocator.Range); } // todo: consider using a Key, instead of 3 ints, if it can be exposed @@ -383,7 +384,7 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, if (varBuffer.Length > m_Snapshot.Entries[pos].Length) { // allocate this Entry's buffer - m_Snapshot.AllocateEntry(ref m_Snapshot.Entries[pos], varBuffer.Length); + m_Snapshot.AllocateEntry(ref m_Snapshot.Entries[pos], pos, (int)varBuffer.Length); } // m_Snapshot.Entries[pos].Key.TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs new file mode 100644 index 0000000000..71901eef9e --- /dev/null +++ b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs @@ -0,0 +1,49 @@ +using NUnit.Framework; + +namespace MLAPI.EditorTests +{ + public class FixedAllocatorTest + { + [Test] + public void TestAllocator() + { + int pos; + + IndexAllocator allocator = new IndexAllocator(20000); + allocator.DebugDisplay(); + + // allocate 20 bytes + Assert.IsTrue(allocator.Allocate(0, 20, out pos)); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + // can't ask for the same index twice + Assert.IsFalse(allocator.Allocate(0, 20, out pos)); + Assert.IsTrue(allocator.Verify()); + + // allocate another 20 bytes + Assert.IsTrue(allocator.Allocate(1, 20, out pos)); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + // allocate a third 20 bytes + Assert.IsTrue(allocator.Allocate(2, 20, out pos)); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + Assert.IsTrue(allocator.Deallocate(0)); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + allocator.Deallocate(1); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + allocator.Deallocate(2); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + Assert.IsTrue(true); + } + } +} diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs.meta b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs.meta new file mode 100644 index 0000000000..760a4ccfe5 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85ac488e1432d49668c711fa625a0743 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 4b94ef9d0afdb0728d1500d06b3741d4beb69d7b Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Mon, 17 May 2021 21:55:40 -0400 Subject: [PATCH 36/55] feat: snapshot. IndexAllocator and associated test --- .../Runtime/Core/IndexAllocator.cs | 294 ++++++++++++++++++ .../Runtime/Core/IndexAllocator.cs.meta | 11 + .../Tests/Editor/IndexAllocatorTests.cs | 64 +++- 3 files changed, 367 insertions(+), 2 deletions(-) create mode 100644 com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs create mode 100644 com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs new file mode 100644 index 0000000000..da521c182c --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -0,0 +1,294 @@ +using UnityEngine; + +namespace MLAPI +{ + internal struct IndexAllocatorEntry + { + public int pos; + public int length; + public bool free; + public int next; + public int prev; + } + + internal class IndexAllocator + { + private const int k_MaxSlot = 128; + private const int k_NotSet = -1; + private readonly int m_BufferSize; + + private int FreeMemoryPosition = 0; + + private IndexAllocatorEntry[] m_Slots = new IndexAllocatorEntry[k_MaxSlot]; + private int[] m_IndexToSlot = new int[k_MaxSlot]; + + internal IndexAllocator(int bufferSize) + { + m_BufferSize = bufferSize; + + for (int i = 0; i < k_MaxSlot; i++) + { + m_Slots[i].free = true; + m_Slots[i].next = i + 1; + m_Slots[i].prev = i - 1; + m_Slots[i].pos = m_BufferSize; + m_Slots[i].length = 0; + + m_IndexToSlot[i] = k_NotSet; + } + + m_Slots[0].pos = 0; + m_Slots[0].length = m_BufferSize; + m_Slots[0].prev = k_NotSet; + m_Slots[k_MaxSlot - 1].next = k_NotSet; + } + + + public int Range + { + get { return FreeMemoryPosition; } + } + + internal bool Allocate(int index, int size, out int pos) + { + if (m_IndexToSlot[index] != k_NotSet) + { + pos = 0; + return false; + } + + for (int i = 0; i < k_MaxSlot; i++) + { + if (m_Slots[i].free && m_Slots[i].length >= size) + { + m_IndexToSlot[index] = i; + + int leftOver = m_Slots[i].length - size; + int next = m_Slots[i].next; + if (m_Slots[next].free) + { + m_Slots[next].pos -= leftOver; + m_Slots[next].length += leftOver; + } + else + { + int add = MoveSlotAfter(i); + + m_Slots[add].pos = m_Slots[i].pos + size; + m_Slots[add].length = m_Slots[i].length - size; + } + + m_Slots[i].free = false; + m_Slots[i].length = size; + + break; + } + } + + pos = FreeMemoryPosition; + FreeMemoryPosition += (int) size; + + return true; + } + + // Take a slot at the end and link it to go just after "slot". Used when allocating part of a slot and we need an entry for the rest + // Returns the slot that was picked + private int MoveSlotAfter(int slot) + { + int ret = m_Slots[k_MaxSlot - 1].prev; + int p0 = m_Slots[ret].prev; + + m_Slots[p0].next = k_MaxSlot - 1; + m_Slots[k_MaxSlot - 1].prev = p0; + + int p1 = m_Slots[slot].next; + m_Slots[slot].next = ret; + m_Slots[p1].prev = ret; + + m_Slots[ret].prev = slot; + m_Slots[ret].next = p1; + + return ret; + } + + // Move the slot "slot" to the end of the list. Used when merging two slots, that gives us an extra entry at the end + private void MoveSlotToEnd(int slot) + { + // if we're already there + if (m_Slots[slot].next == k_NotSet) + { + return; + } + + int prev = m_Slots[slot].prev; + int next = m_Slots[slot].next; + + m_Slots[prev].next = next; + if (next != k_NotSet) + { + m_Slots[next].prev = prev; + } + + int p0 = m_Slots[k_MaxSlot - 1].prev; + + m_Slots[p0].next = slot; + m_Slots[slot].next = k_MaxSlot - 1; + + m_Slots[k_MaxSlot - 1].prev = slot; + m_Slots[slot].prev = p0; + + m_Slots[slot].pos = m_BufferSize; + } + + internal bool Deallocate(int index) + { + int slot = m_IndexToSlot[index]; + + if (slot == k_NotSet) + { + return false; + } + + if (m_Slots[slot].free) + { + return false; + } + + m_Slots[slot].free = true; + + int prev = m_Slots[slot].prev; + int next = m_Slots[slot].next; + + // if previous slot was free, merge and grow + if (prev != k_NotSet && m_Slots[prev].free) + { + m_Slots[prev].length += m_Slots[slot].length; + m_Slots[slot].length = 0; + + // todo: verify what this does on full or nearly full cases + MoveSlotToEnd(slot); + slot = prev; + } + + next = m_Slots[slot].next; + + // merge with next slot if it is free + if (next != k_NotSet && m_Slots[next].free) + { + m_Slots[slot].length += m_Slots[next].length; + m_Slots[next].length = 0; + MoveSlotToEnd(next); + } + + // mark the index as available + m_IndexToSlot[index] = k_NotSet; + + return true; + } + + internal bool Verify() + { + int pos = k_NotSet; + int count = 0; + int total = 0; + + do + { + int prev = pos; + if (pos != k_NotSet) + { + pos = m_Slots[pos].next; + if (pos == k_NotSet) + { + break; + } + } + else + { + pos = 0; + } + + if (m_Slots[pos].prev != prev) + { + // the previous is not correct + return false; + } + + if (m_Slots[pos].length < 0) + { + // length should be positive + return false; + } + + if (prev != k_NotSet && m_Slots[prev].free && m_Slots[pos].free && m_Slots[pos].length > 0) + { + // should not have two consecutive free slots + return false; + } + + if (m_Slots[pos].pos != total) + { + // slots should all line up nicely + return false; + } + + total += m_Slots[pos].length; + count++; + + } while (pos != k_NotSet); + + if (count != k_MaxSlot) + { + // some slots were lost + return false; + } + + if (total != m_BufferSize) + { + return false; + } + + return true; + } + + internal void DebugDisplay() + { + string logMessage = "IndexAllocator structure\n"; + + bool[] seen = new bool[k_MaxSlot]; + + + int pos = 0; + int count = 0; + bool prevEmpty = false; + do + { + seen[pos] = true; + count++; + if (m_Slots[pos].length == 0 && prevEmpty) + { + + } + else + { + logMessage += string.Format("{0}:{1}, {2} ({3}) \n", m_Slots[pos].pos, m_Slots[pos].length, + m_Slots[pos].free ? "Free" : "Used", pos); + if (m_Slots[pos].length == 0) + { + prevEmpty = true; + } + else + { + prevEmpty = false; + } + } + + pos = m_Slots[pos].next; + } while (pos != k_NotSet && !seen[pos]); + + logMessage += string.Format("{0} Total entries\n", count); + + Debug.Log(logMessage); + } + } +} diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs.meta b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs.meta new file mode 100644 index 0000000000..b7c7632344 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd9e1475e8c8e4a6d935fe2409e3bd26 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs index 71901eef9e..d2e7b16887 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs @@ -1,11 +1,12 @@ using NUnit.Framework; +using UnityEngine; namespace MLAPI.EditorTests { public class FixedAllocatorTest { [Test] - public void TestAllocator() + public void SimpleTest() { int pos; @@ -31,19 +32,78 @@ public void TestAllocator() allocator.DebugDisplay(); Assert.IsTrue(allocator.Verify()); + // deallocate 0 Assert.IsTrue(allocator.Deallocate(0)); allocator.DebugDisplay(); Assert.IsTrue(allocator.Verify()); + // deallocate 1 allocator.Deallocate(1); allocator.DebugDisplay(); Assert.IsTrue(allocator.Verify()); + // deallocate 2 allocator.Deallocate(2); allocator.DebugDisplay(); Assert.IsTrue(allocator.Verify()); - Assert.IsTrue(true); + // allocate 50 bytes + Assert.IsTrue(allocator.Allocate(0, 50, out pos)); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + // allocate another 50 bytes + Assert.IsTrue(allocator.Allocate(1, 50, out pos)); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + // allocate a third 50 bytes + Assert.IsTrue(allocator.Allocate(2, 50, out pos)); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + // deallocate 1, a block in the middle this time + allocator.Deallocate(1); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + + // allocate a smaller one in its place + allocator.Allocate(1, 25, out pos); + allocator.DebugDisplay(); + Assert.IsTrue(allocator.Verify()); + } + + [Test] + public void ReuseTest() + { + int count = 100; + bool[] used = new bool[count]; + int[] pos = new int[count]; + int bufferSize = 20000; + int iterations = 10000; + + IndexAllocator allocator = new IndexAllocator(20000); + + for (int i = 0; i < iterations; i++) + { + int index = Random.Range(0, count); + if (used[index]) + { + Assert.IsTrue(allocator.Deallocate(index)); + used[index] = false; + } + else + { + int position; + int length = 10 * Random.Range(1, 10); + Assert.IsTrue(allocator.Allocate(index, length, out position)); + pos[index] = position; + used[index] = true; + } + Assert.IsTrue(allocator.Verify()); + } + allocator.DebugDisplay(); } + } } From 28df2cee8ed87d85d84f6c9ad27493ca0968e31c Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 18 May 2021 18:11:47 -0400 Subject: [PATCH 37/55] feat: snapshot. Adjustement on the Snapshot data structure usage. Fixing deallocation before re-allocation --- .../Runtime/Core/IndexAllocator.cs | 3 ++ .../Runtime/Core/SnapshotSystem.cs | 29 ++++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs index da521c182c..2d6f5799b3 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -57,6 +57,9 @@ internal bool Allocate(int index, int size, out int pos) return false; } + // todo: this is the slowest part + // improvement 1: list of free blocks (minor) + // improvement 2: heap of free blocks for (int i = 0; i < k_MaxSlot; i++) { if (m_Slots[i].free && m_Slots[i].length >= size) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index add48c14f2..c13b9ee760 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -54,15 +54,19 @@ internal class Snapshot public MemoryStream Stream; private NetworkManager m_NetworkManager; + private bool m_tickIndex; /// /// Constructor /// Allocated a MemoryStream to be reused for this Snapshot /// - public Snapshot(NetworkManager networkManager) + /// The NetworkManaher this Snapshot uses. Needed upon receive to set Variables + /// Whether this Snapshot uses the tick as an index + public Snapshot(NetworkManager networkManager, bool tickIndex) { Stream = new MemoryStream(Buffer, 0, k_BufferSize); m_NetworkManager = networkManager; + m_tickIndex = tickIndex; } // todo --M1-- @@ -78,8 +82,7 @@ public int Find(VariableKey key) if (Entries[i].Key.NetworkObjectId == key.NetworkObjectId && Entries[i].Key.BehaviourIndex == key.BehaviourIndex && Entries[i].Key.VariableIndex == key.VariableIndex && - // TODO: TEMPORARY - Entries[i].Key.TickWritten % 4 == key.TickWritten % 4) + (!m_tickIndex || (Entries[i].Key.TickWritten == key.TickWritten))) { return i; } @@ -152,7 +155,19 @@ public void AllocateEntry(ref Entry entry, int index, int size) // todo: deal with full buffer int pos; - m_Allocator.Allocate(index, size, out pos); + + if (entry.Length > 0) + { + m_Allocator.Deallocate(index); + } + + bool ret = m_Allocator.Allocate(index, size, out pos); + + if (!ret) + { + //todo: error handling + } + entry.Position = (ushort)pos; entry.Length = (ushort)size; } @@ -241,7 +256,7 @@ private INetworkVariable FindNetworkVar(VariableKey key) public class SnapshotSystem : INetworkUpdateSystem, IDisposable { private NetworkManager m_NetworkManager = NetworkManager.Singleton; - private Snapshot m_Snapshot = new Snapshot(NetworkManager.Singleton); + private Snapshot m_Snapshot = new Snapshot(NetworkManager.Singleton, false); private Dictionary m_ClientReceivedSnapshot = new Dictionary(); private ushort m_CurrentTick = 0; @@ -286,7 +301,7 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) SendSnapshot(m_NetworkManager.ServerClientId); } - m_Snapshot.m_Allocator.DebugDisplay(); + //m_Snapshot.m_Allocator.DebugDisplay(); /*DebugDisplayStore(m_Snapshot, "Entries"); foreach(var item in m_ClientReceivedSnapshot) @@ -409,7 +424,7 @@ public void ReadSnapshot(ulong clientId, Stream snapshotStream) if (!m_ClientReceivedSnapshot.ContainsKey(clientId)) { - m_ClientReceivedSnapshot[clientId] = new Snapshot(m_NetworkManager); + m_ClientReceivedSnapshot[clientId] = new Snapshot(m_NetworkManager, false); } var snapshot = m_ClientReceivedSnapshot[clientId]; snapshot.ReadIndex(reader); From 62240120669e19ad63637be74b14759a8b66032a Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 19 May 2021 15:23:07 -0400 Subject: [PATCH 38/55] feat: snapshot. Adjusting the default scene to use NetworkTransform. Also logging snapshot entries (verbose), to be removed before merge to develop --- .../Runtime/Core/NetworkBehaviour.cs | 1 + com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 4 ++-- testproject/Assets/Prefabs/PlayerCube.prefab | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 7f2d77584f..fcb5be04a3 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -17,6 +17,7 @@ using MLAPI.Spawning; using MLAPI.Transports; using Unity.Profiling; +using Debug = UnityEngine.Debug; namespace MLAPI { diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index c13b9ee760..e695bece3d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -302,12 +302,12 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) } //m_Snapshot.m_Allocator.DebugDisplay(); - /*DebugDisplayStore(m_Snapshot, "Entries"); + DebugDisplayStore(m_Snapshot, "Entries"); foreach(var item in m_ClientReceivedSnapshot) { DebugDisplayStore(item.Value, "Received Entries " + item.Key); - }*/ + } } } } diff --git a/testproject/Assets/Prefabs/PlayerCube.prefab b/testproject/Assets/Prefabs/PlayerCube.prefab index 67062aef36..29b35f9322 100644 --- a/testproject/Assets/Prefabs/PlayerCube.prefab +++ b/testproject/Assets/Prefabs/PlayerCube.prefab @@ -241,7 +241,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8685790303553767886} - m_Enabled: 0 + m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} m_Name: @@ -255,6 +255,8 @@ MonoBehaviour: MinDegrees: 1.5 MinSize: 0.15 Channel: 10 + m_UseLocal: + m_InternalValue: 0 --- !u!114 &8685790303553767876 MonoBehaviour: m_ObjectHideFlags: 0 @@ -301,7 +303,7 @@ MonoBehaviour: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8685790303553767886} - m_Enabled: 1 + m_Enabled: 0 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9548116c10df1486ea12b7329b77c5cf, type: 3} m_Name: From 7eb61564b07fcfd9535e79426d4b1585bc9cacf6 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 20 May 2021 09:34:50 -0400 Subject: [PATCH 39/55] feat: snapshot. IndexAllocator keeping track of its last entry, to allow sending properly sized snapshots --- .../Runtime/Core/IndexAllocator.cs | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs index 2d6f5799b3..0a0d19739b 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -17,7 +17,7 @@ internal class IndexAllocator private const int k_NotSet = -1; private readonly int m_BufferSize; - private int FreeMemoryPosition = 0; + private int m_LastUsed = 0; private IndexAllocatorEntry[] m_Slots = new IndexAllocatorEntry[k_MaxSlot]; private int[] m_IndexToSlot = new int[k_MaxSlot]; @@ -25,7 +25,13 @@ internal class IndexAllocator internal IndexAllocator(int bufferSize) { m_BufferSize = bufferSize; + Reset(); + } + internal void Reset() + { + // todo: could be made faster, for example by having a last index + // and not needing valid stuff past it for (int i = 0; i < k_MaxSlot; i++) { m_Slots[i].free = true; @@ -46,14 +52,21 @@ internal IndexAllocator(int bufferSize) public int Range { - get { return FreeMemoryPosition; } + get { + // when the whole buffer is free, m_LastUsed points to an empty slot + if (m_Slots[m_LastUsed].free) + { + return 0; + } + // otherwise return the end of the last slot used + return m_Slots[m_LastUsed].pos + m_Slots[m_LastUsed].length; } } internal bool Allocate(int index, int size, out int pos) { + pos = 0; if (m_IndexToSlot[index] != k_NotSet) { - pos = 0; return false; } @@ -84,13 +97,18 @@ internal bool Allocate(int index, int size, out int pos) m_Slots[i].free = false; m_Slots[i].length = size; + pos = m_Slots[i].pos; + + // if we allocate past the current range, we are the last slot + if (m_Slots[i].pos + m_Slots[i].length > Range) + { + m_LastUsed = i; + } + break; } } - pos = FreeMemoryPosition; - FreeMemoryPosition += (int) size; - return true; } @@ -168,6 +186,12 @@ internal bool Deallocate(int index) m_Slots[prev].length += m_Slots[slot].length; m_Slots[slot].length = 0; + // if the slot we're merging was the last one, the last one is now the one we merged with + if (slot == m_LastUsed) + { + m_LastUsed = prev; + } + // todo: verify what this does on full or nearly full cases MoveSlotToEnd(slot); slot = prev; @@ -183,6 +207,17 @@ internal bool Deallocate(int index) MoveSlotToEnd(next); } + // if we just deallocate the last one, we need to move last back + if (slot == m_LastUsed) + { + m_LastUsed = m_Slots[m_LastUsed].prev; + // if there's nothing allocated anymore, use 0 + if (m_LastUsed == k_NotSet) + { + m_LastUsed = 0; + } + } + // mark the index as available m_IndexToSlot[index] = k_NotSet; @@ -194,6 +229,7 @@ internal bool Verify() int pos = k_NotSet; int count = 0; int total = 0; + int endPos = 0; do { @@ -235,6 +271,11 @@ internal bool Verify() return false; } + if (!m_Slots[pos].free) + { + endPos = m_Slots[pos].pos + m_Slots[pos].length; + } + total += m_Slots[pos].length; count++; @@ -251,6 +292,12 @@ internal bool Verify() return false; } + if (endPos != Range) + { + Debug.Log(string.Format("{0} range versue {1} end position", Range, endPos)); + return false; + } + return true; } From c3f43031974e589464d283a0e116e7ddca4e5916 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 20 May 2021 17:34:09 -0400 Subject: [PATCH 40/55] feat: snapshot. Partial improvement. Now works on SceneTransitioningTest, with 5 cubes/s, for a little while --- .../Runtime/Core/IndexAllocator.cs | 33 ++++++++++--------- .../Runtime/Core/SnapshotSystem.cs | 20 ++++++++--- .../Runtime/Transports/NetworkTransport.cs | 2 +- .../Tests/Editor/IndexAllocatorTests.cs | 4 +-- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs index 0a0d19739b..6853557b97 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -13,18 +13,21 @@ internal struct IndexAllocatorEntry internal class IndexAllocator { - private const int k_MaxSlot = 128; + private int m_MaxSlot; private const int k_NotSet = -1; private readonly int m_BufferSize; private int m_LastUsed = 0; - private IndexAllocatorEntry[] m_Slots = new IndexAllocatorEntry[k_MaxSlot]; - private int[] m_IndexToSlot = new int[k_MaxSlot]; + private IndexAllocatorEntry[] m_Slots; + private int[] m_IndexToSlot; - internal IndexAllocator(int bufferSize) + internal IndexAllocator(int bufferSize, int maxSlot) { + m_MaxSlot = maxSlot; m_BufferSize = bufferSize; + m_Slots = new IndexAllocatorEntry[m_MaxSlot]; + m_IndexToSlot = new int[m_MaxSlot]; Reset(); } @@ -32,7 +35,7 @@ internal void Reset() { // todo: could be made faster, for example by having a last index // and not needing valid stuff past it - for (int i = 0; i < k_MaxSlot; i++) + for (int i = 0; i < m_MaxSlot; i++) { m_Slots[i].free = true; m_Slots[i].next = i + 1; @@ -46,7 +49,7 @@ internal void Reset() m_Slots[0].pos = 0; m_Slots[0].length = m_BufferSize; m_Slots[0].prev = k_NotSet; - m_Slots[k_MaxSlot - 1].next = k_NotSet; + m_Slots[m_MaxSlot - 1].next = k_NotSet; } @@ -73,7 +76,7 @@ internal bool Allocate(int index, int size, out int pos) // todo: this is the slowest part // improvement 1: list of free blocks (minor) // improvement 2: heap of free blocks - for (int i = 0; i < k_MaxSlot; i++) + for (int i = 0; i < m_MaxSlot; i++) { if (m_Slots[i].free && m_Slots[i].length >= size) { @@ -116,11 +119,11 @@ internal bool Allocate(int index, int size, out int pos) // Returns the slot that was picked private int MoveSlotAfter(int slot) { - int ret = m_Slots[k_MaxSlot - 1].prev; + int ret = m_Slots[m_MaxSlot - 1].prev; int p0 = m_Slots[ret].prev; - m_Slots[p0].next = k_MaxSlot - 1; - m_Slots[k_MaxSlot - 1].prev = p0; + m_Slots[p0].next = m_MaxSlot - 1; + m_Slots[m_MaxSlot - 1].prev = p0; int p1 = m_Slots[slot].next; m_Slots[slot].next = ret; @@ -150,12 +153,12 @@ private void MoveSlotToEnd(int slot) m_Slots[next].prev = prev; } - int p0 = m_Slots[k_MaxSlot - 1].prev; + int p0 = m_Slots[m_MaxSlot - 1].prev; m_Slots[p0].next = slot; - m_Slots[slot].next = k_MaxSlot - 1; + m_Slots[slot].next = m_MaxSlot - 1; - m_Slots[k_MaxSlot - 1].prev = slot; + m_Slots[m_MaxSlot - 1].prev = slot; m_Slots[slot].prev = p0; m_Slots[slot].pos = m_BufferSize; @@ -281,7 +284,7 @@ internal bool Verify() } while (pos != k_NotSet); - if (count != k_MaxSlot) + if (count != m_MaxSlot) { // some slots were lost return false; @@ -305,7 +308,7 @@ internal void DebugDisplay() { string logMessage = "IndexAllocator structure\n"; - bool[] seen = new bool[k_MaxSlot]; + bool[] seen = new bool[m_MaxSlot]; int pos = 0; diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index e695bece3d..47bea9c9c8 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -43,11 +43,11 @@ internal struct Entry internal class Snapshot { // todo --M1-- functionality to grow these will be needed in a later milestone - private const int k_MaxVariables = 64; - private const int k_BufferSize = 20000; + private const int k_MaxVariables = 600; + private const int k_BufferSize = 10000; public byte[] Buffer = new byte[k_BufferSize]; - internal IndexAllocator m_Allocator = new IndexAllocator(20000); + internal IndexAllocator m_Allocator; public Entry[] Entries = new Entry[k_MaxVariables]; public int LastEntry = 0; @@ -65,10 +65,18 @@ internal class Snapshot public Snapshot(NetworkManager networkManager, bool tickIndex) { Stream = new MemoryStream(Buffer, 0, k_BufferSize); + // we ask for twice as many slots because there could end up being one free spot between each pair of slot used + m_Allocator = new IndexAllocator(k_BufferSize, k_MaxVariables * 2); m_NetworkManager = networkManager; m_tickIndex = tickIndex; } + public void Clear() + { + LastEntry = 0; + m_Allocator.Reset(); + } + // todo --M1-- // Find will change to be efficient in a future milestone /// @@ -302,12 +310,16 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) } //m_Snapshot.m_Allocator.DebugDisplay(); - DebugDisplayStore(m_Snapshot, "Entries"); +/* DebugDisplayStore(m_Snapshot, "Entries"); foreach(var item in m_ClientReceivedSnapshot) { DebugDisplayStore(item.Value, "Received Entries " + item.Key); } +*/ + // todo: --M1b-- + // for now we clear our send snapshot because we don't have per-client partial sends + m_Snapshot.Clear(); } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs b/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs index 53685f95e6..b7cf3c4a00 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Transports/NetworkTransport.cs @@ -104,7 +104,7 @@ public TransportChannel[] MLAPI_CHANNELS // todo: Currently, fragmentation support needed to deal with oversize packets encounterable with current pre-snapshot code". // todo: once we have snapshotting able to deal with missing frame, this should be unreliable new TransportChannel(NetworkChannel.NetworkVariable, NetworkDelivery.ReliableFragmentedSequenced), - new TransportChannel(NetworkChannel.SnapshotExchange, NetworkDelivery.Unreliable), + new TransportChannel(NetworkChannel.SnapshotExchange, NetworkDelivery.ReliableFragmentedSequenced), // todo: temporary until we separate snapshots in chunks }; /// diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs index d2e7b16887..5c5b6f9f68 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs @@ -10,7 +10,7 @@ public void SimpleTest() { int pos; - IndexAllocator allocator = new IndexAllocator(20000); + IndexAllocator allocator = new IndexAllocator(20000, 200); allocator.DebugDisplay(); // allocate 20 bytes @@ -82,7 +82,7 @@ public void ReuseTest() int bufferSize = 20000; int iterations = 10000; - IndexAllocator allocator = new IndexAllocator(20000); + IndexAllocator allocator = new IndexAllocator(20000, 200); for (int i = 0; i < iterations; i++) { From 171b5ebfb12c4a7249171316370ba5aac1915c12 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 20 May 2021 18:13:55 -0400 Subject: [PATCH 41/55] feat: snapshot. Running standards.py --fix --- com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs | 6 ++++-- com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 6 +++--- .../Tests/Editor/IndexAllocatorTests.cs | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs index 6853557b97..8096a6dff9 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -55,14 +55,16 @@ internal void Reset() public int Range { - get { + get + { // when the whole buffer is free, m_LastUsed points to an empty slot if (m_Slots[m_LastUsed].free) { return 0; } // otherwise return the end of the last slot used - return m_Slots[m_LastUsed].pos + m_Slots[m_LastUsed].length; } + return m_Slots[m_LastUsed].pos + m_Slots[m_LastUsed].length; + } } internal bool Allocate(int index, int size, out int pos) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 47bea9c9c8..fe3e26733f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -310,13 +310,14 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) } //m_Snapshot.m_Allocator.DebugDisplay(); -/* DebugDisplayStore(m_Snapshot, "Entries"); + /* + DebugDisplayStore(m_Snapshot, "Entries"); foreach(var item in m_ClientReceivedSnapshot) { DebugDisplayStore(item.Value, "Received Entries " + item.Key); } -*/ + */ // todo: --M1b-- // for now we clear our send snapshot because we don't have per-client partial sends m_Snapshot.Clear(); @@ -414,7 +415,6 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, m_Snapshot.AllocateEntry(ref m_Snapshot.Entries[pos], pos, (int)varBuffer.Length); } -// m_Snapshot.Entries[pos].Key.TickWritten = m_NetworkManager.NetworkTickSystem.GetTick(); // Copy the serialized NetworkVariable into our buffer Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.Buffer, m_Snapshot.Entries[pos].Position, (int)varBuffer.Length); } diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs index 5c5b6f9f68..b3ca178f1f 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs @@ -10,7 +10,7 @@ public void SimpleTest() { int pos; - IndexAllocator allocator = new IndexAllocator(20000, 200); + var allocator = new IndexAllocator(20000, 200); allocator.DebugDisplay(); // allocate 20 bytes @@ -82,7 +82,7 @@ public void ReuseTest() int bufferSize = 20000; int iterations = 10000; - IndexAllocator allocator = new IndexAllocator(20000, 200); + var allocator = new IndexAllocator(20000, 200); for (int i = 0; i < iterations; i++) { From eb639f7a17df561beb19e5a54d1b057153540013 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Fri, 21 May 2021 12:33:58 -0400 Subject: [PATCH 42/55] refactor: snapshot, indexAllocator, organising for readability --- .../Runtime/Core/IndexAllocator.cs | 136 +++++++++--------- 1 file changed, 67 insertions(+), 69 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs index 8096a6dff9..c2935085d2 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -4,21 +4,19 @@ namespace MLAPI { internal struct IndexAllocatorEntry { - public int pos; - public int length; - public bool free; - public int next; - public int prev; + internal int pos; + internal int length; + internal bool free; + internal int next; + internal int prev; } internal class IndexAllocator { - private int m_MaxSlot; private const int k_NotSet = -1; + private readonly int m_MaxSlot; private readonly int m_BufferSize; - - private int m_LastUsed = 0; - + private int m_LastSlot = 0; private IndexAllocatorEntry[] m_Slots; private int[] m_IndexToSlot; @@ -53,17 +51,17 @@ internal void Reset() } - public int Range + internal int Range { get { - // when the whole buffer is free, m_LastUsed points to an empty slot - if (m_Slots[m_LastUsed].free) + // when the whole buffer is free, m_LastSlot points to an empty slot + if (m_Slots[m_LastSlot].free) { return 0; } // otherwise return the end of the last slot used - return m_Slots[m_LastUsed].pos + m_Slots[m_LastUsed].length; + return m_Slots[m_LastSlot].pos + m_Slots[m_LastSlot].length; } } @@ -107,7 +105,7 @@ internal bool Allocate(int index, int size, out int pos) // if we allocate past the current range, we are the last slot if (m_Slots[i].pos + m_Slots[i].length > Range) { - m_LastUsed = i; + m_LastSlot = i; } break; @@ -117,55 +115,6 @@ internal bool Allocate(int index, int size, out int pos) return true; } - // Take a slot at the end and link it to go just after "slot". Used when allocating part of a slot and we need an entry for the rest - // Returns the slot that was picked - private int MoveSlotAfter(int slot) - { - int ret = m_Slots[m_MaxSlot - 1].prev; - int p0 = m_Slots[ret].prev; - - m_Slots[p0].next = m_MaxSlot - 1; - m_Slots[m_MaxSlot - 1].prev = p0; - - int p1 = m_Slots[slot].next; - m_Slots[slot].next = ret; - m_Slots[p1].prev = ret; - - m_Slots[ret].prev = slot; - m_Slots[ret].next = p1; - - return ret; - } - - // Move the slot "slot" to the end of the list. Used when merging two slots, that gives us an extra entry at the end - private void MoveSlotToEnd(int slot) - { - // if we're already there - if (m_Slots[slot].next == k_NotSet) - { - return; - } - - int prev = m_Slots[slot].prev; - int next = m_Slots[slot].next; - - m_Slots[prev].next = next; - if (next != k_NotSet) - { - m_Slots[next].prev = prev; - } - - int p0 = m_Slots[m_MaxSlot - 1].prev; - - m_Slots[p0].next = slot; - m_Slots[slot].next = m_MaxSlot - 1; - - m_Slots[m_MaxSlot - 1].prev = slot; - m_Slots[slot].prev = p0; - - m_Slots[slot].pos = m_BufferSize; - } - internal bool Deallocate(int index) { int slot = m_IndexToSlot[index]; @@ -192,9 +141,9 @@ internal bool Deallocate(int index) m_Slots[slot].length = 0; // if the slot we're merging was the last one, the last one is now the one we merged with - if (slot == m_LastUsed) + if (slot == m_LastSlot) { - m_LastUsed = prev; + m_LastSlot = prev; } // todo: verify what this does on full or nearly full cases @@ -213,13 +162,13 @@ internal bool Deallocate(int index) } // if we just deallocate the last one, we need to move last back - if (slot == m_LastUsed) + if (slot == m_LastSlot) { - m_LastUsed = m_Slots[m_LastUsed].prev; + m_LastSlot = m_Slots[m_LastSlot].prev; // if there's nothing allocated anymore, use 0 - if (m_LastUsed == k_NotSet) + if (m_LastSlot == k_NotSet) { - m_LastUsed = 0; + m_LastSlot = 0; } } @@ -229,6 +178,55 @@ internal bool Deallocate(int index) return true; } + // Take a slot at the end and link it to go just after "slot". Used when allocating part of a slot and we need an entry for the rest + // Returns the slot that was picked + private int MoveSlotAfter(int slot) + { + int ret = m_Slots[m_MaxSlot - 1].prev; + int p0 = m_Slots[ret].prev; + + m_Slots[p0].next = m_MaxSlot - 1; + m_Slots[m_MaxSlot - 1].prev = p0; + + int p1 = m_Slots[slot].next; + m_Slots[slot].next = ret; + m_Slots[p1].prev = ret; + + m_Slots[ret].prev = slot; + m_Slots[ret].next = p1; + + return ret; + } + + // Move the slot "slot" to the end of the list. Used when merging two slots, that gives us an extra entry at the end + private void MoveSlotToEnd(int slot) + { + // if we're already there + if (m_Slots[slot].next == k_NotSet) + { + return; + } + + int prev = m_Slots[slot].prev; + int next = m_Slots[slot].next; + + m_Slots[prev].next = next; + if (next != k_NotSet) + { + m_Slots[next].prev = prev; + } + + int p0 = m_Slots[m_MaxSlot - 1].prev; + + m_Slots[p0].next = slot; + m_Slots[slot].next = m_MaxSlot - 1; + + m_Slots[m_MaxSlot - 1].prev = slot; + m_Slots[slot].prev = p0; + + m_Slots[slot].pos = m_BufferSize; + } + internal bool Verify() { int pos = k_NotSet; From 3a0af249b7274cfc34cb7c97ef68c22d32b685e3 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Fri, 21 May 2021 15:33:07 -0400 Subject: [PATCH 43/55] fix: snapshot. Removing double dispose() of PooledNetworkBuffer --- com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 15a57c0158..2f1df8144e 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -352,7 +352,6 @@ private void SendSnapshot(ulong clientId) m_NetworkManager.MessageSender.Send(clientId, NetworkConstants.SNAPSHOT_DATA, NetworkChannel.SnapshotExchange, buffer); - buffer.Dispose(); } } From 119b3d53a099df7c5c5600ac01d85136dcc64f60 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Fri, 21 May 2021 17:13:14 -0400 Subject: [PATCH 44/55] refactor: Clean-up IndexAllocator for readability and documentation --- .../Runtime/Core/IndexAllocator.cs | 202 ++++++++++-------- .../Runtime/Core/SnapshotSystem.cs | 26 +-- 2 files changed, 128 insertions(+), 100 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs index c2935085d2..c33f7be74c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -4,21 +4,21 @@ namespace MLAPI { internal struct IndexAllocatorEntry { - internal int pos; - internal int length; - internal bool free; - internal int next; - internal int prev; + internal int Pos; // Position where the memory of this slot is + internal int Length; // Length of the memory allocated to this slot + internal bool Free; // Whether this is a free slot + internal int Next; // Next and Prev define the order of the slots in the buffer + internal int Prev; } internal class IndexAllocator { private const int k_NotSet = -1; - private readonly int m_MaxSlot; - private readonly int m_BufferSize; - private int m_LastSlot = 0; - private IndexAllocatorEntry[] m_Slots; - private int[] m_IndexToSlot; + private readonly int m_MaxSlot; // Maximum number of sections (free or not) in the buffer + private readonly int m_BufferSize; // Size of the buffer we allocated into + private int m_LastSlot = 0; // Last allocated slot + private IndexAllocatorEntry[] m_Slots; // Array of slots + private int[] m_IndexToSlot; // Mapping from the client's index to the slot index internal IndexAllocator(int bufferSize, int maxSlot) { @@ -29,42 +29,59 @@ internal IndexAllocator(int bufferSize, int maxSlot) Reset(); } + /// + /// Reset this IndexAllocator to an empty one, with the same sized buffer and slots + /// internal void Reset() { // todo: could be made faster, for example by having a last index // and not needing valid stuff past it for (int i = 0; i < m_MaxSlot; i++) { - m_Slots[i].free = true; - m_Slots[i].next = i + 1; - m_Slots[i].prev = i - 1; - m_Slots[i].pos = m_BufferSize; - m_Slots[i].length = 0; + m_Slots[i].Free = true; + m_Slots[i].Next = i + 1; + m_Slots[i].Prev = i - 1; + m_Slots[i].Pos = m_BufferSize; + m_Slots[i].Length = 0; m_IndexToSlot[i] = k_NotSet; } - m_Slots[0].pos = 0; - m_Slots[0].length = m_BufferSize; - m_Slots[0].prev = k_NotSet; - m_Slots[m_MaxSlot - 1].next = k_NotSet; + m_Slots[0].Pos = 0; + m_Slots[0].Length = m_BufferSize; + m_Slots[0].Prev = k_NotSet; + m_Slots[m_MaxSlot - 1].Next = k_NotSet; } - + /// + /// Returns the amount of memory used + /// + /// + /// Returns the amount of memory used, starting at 0, ending after the last used slot + /// internal int Range { get { // when the whole buffer is free, m_LastSlot points to an empty slot - if (m_Slots[m_LastSlot].free) + if (m_Slots[m_LastSlot].Free) { return 0; } // otherwise return the end of the last slot used - return m_Slots[m_LastSlot].pos + m_Slots[m_LastSlot].length; + return m_Slots[m_LastSlot].Pos + m_Slots[m_LastSlot].Length; } } + /// + /// Allocate a slot with "size" position, for index "index" + /// + /// The client index to identify this. Used in Deallocate to identify which slot + /// The size required. + /// Returns the position to use in the buffer + /// + /// true if successful, false is there isn't enough memory available or no slots are large enough + /// internal bool Allocate(int index, int size, out int pos) { pos = 0; @@ -78,43 +95,50 @@ internal bool Allocate(int index, int size, out int pos) // improvement 2: heap of free blocks for (int i = 0; i < m_MaxSlot; i++) { - if (m_Slots[i].free && m_Slots[i].length >= size) + if (m_Slots[i].Free && m_Slots[i].Length >= size) { m_IndexToSlot[index] = i; - int leftOver = m_Slots[i].length - size; - int next = m_Slots[i].next; - if (m_Slots[next].free) + int leftOver = m_Slots[i].Length - size; + int next = m_Slots[i].Next; + if (m_Slots[next].Free) { - m_Slots[next].pos -= leftOver; - m_Slots[next].length += leftOver; + m_Slots[next].Pos -= leftOver; + m_Slots[next].Length += leftOver; } else { int add = MoveSlotAfter(i); - m_Slots[add].pos = m_Slots[i].pos + size; - m_Slots[add].length = m_Slots[i].length - size; + m_Slots[add].Pos = m_Slots[i].Pos + size; + m_Slots[add].Length = m_Slots[i].Length - size; } - m_Slots[i].free = false; - m_Slots[i].length = size; + m_Slots[i].Free = false; + m_Slots[i].Length = size; - pos = m_Slots[i].pos; + pos = m_Slots[i].Pos; // if we allocate past the current range, we are the last slot - if (m_Slots[i].pos + m_Slots[i].length > Range) + if (m_Slots[i].Pos + m_Slots[i].Length > Range) { m_LastSlot = i; } - break; + return true; } } - return true; + return false; } + /// + /// Deallocate a slot + /// + /// The client index to identify this. Same index used in Allocate + /// + /// true if successful, false is there isn't an allocated slot at this index + /// internal bool Deallocate(int index) { int slot = m_IndexToSlot[index]; @@ -124,21 +148,21 @@ internal bool Deallocate(int index) return false; } - if (m_Slots[slot].free) + if (m_Slots[slot].Free) { return false; } - m_Slots[slot].free = true; + m_Slots[slot].Free = true; - int prev = m_Slots[slot].prev; - int next = m_Slots[slot].next; + int prev = m_Slots[slot].Prev; + int next = m_Slots[slot].Next; // if previous slot was free, merge and grow - if (prev != k_NotSet && m_Slots[prev].free) + if (prev != k_NotSet && m_Slots[prev].Free) { - m_Slots[prev].length += m_Slots[slot].length; - m_Slots[slot].length = 0; + m_Slots[prev].Length += m_Slots[slot].Length; + m_Slots[slot].Length = 0; // if the slot we're merging was the last one, the last one is now the one we merged with if (slot == m_LastSlot) @@ -151,20 +175,20 @@ internal bool Deallocate(int index) slot = prev; } - next = m_Slots[slot].next; + next = m_Slots[slot].Next; // merge with next slot if it is free - if (next != k_NotSet && m_Slots[next].free) + if (next != k_NotSet && m_Slots[next].Free) { - m_Slots[slot].length += m_Slots[next].length; - m_Slots[next].length = 0; + m_Slots[slot].Length += m_Slots[next].Length; + m_Slots[next].Length = 0; MoveSlotToEnd(next); } // if we just deallocate the last one, we need to move last back if (slot == m_LastSlot) { - m_LastSlot = m_Slots[m_LastSlot].prev; + m_LastSlot = m_Slots[m_LastSlot].Prev; // if there's nothing allocated anymore, use 0 if (m_LastSlot == k_NotSet) { @@ -178,55 +202,58 @@ internal bool Deallocate(int index) return true; } - // Take a slot at the end and link it to go just after "slot". Used when allocating part of a slot and we need an entry for the rest + // Take a slot at the end and link it to go just after "slot". + // Used when allocating part of a slot and we need an entry for the rest // Returns the slot that was picked private int MoveSlotAfter(int slot) { - int ret = m_Slots[m_MaxSlot - 1].prev; - int p0 = m_Slots[ret].prev; + int ret = m_Slots[m_MaxSlot - 1].Prev; + int p0 = m_Slots[ret].Prev; - m_Slots[p0].next = m_MaxSlot - 1; - m_Slots[m_MaxSlot - 1].prev = p0; + m_Slots[p0].Next = m_MaxSlot - 1; + m_Slots[m_MaxSlot - 1].Prev = p0; - int p1 = m_Slots[slot].next; - m_Slots[slot].next = ret; - m_Slots[p1].prev = ret; + int p1 = m_Slots[slot].Next; + m_Slots[slot].Next = ret; + m_Slots[p1].Prev = ret; - m_Slots[ret].prev = slot; - m_Slots[ret].next = p1; + m_Slots[ret].Prev = slot; + m_Slots[ret].Next = p1; return ret; } - // Move the slot "slot" to the end of the list. Used when merging two slots, that gives us an extra entry at the end + // Move the slot "slot" to the end of the list. + // Used when merging two slots, that gives us an extra entry at the end private void MoveSlotToEnd(int slot) { // if we're already there - if (m_Slots[slot].next == k_NotSet) + if (m_Slots[slot].Next == k_NotSet) { return; } - int prev = m_Slots[slot].prev; - int next = m_Slots[slot].next; + int prev = m_Slots[slot].Prev; + int next = m_Slots[slot].Next; - m_Slots[prev].next = next; + m_Slots[prev].Next = next; if (next != k_NotSet) { - m_Slots[next].prev = prev; + m_Slots[next].Prev = prev; } - int p0 = m_Slots[m_MaxSlot - 1].prev; + int p0 = m_Slots[m_MaxSlot - 1].Prev; - m_Slots[p0].next = slot; - m_Slots[slot].next = m_MaxSlot - 1; + m_Slots[p0].Next = slot; + m_Slots[slot].Next = m_MaxSlot - 1; - m_Slots[m_MaxSlot - 1].prev = slot; - m_Slots[slot].prev = p0; + m_Slots[m_MaxSlot - 1].Prev = slot; + m_Slots[slot].Prev = p0; - m_Slots[slot].pos = m_BufferSize; + m_Slots[slot].Pos = m_BufferSize; } + // runs a bunch of consistency check on the Allocator internal bool Verify() { int pos = k_NotSet; @@ -239,7 +266,7 @@ internal bool Verify() int prev = pos; if (pos != k_NotSet) { - pos = m_Slots[pos].next; + pos = m_Slots[pos].Next; if (pos == k_NotSet) { break; @@ -250,36 +277,36 @@ internal bool Verify() pos = 0; } - if (m_Slots[pos].prev != prev) + if (m_Slots[pos].Prev != prev) { // the previous is not correct return false; } - if (m_Slots[pos].length < 0) + if (m_Slots[pos].Length < 0) { - // length should be positive + // Length should be positive return false; } - if (prev != k_NotSet && m_Slots[prev].free && m_Slots[pos].free && m_Slots[pos].length > 0) + if (prev != k_NotSet && m_Slots[prev].Free && m_Slots[pos].Free && m_Slots[pos].Length > 0) { // should not have two consecutive free slots return false; } - if (m_Slots[pos].pos != total) + if (m_Slots[pos].Pos != total) { // slots should all line up nicely return false; } - if (!m_Slots[pos].free) + if (!m_Slots[pos].Free) { - endPos = m_Slots[pos].pos + m_Slots[pos].length; + endPos = m_Slots[pos].Pos + m_Slots[pos].Length; } - total += m_Slots[pos].length; + total += m_Slots[pos].Length; count++; } while (pos != k_NotSet); @@ -292,25 +319,26 @@ internal bool Verify() if (total != m_BufferSize) { + // total buffer should be accounted for return false; } if (endPos != Range) { - Debug.Log(string.Format("{0} range versue {1} end position", Range, endPos)); + // end position should match reported end position return false; } return true; } + // Debug display the allocator structure internal void DebugDisplay() { string logMessage = "IndexAllocator structure\n"; bool[] seen = new bool[m_MaxSlot]; - int pos = 0; int count = 0; bool prevEmpty = false; @@ -318,15 +346,15 @@ internal void DebugDisplay() { seen[pos] = true; count++; - if (m_Slots[pos].length == 0 && prevEmpty) + if (m_Slots[pos].Length == 0 && prevEmpty) { - + // don't display repetitive empty slots } else { - logMessage += string.Format("{0}:{1}, {2} ({3}) \n", m_Slots[pos].pos, m_Slots[pos].length, - m_Slots[pos].free ? "Free" : "Used", pos); - if (m_Slots[pos].length == 0) + logMessage += string.Format("{0}:{1}, {2} ({3}) \n", m_Slots[pos].Pos, m_Slots[pos].Length, + m_Slots[pos].Free ? "Free" : "Used", pos); + if (m_Slots[pos].Length == 0) { prevEmpty = true; } @@ -336,7 +364,7 @@ internal void DebugDisplay() } } - pos = m_Slots[pos].next; + pos = m_Slots[pos].Next; } while (pos != k_NotSet && !seen[pos]); logMessage += string.Format("{0} Total entries\n", count); diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 2f1df8144e..dee0001543 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -29,7 +29,7 @@ internal struct Entry { public VariableKey Key; public ushort Position; // the offset in our Buffer - public ushort Length; // the length of the data in Buffer + public ushort Length; // the Length of the data in Buffer public bool Fresh; // indicates entries that were just received public const int NotFound = -1; @@ -47,14 +47,14 @@ internal class Snapshot private const int k_BufferSize = 10000; public byte[] Buffer = new byte[k_BufferSize]; - internal IndexAllocator m_Allocator; + internal IndexAllocator Allocator; public Entry[] Entries = new Entry[k_MaxVariables]; public int LastEntry = 0; public MemoryStream Stream; private NetworkManager m_NetworkManager; - private bool m_tickIndex; + private bool m_TickIndex; /// /// Constructor @@ -66,15 +66,15 @@ public Snapshot(NetworkManager networkManager, bool tickIndex) { Stream = new MemoryStream(Buffer, 0, k_BufferSize); // we ask for twice as many slots because there could end up being one free spot between each pair of slot used - m_Allocator = new IndexAllocator(k_BufferSize, k_MaxVariables * 2); + Allocator = new IndexAllocator(k_BufferSize, k_MaxVariables * 2); m_NetworkManager = networkManager; - m_tickIndex = tickIndex; + m_TickIndex = tickIndex; } public void Clear() { LastEntry = 0; - m_Allocator.Reset(); + Allocator.Reset(); } // todo --M1-- @@ -90,7 +90,7 @@ public int Find(VariableKey key) if (Entries[i].Key.NetworkObjectId == key.NetworkObjectId && Entries[i].Key.BehaviourIndex == key.BehaviourIndex && Entries[i].Key.VariableIndex == key.VariableIndex && - (!m_tickIndex || (Entries[i].Key.TickWritten == key.TickWritten))) + (!m_TickIndex || (Entries[i].Key.TickWritten == key.TickWritten))) { return i; } @@ -166,10 +166,10 @@ public void AllocateEntry(ref Entry entry, int index, int size) if (entry.Length > 0) { - m_Allocator.Deallocate(index); + Allocator.Deallocate(index); } - bool ret = m_Allocator.Allocate(index, size, out pos); + bool ret = Allocator.Allocate(index, size, out pos); if (!ret) { @@ -314,7 +314,7 @@ public void NetworkUpdate(NetworkUpdateStage updateStage) SendSnapshot(m_NetworkManager.ServerClientId); } - //m_Snapshot.m_Allocator.DebugDisplay(); + //m_Snapshot.Allocator.DebugDisplay(); /* DebugDisplayStore(m_Snapshot, "Entries"); @@ -380,13 +380,13 @@ private void WriteBuffer(NetworkBuffer buffer) { using (var writer = PooledNetworkWriter.Get(buffer)) { - writer.WriteUInt16((ushort)m_Snapshot.m_Allocator.Range); + writer.WriteUInt16((ushort)m_Snapshot.Allocator.Range); } // todo --M1-- // // this sends the whole buffer // we'll need to build a per-client list - buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.m_Allocator.Range); + buffer.Write(m_Snapshot.Buffer, 0, m_Snapshot.Allocator.Range); } // todo: consider using a Key, instead of 3 ints, if it can be exposed @@ -409,7 +409,7 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, pos = m_Snapshot.AddEntry(k); } - // write var into buffer, possibly adjusting entry's position and length + // write var into buffer, possibly adjusting entry's position and Length using (var varBuffer = PooledNetworkBuffer.Get()) { networkVariable.WriteDelta(varBuffer); From 84155476dfc4f5ee74ea49bdedbd4f14e55d7ea6 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 25 May 2021 08:36:24 -0400 Subject: [PATCH 45/55] feat: snapshot. Clearing receive snapshot before receiving, for now --- .../Runtime/Core/SnapshotSystem.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index dee0001543..6515d9eb2f 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -43,8 +43,8 @@ internal struct Entry internal class Snapshot { // todo --M1-- functionality to grow these will be needed in a later milestone - private const int k_MaxVariables = 600; - private const int k_BufferSize = 10000; + private const int k_MaxVariables = 2000; + private const int k_BufferSize = 30000; public byte[] Buffer = new byte[k_BufferSize]; internal IndexAllocator Allocator; @@ -443,6 +443,10 @@ public void ReadSnapshot(ulong clientId, Stream snapshotStream) m_ClientReceivedSnapshot[clientId] = new Snapshot(m_NetworkManager, false); } var snapshot = m_ClientReceivedSnapshot[clientId]; + + // todo --M1b-- temporary, clear before receive. + snapshot.Clear(); + snapshot.ReadIndex(reader); snapshot.ReadBuffer(reader, snapshotStream); } From 8d230c4bdbf1202a62e40609824b5fe8ddb2b91a Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Tue, 25 May 2021 08:42:49 -0400 Subject: [PATCH 46/55] feat: snapshot. Reducing the verbosity of logging --- com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 6515d9eb2f..80273cf2ca 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -459,7 +459,7 @@ public void ReadAck(ulong clientId, Stream snapshotStream) using (var reader = PooledNetworkReader.Get(snapshotStream)) { var ackTick = reader.ReadUInt16(); - Debug.Log(string.Format("Receive ack {0} from client {1}", ackTick, clientId)); + //Debug.Log(string.Format("Receive ack {0} from client {1}", ackTick, clientId)); } } From 03d1e7c653032359ebab284f2e85e4bbb0092d6f Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 26 May 2021 23:09:56 -0400 Subject: [PATCH 47/55] feat: snapshot. Refactoring to move the write of a NetVar in its own function, in case we change it for something else (blit) in the future --- .../Runtime/Core/SnapshotSystem.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index ffc89cbeed..3187b0f0f9 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -201,6 +201,8 @@ internal void ReadBuffer(NetworkReader reader, Stream snapshotStream) Stream.Seek(Entries[i].Position, SeekOrigin.Begin); + // todo: consider refactoring out in its own function to accomodate + // other ways to (de)serialize // todo --M1-- // Review whether tick still belong in netvar or in the snapshot table. nv.ReadDelta(Stream, m_NetworkManager.IsServer); @@ -409,21 +411,27 @@ public void Store(ulong networkObjectId, int behaviourIndex, int variableIndex, pos = m_Snapshot.AddEntry(k); } + WriteVariableToSnapshot(m_Snapshot, networkVariable, pos); + } + + private void WriteVariableToSnapshot(Snapshot snapshot, INetworkVariable networkVariable, int index) + { // write var into buffer, possibly adjusting entry's position and Length using (var varBuffer = PooledNetworkBuffer.Get()) { networkVariable.WriteDelta(varBuffer); - if (varBuffer.Length > m_Snapshot.Entries[pos].Length) + if (varBuffer.Length > snapshot.Entries[index].Length) { // allocate this Entry's buffer - m_Snapshot.AllocateEntry(ref m_Snapshot.Entries[pos], pos, (int)varBuffer.Length); + snapshot.AllocateEntry(ref snapshot.Entries[index], index, (int)varBuffer.Length); } // Copy the serialized NetworkVariable into our buffer - Buffer.BlockCopy(varBuffer.GetBuffer(), 0, m_Snapshot.Buffer, m_Snapshot.Entries[pos].Position, (int)varBuffer.Length); + Buffer.BlockCopy(varBuffer.GetBuffer(), 0, snapshot.Buffer, snapshot.Entries[index].Position, (int)varBuffer.Length); } } + /// /// Entry point when a Snapshot is received /// This is where we read and store the received snapshot From 884aa7c4b3d62bd64d8729d42f13898312470dbb Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 27 May 2021 14:16:04 -0400 Subject: [PATCH 48/55] feat: snapshot. Removing unneeded 'using' directive --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 86685f1d48..f1638fb95d 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -17,7 +17,6 @@ using MLAPI.Spawning; using MLAPI.Transports; using Unity.Profiling; -using Debug = UnityEngine.Debug; namespace MLAPI { From eb1fb94c1e8b29087d17b4090d71046422362082 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 27 May 2021 14:26:22 -0400 Subject: [PATCH 49/55] feat: snapshot. going back to classic deltas for merge to develop --- com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index a272f77109..ad91586dd4 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -54,8 +54,8 @@ public class NetworkManager : MonoBehaviour, INetworkUpdateSystem, IProfilableTr // todo: transitional. For the next release, only Snapshot should remain // The booleans allow iterative development and testing in the meantime - static internal bool UseClassicDelta = false; - static internal bool UseSnapshot = true; + static internal bool UseClassicDelta = true; + static internal bool UseSnapshot = false; internal RpcQueueContainer RpcQueueContainer { get; private set; } internal NetworkTickSystem NetworkTickSystem { get; private set; } From 00ccc8f1f8d62ae928dd8ed5e33fedaa4c0516e5 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 27 May 2021 14:30:06 -0400 Subject: [PATCH 50/55] feat: snapshot. keeping the origin/develop PlayerCube.prefab, for merging to develop --- testproject/Assets/Prefabs/PlayerCube.prefab | 26 +++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/testproject/Assets/Prefabs/PlayerCube.prefab b/testproject/Assets/Prefabs/PlayerCube.prefab index 29b35f9322..0c7bcb3067 100644 --- a/testproject/Assets/Prefabs/PlayerCube.prefab +++ b/testproject/Assets/Prefabs/PlayerCube.prefab @@ -229,8 +229,8 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} - m_Name: - m_EditorClassIdentifier: + m_Name: + m_EditorClassIdentifier: GlobalObjectIdHash: 951099334 AlwaysReplicateAsRoot: 0 DontDestroyWithOwner: 0 @@ -244,8 +244,8 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} - m_Name: - m_EditorClassIdentifier: + m_Name: + m_EditorClassIdentifier: TransformAuthority: 1 FixedSendsPerSecond: 30 InterpolatePosition: 1 @@ -255,8 +255,6 @@ MonoBehaviour: MinDegrees: 1.5 MinSize: 0.15 Channel: 10 - m_UseLocal: - m_InternalValue: 0 --- !u!114 &8685790303553767876 MonoBehaviour: m_ObjectHideFlags: 0 @@ -267,8 +265,8 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 82b41b172a31546ffba450f1418f4e69, type: 3} - m_Name: - m_EditorClassIdentifier: + m_Name: + m_EditorClassIdentifier: m_Speed: 10 m_RotSpeed: 2 --- !u!114 &3809075828520557319 @@ -281,8 +279,8 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 7aabd0e21680746e38b8c3deb86384b8, type: 3} - m_Name: - m_EditorClassIdentifier: + m_Name: + m_EditorClassIdentifier: --- !u!114 &7138389085065872747 MonoBehaviour: m_ObjectHideFlags: 0 @@ -293,8 +291,8 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 3e34656ebae784afca7d1f7f6dc18580, type: 3} - m_Name: - m_EditorClassIdentifier: + m_Name: + m_EditorClassIdentifier: Range: 10 --- !u!114 &-4978466230159947418 MonoBehaviour: @@ -306,5 +304,5 @@ MonoBehaviour: m_Enabled: 0 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 9548116c10df1486ea12b7329b77c5cf, type: 3} - m_Name: - m_EditorClassIdentifier: + m_Name: + m_EditorClassIdentifier: From 7190850a3544900a6810aa2f874912f4f8a98025 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Thu, 27 May 2021 14:44:55 -0400 Subject: [PATCH 51/55] style: whitespace fix --- .../Runtime/NetworkVariable/NetworkVariable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs b/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs index 42016b0e77..d8fcd4b3fc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs +++ b/com.unity.multiplayer.mlapi/Runtime/NetworkVariable/NetworkVariable.cs @@ -204,7 +204,7 @@ public void ReadField(Stream stream) /// public void WriteField(Stream stream) { - using (var writer = PooledNetworkWriter.Get(stream)) + using (var writer = PooledNetworkWriter.Get(stream)) { writer.WriteObjectPacked(m_InternalValue); //BOX } From 23d84d0e2339fab9c99fbe6adc89585fa8beb3e8 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Wed, 16 Jun 2021 14:52:50 -0400 Subject: [PATCH 52/55] feat: snapshot. Addressing code review comment --- .../Runtime/Core/IndexAllocator.cs | 13 +++++++++++++ .../Tests/Editor/IndexAllocatorTests.cs | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs index c33f7be74c..5e7525cefc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -85,6 +85,13 @@ internal int Range internal bool Allocate(int index, int size, out int pos) { pos = 0; + // size must be positive, index must be within range + if (size < 0 || index < 0 || index >= m_MaxSlot) + { + return false; + } + + // refuse allocation if the index is already in use if (m_IndexToSlot[index] != k_NotSet) { return false; @@ -141,6 +148,12 @@ internal bool Allocate(int index, int size, out int pos) /// internal bool Deallocate(int index) { + // size must be positive, index must be within range + if (index < 0 || index >= m_MaxSlot) + { + return false; + } + int slot = m_IndexToSlot[index]; if (slot == k_NotSet) diff --git a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs index b3ca178f1f..da5610b235 100644 --- a/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Editor/IndexAllocatorTests.cs @@ -18,6 +18,14 @@ public void SimpleTest() allocator.DebugDisplay(); Assert.IsTrue(allocator.Verify()); + // can't ask for negative amount of memory + Assert.IsFalse(allocator.Allocate(1, -20, out pos)); + Assert.IsTrue(allocator.Verify()); + + // can't ask for deallocation of negative index + Assert.IsFalse(allocator.Deallocate(-1)); + Assert.IsTrue(allocator.Verify()); + // can't ask for the same index twice Assert.IsFalse(allocator.Allocate(0, 20, out pos)); Assert.IsTrue(allocator.Verify()); From 51698a2004aa388a00f2e641f6bdec0b37db4b25 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Fri, 18 Jun 2021 11:25:00 -0400 Subject: [PATCH 53/55] feat: snapshot. Applying code review comments --- com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs | 2 +- com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs index 5e7525cefc..0d3a76849a 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/IndexAllocator.cs @@ -6,9 +6,9 @@ internal struct IndexAllocatorEntry { internal int Pos; // Position where the memory of this slot is internal int Length; // Length of the memory allocated to this slot - internal bool Free; // Whether this is a free slot internal int Next; // Next and Prev define the order of the slots in the buffer internal int Prev; + internal bool Free; // Whether this is a free slot } internal class IndexAllocator diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index 3187b0f0f9..fecef8bdbc 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -123,6 +123,9 @@ public int AddEntry(VariableKey k) /// The writer to write the entry to internal void WriteEntry(NetworkWriter writer, in Entry entry) { + //todo: major refactor. + // use blittable types and copy variable in memory locally + // only serialize when put on the wire for network transfer writer.WriteUInt64(entry.Key.NetworkObjectId); writer.WriteUInt16(entry.Key.BehaviourIndex); writer.WriteUInt16(entry.Key.VariableIndex); From 2276459386938f3625e8901f1c9d557041ecd667 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Mon, 21 Jun 2021 10:12:54 -0400 Subject: [PATCH 54/55] feat: snapshot. Checking variable presence, before trying to read it. --- .../Runtime/Core/SnapshotSystem.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index fecef8bdbc..af2f87fcad 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -200,15 +200,18 @@ internal void ReadBuffer(NetworkReader reader, Stream snapshotStream) { if (Entries[i].Fresh && Entries[i].Key.TickWritten > 0) { - var nv = FindNetworkVar(Entries[i].Key); + var networkVariable = FindNetworkVar(Entries[i].Key); - Stream.Seek(Entries[i].Position, SeekOrigin.Begin); + if (networkVariable != null) + { + Stream.Seek(Entries[i].Position, SeekOrigin.Begin); - // todo: consider refactoring out in its own function to accomodate - // other ways to (de)serialize - // todo --M1-- - // Review whether tick still belong in netvar or in the snapshot table. - nv.ReadDelta(Stream, m_NetworkManager.IsServer); + // todo: consider refactoring out in its own function to accomodate + // other ways to (de)serialize + // todo --M1-- + // Review whether tick still belong in netvar or in the snapshot table. + networkVariable.ReadDelta(Stream, m_NetworkManager.IsServer); + } } Entries[i].Fresh = false; From aecefeba1a5d77610019477cb4b632f1e73a2f08 Mon Sep 17 00:00:00 2001 From: Jeffrey Rainy Date: Mon, 5 Jul 2021 17:15:43 -0400 Subject: [PATCH 55/55] feat: snapshot. Some review comments --- com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs index af2f87fcad..216ab945d0 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/SnapshotSystem.cs @@ -85,6 +85,7 @@ public void Clear() /// The key we're looking for public int Find(VariableKey key) { + // todo: Add a IEquatable interface for VariableKey. Rely on that instead. for (int i = 0; i < LastEntry; i++) { if (Entries[i].Key.NetworkObjectId == key.NetworkObjectId && @@ -102,7 +103,7 @@ public int Find(VariableKey key) /// /// Adds an entry in the table for a new key /// - public int AddEntry(VariableKey k) + public int AddEntry(in VariableKey k) { var pos = LastEntry++; var entry = Entries[pos]; @@ -165,13 +166,12 @@ public void AllocateEntry(ref Entry entry, int index, int size) // todo: deal with free space // todo: deal with full buffer - int pos; - if (entry.Length > 0) { Allocator.Deallocate(index); } + int pos; bool ret = Allocator.Allocate(index, size, out pos); if (!ret) @@ -200,6 +200,7 @@ internal void ReadBuffer(NetworkReader reader, Stream snapshotStream) { if (Entries[i].Fresh && Entries[i].Key.TickWritten > 0) { + // todo: there might be a race condition here with object reuse. To investigate. var networkVariable = FindNetworkVar(Entries[i].Key); if (networkVariable != null)