com.unity.netcode.gameobjects@2.1.1
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [2.1.1] - 2024-10-18 ### Added - Added ability to edit the `NetworkConfig.AutoSpawnPlayerPrefabClientSide` within the inspector view. (#3097) - Added `IContactEventHandlerWithInfo` that derives from `IContactEventHandler` that can be updated per frame to provide `ContactEventHandlerInfo` information to the `RigidbodyContactEventManager` when processing collisions. (#3094) - `ContactEventHandlerInfo.ProvideNonRigidBodyContactEvents`: When set to true, non-`Rigidbody` collisions with the registered `Rigidbody` will generate contact event notifications. (#3094) - `ContactEventHandlerInfo.HasContactEventPriority`: When set to true, the `Rigidbody` will be prioritized as the instance that generates the event if the `Rigidbody` colliding does not have priority. (#3094) - Added a static `NetworkManager.OnInstantiated` event notification to be able to track when a new `NetworkManager` instance has been instantiated. (#3088) - Added a static `NetworkManager.OnDestroying` event notification to be able to track when an existing `NetworkManager` instance is being destroyed. (#3088) ### Fixed - Fixed issue where `NetworkPrefabProcessor` would not mark the prefab list as dirty and prevent saving the `DefaultNetworkPrefabs` asset when only imports or only deletes were detected.(#3103) - Fixed an issue where nested `NetworkTransform` components in owner authoritative mode cleared their initial settings on the server, causing improper synchronization. (#3099) - Fixed issue with service not getting synchronized with in-scene placed `NetworkObject` instances when a session owner starts a `SceneEventType.Load` event. (#3096) - Fixed issue with the in-scene network prefab instance update menu tool where it was not properly updating scenes when invoked on the root prefab instance. (#3092) - Fixed an issue where newly synchronizing clients would always receive current `NetworkVariable` values, potentially causing issues with collections if there were pending updates. Now, pending state updates serialize previous values to avoid duplicates on new clients. (#3081) - Fixed issue where changing ownership would mark every `NetworkVariable` dirty. Now, it will only mark any `NetworkVariable` with owner read permissions as dirty and will send/flush any pending updates to all clients prior to sending the change in ownership message. (#3081) - Fixed an issue where transferring ownership of `NetworkVariable` collections didn't update the new owner’s previous value, causing the last added value to be detected as a change during additions or removals. (#3081) - Fixed issue where a client (or server) with no write permissions for a `NetworkVariable` using a standard .NET collection type could still modify the collection which could cause various issues depending upon the modification and collection type. (#3081) - Fixed issue where applying the position and/or rotation to the `NetworkManager.ConnectionApprovalResponse` when connection approval and auto-spawn player prefab were enabled would not apply the position and/or rotation when the player prefab was instantiated. (#3078) - Fixed issue where `NetworkObject.SpawnWithObservers` was not being honored when spawning the player prefab. (#3077) - Fixed issue with the client count not being correct on the host or server side when a client disconnects itself from a session. (#3075) ### Changed - Changed `NetworkConfig.AutoSpawnPlayerPrefabClientSide` is no longer automatically set when starting `NetworkManager`. (#3097) - Updated `NetworkVariableDeltaMessage` so the server now forwards delta state updates from clients immediately, instead of waiting until the end of the frame or the next network tick. (#3081)
This commit is contained in:
@@ -9,7 +9,6 @@ namespace Unity.Netcode
|
||||
|
||||
public ulong NetworkObjectId;
|
||||
public ulong OwnerClientId;
|
||||
// DANGOEXP TODO: Remove these notes or change their format
|
||||
// SERVICE NOTES:
|
||||
// When forwarding the message to clients on the CMB Service side,
|
||||
// you can set the ClientIdCount to 0 and skip writing the ClientIds.
|
||||
@@ -258,15 +257,18 @@ namespace Unity.Netcode
|
||||
continue;
|
||||
}
|
||||
|
||||
// If ownership is changing and this is not an ownership request approval then ignore the OnwerClientId
|
||||
// If it is just updating flags then ignore sending to the owner
|
||||
// If it is a request or approving request, then ignore the RequestClientId
|
||||
if ((OwnershipIsChanging && !RequestApproved && OwnerClientId == clientId) || (OwnershipFlagsUpdate && clientId == OwnerClientId)
|
||||
|| ((RequestOwnership || RequestApproved) && clientId == RequestClientId))
|
||||
// If ownership is changing and this is not an ownership request approval then ignore the SenderId
|
||||
if (OwnershipIsChanging && !RequestApproved && context.SenderId == clientId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it is just updating flags then ignore sending to the owner
|
||||
// If it is a request or approving request, then ignore the RequestClientId
|
||||
if ((OwnershipFlagsUpdate && clientId == OwnerClientId) || ((RequestOwnership || RequestApproved) && clientId == RequestClientId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
networkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery.Reliable, clientId);
|
||||
}
|
||||
}
|
||||
@@ -327,10 +329,12 @@ namespace Unity.Netcode
|
||||
var networkManager = (NetworkManager)context.SystemOwner;
|
||||
var networkObject = networkManager.SpawnManager.SpawnedObjects[NetworkObjectId];
|
||||
|
||||
// DANGO-TODO: This probably shouldn't be allowed to happen.
|
||||
// Sanity check that we are not sending duplicated change ownership messages
|
||||
if (networkObject.OwnerClientId == OwnerClientId)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"Unnecessary ownership changed message for {NetworkObjectId}");
|
||||
UnityEngine.Debug.LogError($"Unnecessary ownership changed message for {NetworkObjectId}.");
|
||||
// Ignore the message
|
||||
return;
|
||||
}
|
||||
|
||||
var originalOwner = networkObject.OwnerClientId;
|
||||
@@ -347,12 +351,6 @@ namespace Unity.Netcode
|
||||
networkObject.InvokeBehaviourOnLostOwnership();
|
||||
}
|
||||
|
||||
// We are new owner or (client-server) or running in distributed authority mode
|
||||
if (OwnerClientId == networkManager.LocalClientId || networkManager.DistributedAuthorityMode)
|
||||
{
|
||||
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||
}
|
||||
|
||||
// If in distributed authority mode
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
{
|
||||
@@ -374,6 +372,22 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
// We are new owner or (client-server) or running in distributed authority mode
|
||||
if (OwnerClientId == networkManager.LocalClientId || networkManager.DistributedAuthorityMode)
|
||||
{
|
||||
networkObject.InvokeBehaviourOnGainedOwnership();
|
||||
}
|
||||
|
||||
|
||||
if (originalOwner == networkManager.LocalClientId && !networkManager.DistributedAuthorityMode)
|
||||
{
|
||||
// Mark any owner read variables as dirty
|
||||
networkObject.MarkOwnerReadVariablesDirty();
|
||||
// Immediately queue any pending deltas and order the message before the
|
||||
// change in ownership message.
|
||||
networkManager.BehaviourUpdater.NetworkBehaviourUpdate(true);
|
||||
}
|
||||
|
||||
// Always invoke ownership change notifications
|
||||
networkObject.InvokeOwnershipChanged(originalOwner, OwnerClientId);
|
||||
|
||||
|
||||
@@ -3,14 +3,39 @@ using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct ServiceConfig : INetworkSerializable
|
||||
{
|
||||
public uint Version;
|
||||
public bool IsRestoredSession;
|
||||
public ulong CurrentSessionOwner;
|
||||
|
||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||
{
|
||||
if (serializer.IsWriter)
|
||||
{
|
||||
BytePacker.WriteValueBitPacked(serializer.GetFastBufferWriter(), Version);
|
||||
serializer.SerializeValue(ref IsRestoredSession);
|
||||
BytePacker.WriteValueBitPacked(serializer.GetFastBufferWriter(), CurrentSessionOwner);
|
||||
}
|
||||
else
|
||||
{
|
||||
ByteUnpacker.ReadValueBitPacked(serializer.GetFastBufferReader(), out Version);
|
||||
serializer.SerializeValue(ref IsRestoredSession);
|
||||
ByteUnpacker.ReadValueBitPacked(serializer.GetFastBufferReader(), out CurrentSessionOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ConnectionApprovedMessage : INetworkMessage
|
||||
{
|
||||
private const int k_AddCMBServiceConfig = 2;
|
||||
private const int k_VersionAddClientIds = 1;
|
||||
public int Version => k_VersionAddClientIds;
|
||||
public int Version => k_AddCMBServiceConfig;
|
||||
|
||||
public ulong OwnerClientId;
|
||||
public int NetworkTick;
|
||||
// The cloud state service should set this if we are restoring a session
|
||||
public ServiceConfig ServiceConfig;
|
||||
public bool IsRestoredSession;
|
||||
public ulong CurrentSessionOwner;
|
||||
// Not serialized
|
||||
@@ -25,6 +50,32 @@ namespace Unity.Netcode
|
||||
|
||||
public NativeArray<ulong> ConnectedClientIds;
|
||||
|
||||
private int m_ReceiveMessageVersion;
|
||||
|
||||
private ulong GetSessionOwner()
|
||||
{
|
||||
if (m_ReceiveMessageVersion >= k_AddCMBServiceConfig)
|
||||
{
|
||||
return ServiceConfig.CurrentSessionOwner;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CurrentSessionOwner;
|
||||
}
|
||||
}
|
||||
|
||||
private bool GetIsSessionRestor()
|
||||
{
|
||||
if (m_ReceiveMessageVersion >= k_AddCMBServiceConfig)
|
||||
{
|
||||
return ServiceConfig.IsRestoredSession;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsRestoredSession;
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||
{
|
||||
// ============================================================
|
||||
@@ -45,8 +96,17 @@ namespace Unity.Netcode
|
||||
BytePacker.WriteValueBitPacked(writer, NetworkTick);
|
||||
if (IsDistributedAuthority)
|
||||
{
|
||||
writer.WriteValueSafe(IsRestoredSession);
|
||||
BytePacker.WriteValueBitPacked(writer, CurrentSessionOwner);
|
||||
if (targetVersion >= k_AddCMBServiceConfig)
|
||||
{
|
||||
ServiceConfig.IsRestoredSession = false;
|
||||
ServiceConfig.CurrentSessionOwner = CurrentSessionOwner;
|
||||
writer.WriteNetworkSerializable(ServiceConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValueSafe(IsRestoredSession);
|
||||
BytePacker.WriteValueBitPacked(writer, CurrentSessionOwner);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetVersion >= k_VersionAddClientIds)
|
||||
@@ -122,13 +182,20 @@ namespace Unity.Netcode
|
||||
// ============================================================
|
||||
// END FORBIDDEN SEGMENT
|
||||
// ============================================================
|
||||
|
||||
m_ReceiveMessageVersion = receivedMessageVersion;
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out OwnerClientId);
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out NetworkTick);
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
{
|
||||
reader.ReadValueSafe(out IsRestoredSession);
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out CurrentSessionOwner);
|
||||
if (receivedMessageVersion >= k_AddCMBServiceConfig)
|
||||
{
|
||||
reader.ReadNetworkSerializable(out ServiceConfig);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.ReadValueSafe(out IsRestoredSession);
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out CurrentSessionOwner);
|
||||
}
|
||||
}
|
||||
|
||||
if (receivedMessageVersion >= k_VersionAddClientIds)
|
||||
@@ -157,7 +224,7 @@ namespace Unity.Netcode
|
||||
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
{
|
||||
networkManager.SetSessionOwner(CurrentSessionOwner);
|
||||
networkManager.SetSessionOwner(GetSessionOwner());
|
||||
if (networkManager.LocalClient.IsSessionOwner && networkManager.NetworkConfig.EnableSceneManagement)
|
||||
{
|
||||
networkManager.SceneManager.InitializeScenesLoaded();
|
||||
@@ -233,9 +300,9 @@ namespace Unity.Netcode
|
||||
// Mark the client being connected
|
||||
networkManager.IsConnectedClient = true;
|
||||
|
||||
networkManager.SceneManager.IsRestoringSession = IsRestoredSession;
|
||||
networkManager.SceneManager.IsRestoringSession = GetIsSessionRestor();
|
||||
|
||||
if (!IsRestoredSession)
|
||||
if (!networkManager.SceneManager.IsRestoringSession)
|
||||
{
|
||||
// Synchronize the service with the initial session owner's loaded scenes and spawned objects
|
||||
networkManager.SceneManager.SynchronizeNetworkObjects(NetworkManager.ServerClientId);
|
||||
|
||||
@@ -2,16 +2,54 @@ using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode
|
||||
{
|
||||
internal struct ConnectionRequestMessage : INetworkMessage
|
||||
/// <summary>
|
||||
/// Only used when connecting to the distributed authority service
|
||||
/// </summary>
|
||||
internal struct ClientConfig : INetworkSerializable
|
||||
{
|
||||
public int Version => 0;
|
||||
|
||||
public ulong ConfigHash;
|
||||
|
||||
public bool CMBServiceConnection;
|
||||
/// <summary>
|
||||
/// We start at version 1, where anything less than version 1 on the service side
|
||||
/// is not bypass feature compatible.
|
||||
/// </summary>
|
||||
private const int k_BypassFeatureCompatible = 1;
|
||||
public int Version => k_BypassFeatureCompatible;
|
||||
public uint TickRate;
|
||||
public bool EnableSceneManagement;
|
||||
|
||||
// Only gets deserialized but should never be used unless testing
|
||||
public int RemoteClientVersion;
|
||||
|
||||
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
|
||||
{
|
||||
if (serializer.IsWriter)
|
||||
{
|
||||
var writer = serializer.GetFastBufferWriter();
|
||||
BytePacker.WriteValueBitPacked(writer, Version);
|
||||
BytePacker.WriteValueBitPacked(writer, TickRate);
|
||||
writer.WriteValueSafe(EnableSceneManagement);
|
||||
}
|
||||
else
|
||||
{
|
||||
var reader = serializer.GetFastBufferReader();
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out RemoteClientVersion);
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out TickRate);
|
||||
reader.ReadValueSafe(out EnableSceneManagement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal struct ConnectionRequestMessage : INetworkMessage
|
||||
{
|
||||
// This version update is unidirectional (client to service) and version
|
||||
// handling occurs on the service side. This serialized data is never sent
|
||||
// to a host or server.
|
||||
private const int k_SendClientConfigToService = 1;
|
||||
public int Version => k_SendClientConfigToService;
|
||||
|
||||
public ulong ConfigHash;
|
||||
public bool CMBServiceConnection;
|
||||
public ClientConfig ClientConfig;
|
||||
|
||||
public byte[] ConnectionData;
|
||||
|
||||
public bool ShouldSendConnectionData;
|
||||
@@ -36,8 +74,7 @@ namespace Unity.Netcode
|
||||
|
||||
if (CMBServiceConnection)
|
||||
{
|
||||
writer.WriteValueSafe(TickRate);
|
||||
writer.WriteValueSafe(EnableSceneManagement);
|
||||
writer.WriteNetworkSerializable(ClientConfig);
|
||||
}
|
||||
|
||||
if (ShouldSendConnectionData)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode
|
||||
@@ -10,9 +11,22 @@ namespace Unity.Netcode
|
||||
/// serialization. This is due to the generally amorphous nature of network variable
|
||||
/// deltas, since they're all driven by custom virtual method overloads.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Version 1:
|
||||
/// This version -does not- use the "KeepDirty" approach. Instead, the server will forward any state updates
|
||||
/// to the connected clients that are not the sender or the server itself. Each NetworkVariable state update
|
||||
/// included, on a per client basis, is first validated that the client can read the NetworkVariable before
|
||||
/// being added to the m_ForwardUpdates table.
|
||||
/// Version 0:
|
||||
/// The original version uses the "KeepDirty" approach in a client-server network topology where the server
|
||||
/// proxies state updates by "keeping the NetworkVariable(s) dirty" so it will send state updates
|
||||
/// at the end of the frame (but could delay until the next tick).
|
||||
/// </remarks>
|
||||
internal struct NetworkVariableDeltaMessage : INetworkMessage
|
||||
{
|
||||
public int Version => 0;
|
||||
private const int k_ServerDeltaForwardingAndNetworkDelivery = 1;
|
||||
public int Version => k_ServerDeltaForwardingAndNetworkDelivery;
|
||||
|
||||
|
||||
public ulong NetworkObjectId;
|
||||
public ushort NetworkBehaviourIndex;
|
||||
@@ -21,10 +35,62 @@ namespace Unity.Netcode
|
||||
public ulong TargetClientId;
|
||||
public NetworkBehaviour NetworkBehaviour;
|
||||
|
||||
public NetworkDelivery NetworkDelivery;
|
||||
|
||||
private FastBufferReader m_ReceivedNetworkVariableData;
|
||||
|
||||
private bool m_ForwardingMessage;
|
||||
|
||||
private int m_ReceivedMessageVersion;
|
||||
|
||||
private const string k_Name = "NetworkVariableDeltaMessage";
|
||||
|
||||
private Dictionary<ulong, List<int>> m_ForwardUpdates;
|
||||
|
||||
private List<int> m_UpdatedNetworkVariables;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void WriteNetworkVariable(ref FastBufferWriter writer, ref NetworkVariableBase networkVariable, bool distributedAuthorityMode, bool ensureNetworkVariableLengthSafety, int nonfragmentedSize, int fragmentedSize)
|
||||
{
|
||||
if (ensureNetworkVariableLengthSafety)
|
||||
{
|
||||
var tempWriter = new FastBufferWriter(nonfragmentedSize, Allocator.Temp, fragmentedSize);
|
||||
networkVariable.WriteDelta(tempWriter);
|
||||
BytePacker.WriteValueBitPacked(writer, tempWriter.Length);
|
||||
|
||||
if (!writer.TryBeginWrite(tempWriter.Length))
|
||||
{
|
||||
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
||||
}
|
||||
|
||||
tempWriter.CopyTo(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Determine if we need to remove this with the 6.1 service updates
|
||||
if (distributedAuthorityMode)
|
||||
{
|
||||
var size_marker = writer.Position;
|
||||
writer.WriteValueSafe<ushort>(0);
|
||||
var start_marker = writer.Position;
|
||||
networkVariable.WriteDelta(writer);
|
||||
var end_marker = writer.Position;
|
||||
writer.Seek(size_marker);
|
||||
var size = end_marker - start_marker;
|
||||
if (size == 0)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Invalid write size of zero!");
|
||||
}
|
||||
writer.WriteValueSafe((ushort)size);
|
||||
writer.Seek(end_marker);
|
||||
}
|
||||
else
|
||||
{
|
||||
networkVariable.WriteDelta(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Serialize(FastBufferWriter writer, int targetVersion)
|
||||
{
|
||||
if (!writer.TryBeginWrite(FastBufferWriter.GetWriteSize(NetworkObjectId) + FastBufferWriter.GetWriteSize(NetworkBehaviourIndex)))
|
||||
@@ -34,10 +100,67 @@ namespace Unity.Netcode
|
||||
|
||||
var obj = NetworkBehaviour.NetworkObject;
|
||||
var networkManager = obj.NetworkManagerOwner;
|
||||
var typeName = NetworkBehaviour.__getTypeName();
|
||||
var nonFragmentedMessageMaxSize = networkManager.MessageManager.NonFragmentedMessageMaxSize;
|
||||
var fragmentedMessageMaxSize = networkManager.MessageManager.FragmentedMessageMaxSize;
|
||||
var ensureNetworkVariableLengthSafety = networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety;
|
||||
var distributedAuthorityMode = networkManager.DistributedAuthorityMode;
|
||||
|
||||
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
|
||||
BytePacker.WriteValueBitPacked(writer, NetworkBehaviourIndex);
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
|
||||
// If using k_IncludeNetworkDelivery version, then we want to write the network delivery used and if we
|
||||
// are forwarding state updates then serialize any NetworkVariable states specific to this client.
|
||||
if (targetVersion >= k_ServerDeltaForwardingAndNetworkDelivery)
|
||||
{
|
||||
writer.WriteValueSafe(NetworkDelivery);
|
||||
// If we are forwarding the message, then proceed to forward state updates specific to the targeted client
|
||||
if (m_ForwardingMessage)
|
||||
{
|
||||
// DANGO TODO: Remove distributedAuthorityMode portion when we remove the service specific NetworkVariable stuff
|
||||
if (distributedAuthorityMode)
|
||||
{
|
||||
writer.WriteValueSafe((ushort)NetworkBehaviour.NetworkVariableFields.Count);
|
||||
}
|
||||
|
||||
for (int i = 0; i < NetworkBehaviour.NetworkVariableFields.Count; i++)
|
||||
{
|
||||
var startingSize = writer.Length;
|
||||
var networkVariable = NetworkBehaviour.NetworkVariableFields[i];
|
||||
var shouldWrite = m_ForwardUpdates[TargetClientId].Contains(i);
|
||||
|
||||
// This var does not belong to the currently iterating delivery group.
|
||||
if (distributedAuthorityMode)
|
||||
{
|
||||
if (!shouldWrite)
|
||||
{
|
||||
writer.WriteValueSafe<ushort>(0);
|
||||
}
|
||||
}
|
||||
else if (ensureNetworkVariableLengthSafety)
|
||||
{
|
||||
if (!shouldWrite)
|
||||
{
|
||||
BytePacker.WriteValueBitPacked(writer, (ushort)0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteValueSafe(shouldWrite);
|
||||
}
|
||||
|
||||
if (shouldWrite)
|
||||
{
|
||||
WriteNetworkVariable(ref writer, ref networkVariable, distributedAuthorityMode, ensureNetworkVariableLengthSafety, nonFragmentedMessageMaxSize, fragmentedMessageMaxSize);
|
||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(TargetClientId, obj, networkVariable.Name, typeName, writer.Length - startingSize);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// DANGO TODO: Remove this when we remove the service specific NetworkVariable stuff
|
||||
if (distributedAuthorityMode)
|
||||
{
|
||||
writer.WriteValueSafe((ushort)NetworkBehaviour.NetworkVariableFields.Count);
|
||||
}
|
||||
@@ -46,12 +169,12 @@ namespace Unity.Netcode
|
||||
{
|
||||
if (!DeliveryMappedNetworkVariableIndex.Contains(i))
|
||||
{
|
||||
// This var does not belong to the currently iterating delivery group.
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
// DANGO TODO: Remove distributedAuthorityMode portion when we remove the service specific NetworkVariable stuff
|
||||
if (distributedAuthorityMode)
|
||||
{
|
||||
writer.WriteValueSafe<ushort>(0);
|
||||
}
|
||||
else if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
else if (ensureNetworkVariableLengthSafety)
|
||||
{
|
||||
BytePacker.WriteValueBitPacked(writer, (ushort)0);
|
||||
}
|
||||
@@ -88,14 +211,15 @@ namespace Unity.Netcode
|
||||
shouldWrite = false;
|
||||
}
|
||||
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
// DANGO TODO: Remove distributedAuthorityMode portion when we remove the service specific NetworkVariable stuff
|
||||
if (distributedAuthorityMode)
|
||||
{
|
||||
if (!shouldWrite)
|
||||
{
|
||||
writer.WriteValueSafe<ushort>(0);
|
||||
}
|
||||
}
|
||||
else if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
else if (ensureNetworkVariableLengthSafety)
|
||||
{
|
||||
if (!shouldWrite)
|
||||
{
|
||||
@@ -109,53 +233,22 @@ namespace Unity.Netcode
|
||||
|
||||
if (shouldWrite)
|
||||
{
|
||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
{
|
||||
var tempWriter = new FastBufferWriter(networkManager.MessageManager.NonFragmentedMessageMaxSize, Allocator.Temp, networkManager.MessageManager.FragmentedMessageMaxSize);
|
||||
NetworkBehaviour.NetworkVariableFields[i].WriteDelta(tempWriter);
|
||||
BytePacker.WriteValueBitPacked(writer, tempWriter.Length);
|
||||
|
||||
if (!writer.TryBeginWrite(tempWriter.Length))
|
||||
{
|
||||
throw new OverflowException($"Not enough space in the buffer to write {nameof(NetworkVariableDeltaMessage)}");
|
||||
}
|
||||
|
||||
tempWriter.CopyTo(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
{
|
||||
var size_marker = writer.Position;
|
||||
writer.WriteValueSafe<ushort>(0);
|
||||
var start_marker = writer.Position;
|
||||
networkVariable.WriteDelta(writer);
|
||||
var end_marker = writer.Position;
|
||||
writer.Seek(size_marker);
|
||||
var size = end_marker - start_marker;
|
||||
writer.WriteValueSafe((ushort)size);
|
||||
writer.Seek(end_marker);
|
||||
}
|
||||
else
|
||||
{
|
||||
networkVariable.WriteDelta(writer);
|
||||
}
|
||||
}
|
||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(
|
||||
TargetClientId,
|
||||
obj,
|
||||
networkVariable.Name,
|
||||
NetworkBehaviour.__getTypeName(),
|
||||
writer.Length - startingSize);
|
||||
WriteNetworkVariable(ref writer, ref networkVariable, distributedAuthorityMode, ensureNetworkVariableLengthSafety, nonFragmentedMessageMaxSize, fragmentedMessageMaxSize);
|
||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaSent(TargetClientId, obj, networkVariable.Name, typeName, writer.Length - startingSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context, int receivedMessageVersion)
|
||||
{
|
||||
m_ReceivedMessageVersion = receivedMessageVersion;
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out NetworkBehaviourIndex);
|
||||
|
||||
// If we are using the k_IncludeNetworkDelivery message version, then read the NetworkDelivery used
|
||||
if (receivedMessageVersion >= k_ServerDeltaForwardingAndNetworkDelivery)
|
||||
{
|
||||
reader.ReadValueSafe(out NetworkDelivery);
|
||||
}
|
||||
m_ReceivedNetworkVariableData = reader;
|
||||
|
||||
return true;
|
||||
@@ -167,7 +260,12 @@ namespace Unity.Netcode
|
||||
|
||||
if (networkManager.SpawnManager.SpawnedObjects.TryGetValue(NetworkObjectId, out NetworkObject networkObject))
|
||||
{
|
||||
var distributedAuthorityMode = networkManager.DistributedAuthorityMode;
|
||||
var ensureNetworkVariableLengthSafety = networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety;
|
||||
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(NetworkBehaviourIndex);
|
||||
var isServerAndDeltaForwarding = m_ReceivedMessageVersion >= k_ServerDeltaForwardingAndNetworkDelivery && networkManager.IsServer;
|
||||
var markNetworkVariableDirty = m_ReceivedMessageVersion >= k_ServerDeltaForwardingAndNetworkDelivery ? false : networkManager.IsServer;
|
||||
m_UpdatedNetworkVariables = new List<int>();
|
||||
|
||||
if (networkBehaviour == null)
|
||||
{
|
||||
@@ -178,7 +276,8 @@ namespace Unity.Netcode
|
||||
}
|
||||
else
|
||||
{
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
// DANGO TODO: Remove distributedAuthorityMode portion when we remove the service specific NetworkVariable stuff
|
||||
if (distributedAuthorityMode)
|
||||
{
|
||||
m_ReceivedNetworkVariableData.ReadValueSafe(out ushort variableCount);
|
||||
if (variableCount != networkBehaviour.NetworkVariableFields.Count)
|
||||
@@ -187,10 +286,30 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
// (For client-server) As opposed to worrying about adding additional processing on the server to send NetworkVariable
|
||||
// updates at the end of the frame, we now track all NetworkVariable state updates, per client, that need to be forwarded
|
||||
// to the client. This creates a list of all remaining connected clients that could have updates applied.
|
||||
if (isServerAndDeltaForwarding)
|
||||
{
|
||||
m_ForwardUpdates = new Dictionary<ulong, List<int>>();
|
||||
foreach (var clientId in networkManager.ConnectedClientsIds)
|
||||
{
|
||||
if (clientId == context.SenderId || clientId == networkManager.LocalClientId || !networkObject.Observers.Contains(clientId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
m_ForwardUpdates.Add(clientId, new List<int>());
|
||||
}
|
||||
}
|
||||
|
||||
// Update NetworkVariable Fields
|
||||
for (int i = 0; i < networkBehaviour.NetworkVariableFields.Count; i++)
|
||||
{
|
||||
int varSize = 0;
|
||||
if (networkManager.DistributedAuthorityMode)
|
||||
var networkVariable = networkBehaviour.NetworkVariableFields[i];
|
||||
|
||||
// DANGO TODO: Remove distributedAuthorityMode portion when we remove the service specific NetworkVariable stuff
|
||||
if (distributedAuthorityMode)
|
||||
{
|
||||
m_ReceivedNetworkVariableData.ReadValueSafe(out ushort variableSize);
|
||||
varSize = variableSize;
|
||||
@@ -200,10 +319,9 @@ namespace Unity.Netcode
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
|
||||
else if (ensureNetworkVariableLengthSafety)
|
||||
{
|
||||
ByteUnpacker.ReadValueBitPacked(m_ReceivedNetworkVariableData, out varSize);
|
||||
|
||||
if (varSize == 0)
|
||||
{
|
||||
continue;
|
||||
@@ -218,8 +336,6 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
var networkVariable = networkBehaviour.NetworkVariableFields[i];
|
||||
|
||||
if (networkManager.IsServer && !networkVariable.CanClientWrite(context.SenderId))
|
||||
{
|
||||
// we are choosing not to fire an exception here, because otherwise a malicious client could use this to crash the server
|
||||
@@ -247,13 +363,58 @@ namespace Unity.Netcode
|
||||
NetworkLog.LogError($"Client wrote to {typeof(NetworkVariable<>).Name} without permission. No more variables can be read. This is critical. => {nameof(NetworkObjectId)}: {NetworkObjectId} - {nameof(NetworkObject.GetNetworkBehaviourOrderIndex)}(): {networkObject.GetNetworkBehaviourOrderIndex(networkBehaviour)} - VariableIndex: {i}");
|
||||
NetworkLog.LogError($"[{networkVariable.GetType().Name}]");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
int readStartPos = m_ReceivedNetworkVariableData.Position;
|
||||
|
||||
// Read Delta so we also notify any subscribers to a change in the NetworkVariable
|
||||
networkVariable.ReadDelta(m_ReceivedNetworkVariableData, networkManager.IsServer);
|
||||
// DANGO TODO: Remove distributedAuthorityMode portion when we remove the service specific NetworkVariable stuff
|
||||
if (distributedAuthorityMode || ensureNetworkVariableLengthSafety)
|
||||
{
|
||||
var remainingBufferSize = m_ReceivedNetworkVariableData.Length - m_ReceivedNetworkVariableData.Position;
|
||||
if (varSize > (remainingBufferSize))
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[{networkBehaviour.name}][Delta State Read Error] Expecting to read {varSize} but only {remainingBufferSize} remains!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Added a try catch here to assure any failure will only fail on this one message and not disrupt the stack
|
||||
try
|
||||
{
|
||||
// Read the delta
|
||||
networkVariable.ReadDelta(m_ReceivedNetworkVariableData, markNetworkVariableDirty);
|
||||
|
||||
// Add the NetworkVariable field index so we can invoke the PostDeltaRead
|
||||
m_UpdatedNetworkVariables.Add(i);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// (For client-server) As opposed to worrying about adding additional processing on the server to send NetworkVariable
|
||||
// updates at the end of the frame, we now track all NetworkVariable state updates, per client, that need to be forwarded
|
||||
// to the client. This happens once the server is finished processing all state updates for this message.
|
||||
if (isServerAndDeltaForwarding)
|
||||
{
|
||||
foreach (var forwardEntry in m_ForwardUpdates)
|
||||
{
|
||||
// Only track things that the client can read
|
||||
if (networkVariable.CanClientRead(forwardEntry.Key))
|
||||
{
|
||||
// If the object is about to be shown to the client then don't send an update as it will
|
||||
// send a full update when shown.
|
||||
if (networkManager.SpawnManager.ObjectsToShowToClient.ContainsKey(forwardEntry.Key) &&
|
||||
networkManager.SpawnManager.ObjectsToShowToClient[forwardEntry.Key]
|
||||
.Contains(networkObject))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
forwardEntry.Value.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
networkManager.NetworkMetrics.TrackNetworkVariableDeltaReceived(
|
||||
context.SenderId,
|
||||
@@ -262,7 +423,8 @@ namespace Unity.Netcode
|
||||
networkBehaviour.__getTypeName(),
|
||||
context.MessageSize);
|
||||
|
||||
if (networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety || networkManager.DistributedAuthorityMode)
|
||||
// DANGO TODO: Remove distributedAuthorityMode portion when we remove the service specific NetworkVariable stuff
|
||||
if (distributedAuthorityMode || ensureNetworkVariableLengthSafety)
|
||||
{
|
||||
if (m_ReceivedNetworkVariableData.Position > (readStartPos + varSize))
|
||||
{
|
||||
@@ -284,6 +446,40 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are using the version of this message that includes network delivery, then
|
||||
// forward this update to all connected clients (other than the sender and the server).
|
||||
if (isServerAndDeltaForwarding)
|
||||
{
|
||||
var message = new NetworkVariableDeltaMessage()
|
||||
{
|
||||
NetworkBehaviour = networkBehaviour,
|
||||
NetworkBehaviourIndex = NetworkBehaviourIndex,
|
||||
NetworkObjectId = NetworkObjectId,
|
||||
m_ForwardingMessage = true,
|
||||
m_ForwardUpdates = m_ForwardUpdates,
|
||||
};
|
||||
|
||||
foreach (var forwardEntry in m_ForwardUpdates)
|
||||
{
|
||||
// Only forward updates to any client that has visibility to the state updates included in this message
|
||||
if (forwardEntry.Value.Count > 0)
|
||||
{
|
||||
message.TargetClientId = forwardEntry.Key;
|
||||
networkManager.ConnectionManager.SendMessage(ref message, NetworkDelivery, forwardEntry.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This should be always invoked (client & server) to assure the previous values are set
|
||||
// !! IMPORTANT ORDER OF OPERATIONS !! (Has to happen after forwarding deltas)
|
||||
// When a server forwards delta updates to connected clients, it needs to preserve the previous value
|
||||
// until it is done serializing all valid NetworkVariable field deltas (relative to each client). This
|
||||
// is invoked after it is done forwarding the deltas.
|
||||
foreach (var fieldIndex in m_UpdatedNetworkVariables)
|
||||
{
|
||||
networkBehaviour.NetworkVariableFields[fieldIndex].PostDeltaRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user