diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs index 5ee2b7dd09..a7a89c458c 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkBehaviour.cs @@ -551,6 +551,7 @@ private void NetworkVariableUpdate(ulong clientId, int behaviourIndex) else { NetworkVariableFields[k].WriteDelta(buffer); + buffer.PadBuffer(); } if (!m_NetworkVariableIndexesToResetSet.Contains(k)) @@ -656,11 +657,10 @@ internal static void HandleNetworkVariableDeltas(List networkV PerformanceDataManager.Increment(ProfilerConstants.NetworkVarDeltas); ProfilerStatManager.NetworkVarsRcvd.Record(); + (stream as NetworkBuffer).SkipPadBits(); if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { - (stream as NetworkBuffer).SkipPadBits(); - if (stream.Position > (readStartPos + varSize)) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) @@ -816,6 +816,7 @@ internal static void WriteNetworkVariableData(List networkVari else { networkVariableList[j].WriteField(stream); + writer.WritePadBits(); } } } @@ -855,6 +856,7 @@ internal static void SetNetworkVariableData(List networkVariab long readStartPos = stream.Position; networkVariableList[j].ReadField(stream); + reader.SkipPadBits(); if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety) { diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkVarBufferCopyTest.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkVarBufferCopyTest.cs new file mode 100644 index 0000000000..42ddc12774 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkVarBufferCopyTest.cs @@ -0,0 +1,150 @@ +using System.Collections; +using System.IO; +using NUnit.Framework; +using UnityEngine.TestTools; + +namespace Unity.Netcode.RuntimeTests +{ + public class NetworkVarBufferCopyTest : BaseMultiInstanceTest + { + public class DummyNetVar : INetworkVariable + { + private const int k_DummyValue = 0x13579BDF; + public bool DeltaWritten; + public bool FieldWritten; + public bool DeltaRead; + public bool FieldRead; + public bool Dirty = true; + + public string Name { get; internal set; } + + public NetworkChannel GetChannel() + { + return NetworkChannel.NetworkVariable; + } + + public void ResetDirty() + { + Dirty = false; + } + + public bool IsDirty() + { + return Dirty; + } + + public bool CanClientWrite(ulong clientId) + { + return true; + } + + public bool CanClientRead(ulong clientId) + { + return true; + } + + public void WriteDelta(Stream stream) + { + using (var writer = PooledNetworkWriter.Get(stream)) + { + writer.WriteBits((byte)1, 1); + writer.WriteInt32(k_DummyValue); + } + + DeltaWritten = true; + } + + public void WriteField(Stream stream) + { + using (var writer = PooledNetworkWriter.Get(stream)) + { + writer.WriteBits((byte)1, 1); + writer.WriteInt32(k_DummyValue); + } + + FieldWritten = true; + } + + public void ReadField(Stream stream) + { + using (var reader = PooledNetworkReader.Get(stream)) + { + reader.ReadBits(1); + Assert.AreEqual(k_DummyValue, reader.ReadInt32()); + } + + FieldRead = true; + } + + public void ReadDelta(Stream stream, bool keepDirtyDelta) + { + using (var reader = PooledNetworkReader.Get(stream)) + { + reader.ReadBits(1); + Assert.AreEqual(k_DummyValue, reader.ReadInt32()); + } + + DeltaRead = true; + } + + public void SetNetworkBehaviour(NetworkBehaviour behaviour) + { + // nop + } + } + + public class DummyNetBehaviour : NetworkBehaviour + { + public DummyNetVar NetVar; + } + protected override int NbClients => 1; + + [UnitySetUp] + public override IEnumerator Setup() + { + yield return StartSomeClientsAndServerWithPlayers(useHost: true, nbClients: NbClients, + updatePlayerPrefab: playerPrefab => + { + var dummyNetBehaviour = playerPrefab.AddComponent(); + }); + } + + [UnityTest] + public IEnumerator TestEntireBufferIsCopiedOnNetworkVariableDelta() + { + // This is the *SERVER VERSION* of the *CLIENT PLAYER* + var serverClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation( + x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId, + m_ServerNetworkManager, serverClientPlayerResult)); + + // This is the *CLIENT VERSION* of the *CLIENT PLAYER* + var clientClientPlayerResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.GetNetworkObjectByRepresentation( + x => x.IsPlayerObject && x.OwnerClientId == m_ClientNetworkManagers[0].LocalClientId, + m_ClientNetworkManagers[0], clientClientPlayerResult)); + + var serverSideClientPlayer = serverClientPlayerResult.Result; + var clientSideClientPlayer = clientClientPlayerResult.Result; + + var serverComponent = (serverSideClientPlayer).GetComponent(); + var clientComponent = (clientSideClientPlayer).GetComponent(); + + var waitResult = new MultiInstanceHelpers.CoroutineResultWrapper(); + + yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForCondition( + () => clientComponent.NetVar.DeltaRead == true, + waitResult, + maxFrames: 120)); + + if (!waitResult.Result) + { + Assert.Fail("Failed to send a delta within 120 frames"); + } + Assert.True(serverComponent.NetVar.FieldWritten); + Assert.True(serverComponent.NetVar.DeltaWritten); + Assert.True(clientComponent.NetVar.FieldRead); + Assert.True(clientComponent.NetVar.DeltaRead); + } + } +} diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkVarBufferCopyTest.cs.meta b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkVarBufferCopyTest.cs.meta new file mode 100644 index 0000000000..88e49e3e87 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkVarBufferCopyTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a7d44caa76a64b02978f5aca0e7b576a +timeCreated: 1627926008 \ No newline at end of file