Skip to content

Commit 169a1ae

Browse files
fix: NetworkBehaviour and NetworkVariableDelta length safety checks (#3405)
<!-- Replace this block with what this PR does and why. Describe what you'd like reviewers to know, how you applied the engineering principles, and any interesting tradeoffs made. Delete bullet points below that don't apply, and update the changelog section as appropriate. --> <!-- Add short version of the JIRA ticket to the PR title (e.g. "feat: new shiny feature [MTT-123]") --> [MTTB-1080](https://jira.unity3d.com/browse/MTTB-1080) fixes: #3335 ## Changelog - Fixed: NetworkBehaviour length safety checks use `int` instead of `ushort` - Fixed: NetworkVariable `EnsureNetworkVariableLengthSafety` checks use `int` instead of `ushort` - Fixed: NetworkVariableDeltas did not work with `DistributedAuthorityMode` and `EnsureNetworkVariableLengthSafety` ## Testing and Documentation - Includes unit tests. - No documentation changes or additions were necessary. <!-- Uncomment and mark items off with a * if this PR deprecates any API: ### Deprecated API - [ ] An `[Obsolete]` attribute was added along with a `(RemovedAfter yyyy-mm-dd)` entry. - [ ] An [api updater] was added. - [ ] Deprecation of the API is explained in the CHANGELOG. - [ ] The users can understand why this API was removed and what they should use instead. --> ## Backport <!-- If this is a backport: - Add the following to the PR title: "\[Backport\] ..." . - Link to the original PR. If this needs a backport - state this here If a backport is not needed please provide the reason why. If the "Backports" section is not present it will lead to a CI test failure. --> Backported in #3415 --------- Co-authored-by: Noel Stephens <[email protected]>
1 parent 6083210 commit 169a1ae

File tree

13 files changed

+176
-285
lines changed

13 files changed

+176
-285
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1414

1515
### Fixed
1616

17+
- Fixed issues with the `NetworkBehaviour` and `NetworkVariable` length safety checks. (#3405)
1718
- Fixed memory leaks when domain reload is disabled. (#3427)
1819
- Fixed an exception being thrown when unregistering a custom message handler from within the registered callback. (#3417)
1920

com.unity.netcode.gameobjects/Runtime/Configuration/SessionConfig.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ internal class SessionConfig
99
public const uint BypassFeatureCompatible = 1;
1010
public const uint ServerDistributionCompatible = 2;
1111
public const uint SessionStateToken = 3;
12+
public const uint NetworkBehaviourSerializationSafety = 4;
1213

1314
// The most current session version (!!!!set this when you increment!!!!!)
14-
public static uint PackageSessionVersion => SessionStateToken;
15+
public static uint PackageSessionVersion => NetworkBehaviourSerializationSafety;
1516

1617
internal uint SessionVersion;
1718

com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs

Lines changed: 31 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,55 +1232,41 @@ internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClie
12321232
var networkManager = NetworkManager;
12331233
var ensureLengthSafety = networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety;
12341234

1235-
1236-
// Exit early if there are no NetworkVariables
1237-
if (NetworkVariableFields.Count == 0)
1238-
{
1239-
return;
1240-
}
1241-
1242-
for (int j = 0; j < NetworkVariableFields.Count; j++)
1235+
foreach (var field in NetworkVariableFields)
12431236
{
12441237
// Client-Server: Try to write values only for clients that have read permissions.
12451238
// Distributed Authority: All clients have read permissions, always try to write the value.
1246-
if (NetworkVariableFields[j].CanClientRead(targetClientId))
1239+
if (field.CanClientRead(targetClientId))
12471240
{
1248-
// Write additional NetworkVariable information when length safety is enabled or when in distributed authority mode
1241+
// Write additional NetworkVariable information when length safety is enabled
12491242
if (ensureLengthSafety)
12501243
{
12511244
var writePos = writer.Position;
12521245
// Note: This value can't be packed because we don't know how large it will be in advance
1253-
// we reserve space for it, then write the data, then come back and fill in the space
1254-
// to pack here, we'd have to write data to a temporary buffer and copy it in - which
1255-
// isn't worth possibly saving one byte if and only if the data is less than 63 bytes long...
1256-
// The way we do packing, any value > 63 in a ushort will use the full 2 bytes to represent.
1257-
writer.WriteValueSafe((ushort)0);
1246+
// we reserve space for it, then write the data, then come back and write the final size value
1247+
writer.WriteValueSafe(0);
12581248
var startPos = writer.Position;
1249+
12591250
// Write the NetworkVariable field value
1260-
// WriteFieldSynchronization will write the current value only if there are no pending changes.
1261-
// Otherwise, it will write the previous value if there are pending changes since the pending
1262-
// changes will be sent shortly after the client's synchronization.
1263-
NetworkVariableFields[j].WriteFieldSynchronization(writer);
1251+
field.WriteFieldSynchronization(writer);
1252+
1253+
// Write the NetworkVariable field value size
12641254
var size = writer.Position - startPos;
12651255
writer.Seek(writePos);
1266-
// Write the NetworkVariable field value size
1267-
writer.WriteValueSafe((ushort)size);
1256+
writer.WriteValueSafe(size);
12681257
writer.Seek(startPos + size);
12691258
}
1270-
else // Client-Server Only: Should only ever be invoked when using a client-server NetworkTopology
1259+
else
12711260
{
12721261
// Write the NetworkVariable field value
1273-
// WriteFieldSynchronization will write the current value only if there are no pending changes.
1274-
// Otherwise, it will write the previous value if there are pending changes since the pending
1275-
// changes will be sent shortly after the client's synchronization.
1276-
NetworkVariableFields[j].WriteFieldSynchronization(writer);
1262+
field.WriteFieldSynchronization(writer);
12771263
}
12781264
}
12791265
else if (ensureLengthSafety)
12801266
{
12811267
// Client-Server Only: If the client cannot read this field, then skip it but write a 0 for this NetworkVariable's position
12821268
{
1283-
writer.WriteValueSafe((ushort)0);
1269+
writer.WriteValueSafe(0);
12841270
}
12851271
}
12861272
}
@@ -1300,26 +1286,20 @@ internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
13001286
var networkManager = NetworkManager;
13011287
var ensureLengthSafety = networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety;
13021288

1303-
// Exit early if nothing else to read
1304-
if (NetworkVariableFields.Count == 0)
1289+
foreach (var field in NetworkVariableFields)
13051290
{
1306-
return;
1307-
}
1308-
1309-
for (int j = 0; j < NetworkVariableFields.Count; j++)
1310-
{
1311-
var varSize = (ushort)0;
1291+
int expectedBytesToRead = 0;
13121292
var readStartPos = 0;
13131293
// Client-Server: Clients that only have read permissions will try to read the value
13141294
// Distributed Authority: All clients have read permissions, always try to read the value
1315-
if (NetworkVariableFields[j].CanClientRead(clientId))
1295+
if (field.CanClientRead(clientId))
13161296
{
13171297
if (ensureLengthSafety)
13181298
{
1319-
reader.ReadValueSafe(out varSize);
1320-
if (varSize == 0)
1299+
reader.ReadValueSafe(out expectedBytesToRead);
1300+
if (expectedBytesToRead == 0)
13211301
{
1322-
Debug.LogError($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{NetworkVariableFields[j].Name}] Expected non-zero size readable NetworkVariable! (Skipping)");
1302+
Debug.LogError($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{field.Name}] Expected non-zero size readable NetworkVariable! (Skipping)");
13231303
continue;
13241304
}
13251305
readStartPos = reader.Position;
@@ -1330,38 +1310,29 @@ internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
13301310
// If skipping and length safety, then fill in a 0 size for this one spot
13311311
if (ensureLengthSafety)
13321312
{
1333-
reader.ReadValueSafe(out ushort size);
1334-
if (size != 0)
1313+
reader.ReadValueSafe(out expectedBytesToRead);
1314+
if (expectedBytesToRead != 0)
13351315
{
1336-
Debug.LogError($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{NetworkVariableFields[j].Name}] Expected zero size for non-readable NetworkVariable when EnsureNetworkVariableLengthSafety is enabled! (Skipping)");
1316+
Debug.LogError($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{field.Name}] Expected zero size for non-readable NetworkVariable when EnsureNetworkVariableLengthSafety is enabled! (Skipping)");
13371317
}
13381318
}
13391319
continue;
13401320
}
13411321

13421322
// Read the NetworkVariable value
1343-
NetworkVariableFields[j].ReadField(reader);
1323+
field.ReadField(reader);
13441324

13451325
// When EnsureNetworkVariableLengthSafety always do a bounds check
13461326
if (ensureLengthSafety)
13471327
{
1348-
if (reader.Position > (readStartPos + varSize))
1349-
{
1350-
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
1351-
{
1352-
NetworkLog.LogWarning($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{NetworkVariableFields[j].Name}] NetworkVariable data read too big. {reader.Position - (readStartPos + varSize)} bytes.");
1353-
}
1354-
1355-
reader.Seek(readStartPos + varSize);
1356-
}
1357-
else if (reader.Position < (readStartPos + varSize))
1328+
var totalBytesRead = reader.Position - readStartPos;
1329+
if (totalBytesRead != expectedBytesToRead)
13581330
{
1359-
if (NetworkLog.CurrentLogLevel <= LogLevel.Normal)
1331+
if (NetworkManager.LogLevel <= LogLevel.Normal)
13601332
{
1361-
NetworkLog.LogWarning($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{NetworkVariableFields[j].Name}] NetworkVariable data read too small. {(readStartPos + varSize) - reader.Position} bytes.");
1333+
NetworkLog.LogWarning($"[{name}][NetworkObjectId: {NetworkObjectId}][NetworkBehaviourId: {NetworkBehaviourId}][{field.Name}] NetworkVariable read {totalBytesRead} bytes but was expected to read {expectedBytesToRead} bytes during synchronization deserialization!");
13621334
}
1363-
1364-
reader.Seek(readStartPos + varSize);
1335+
reader.Seek(readStartPos + expectedBytesToRead);
13651336
}
13661337
}
13671338
}
@@ -1443,7 +1414,7 @@ internal bool Synchronize<T>(ref BufferSerializer<T> serializer, ulong targetCli
14431414
// Save our position where we will write the final size being written so we can skip over it in the
14441415
// event an exception occurs when deserializing.
14451416
var sizePosition = writer.Position;
1446-
writer.WriteValueSafe((ushort)0);
1417+
writer.WriteValueSafe(0);
14471418

14481419
// Save our position before synchronizing to determine how much was written
14491420
var positionBeforeSynchronize = writer.Position;
@@ -1481,7 +1452,7 @@ internal bool Synchronize<T>(ref BufferSerializer<T> serializer, ulong targetCli
14811452
// Write the number of bytes serialized to handle exceptions on the deserialization side
14821453
var bytesWritten = finalPosition - positionBeforeSynchronize;
14831454
writer.Seek(sizePosition);
1484-
writer.WriteValueSafe((ushort)bytesWritten);
1455+
writer.WriteValueSafe(bytesWritten);
14851456
writer.Seek(finalPosition);
14861457
}
14871458
return true;
@@ -1490,7 +1461,7 @@ internal bool Synchronize<T>(ref BufferSerializer<T> serializer, ulong targetCli
14901461
{
14911462
var reader = serializer.GetFastBufferReader();
14921463
// We will always read the expected byte count
1493-
reader.ReadValueSafe(out ushort expectedBytesToRead);
1464+
reader.ReadValueSafe(out int expectedBytesToRead);
14941465

14951466
// Save our position before we begin synchronization deserialization
14961467
var positionBeforeSynchronize = reader.Position;

com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviourUpdater.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,14 @@ internal void NetworkBehaviourUpdate(bool forceSend = false)
104104
}
105105
}
106106
}
107+
107108
// Now, reset all the no-longer-dirty variables
108-
foreach (var dirtyobj in m_DirtyNetworkObjects)
109+
foreach (var dirtyObj in m_DirtyNetworkObjects)
109110
{
110-
dirtyobj.PostNetworkVariableWrite(forceSend);
111+
foreach (var behaviour in dirtyObj.ChildNetworkBehaviours)
112+
{
113+
behaviour.PostNetworkVariableWrite(forceSend);
114+
}
111115
}
112116
m_DirtyNetworkObjects.Clear();
113117
}

0 commit comments

Comments
 (0)