com.unity.netcode.gameobjects@2.0.0-exp.2
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.0.0-exp.2] - 2024-04-02 ### Added - Added updates to all internal messages to account for a distributed authority network session connection. (#2863) - Added `NetworkRigidbodyBase` that provides users with a more customizable network rigidbody, handles both `Rigidbody` and `Rigidbody2D`, and provides an option to make `NetworkTransform` use the rigid body for motion. (#2863) - For a customized `NetworkRigidbodyBase` class: - `NetworkRigidbodyBase.AutoUpdateKinematicState` provides control on whether the kinematic setting will be automatically set or not when ownership changes. - `NetworkRigidbodyBase.AutoSetKinematicOnDespawn` provides control on whether isKinematic will automatically be set to true when the associated `NetworkObject` is despawned. - `NetworkRigidbodyBase.Initialize` is a protected method that, when invoked, will initialize the instance. This includes options to: - Set whether using a `RigidbodyTypes.Rigidbody` or `RigidbodyTypes.Rigidbody2D`. - Includes additional optional parameters to set the `NetworkTransform`, `Rigidbody`, and `Rigidbody2d` to use. - Provides additional public methods: - `NetworkRigidbodyBase.GetPosition` to return the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.GetRotation` to return the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.MovePosition` to move to the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.MoveRotation` to move to the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.Move` to move to the position and rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.Move` to move to the position and rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.SetPosition` to set the position of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.SetRotation` to set the rotation of the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting). - `NetworkRigidbodyBase.ApplyCurrentTransform` to set the position and rotation of the `Rigidbody` or `Rigidbody2d` based on the associated `GameObject` transform (depending upon its initialized setting). - `NetworkRigidbodyBase.WakeIfSleeping` to wake up the rigid body if sleeping. - `NetworkRigidbodyBase.SleepRigidbody` to put the rigid body to sleep. - `NetworkRigidbodyBase.IsKinematic` to determine if the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) is currently kinematic. - `NetworkRigidbodyBase.SetIsKinematic` to set the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) current kinematic state. - `NetworkRigidbodyBase.ResetInterpolation` to reset the `Rigidbody` or `Rigidbody2d` (depending upon its initialized setting) back to its original interpolation value when initialized. - Now includes a `MonoBehaviour.FixedUpdate` implementation that will update the assigned `NetworkTransform` when `NetworkRigidbodyBase.UseRigidBodyForMotion` is true. (#2863) - Added `RigidbodyContactEventManager` that provides a more optimized way to process collision enter and collision stay events as opposed to the `Monobehaviour` approach. (#2863) - Can be used in client-server and distributed authority modes, but is particularly useful in distributed authority. - Added rigid body motion updates to `NetworkTransform` which allows users to set interolation on rigid bodies. (#2863) - Extrapolation is only allowed on authoritative instances, but custom class derived from `NetworkRigidbodyBase` or `NetworkRigidbody` or `NetworkRigidbody2D` automatically switches non-authoritative instances to interpolation if set to extrapolation. - Added distributed authority mode support to `NetworkAnimator`. (#2863) - Added session mode selection to `NetworkManager` inspector view. (#2863) - Added distributed authority permissions feature. (#2863) - Added distributed authority mode specific `NetworkObject` permissions flags (Distributable, Transferable, and RequestRequired). (#2863) - Added distributed authority mode specific `NetworkObject.SetOwnershipStatus` method that applies one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863) - Added distributed authority mode specific `NetworkObject.RemoveOwnershipStatus` method that removes one or more `NetworkObject` instance's ownership flags. If updated when spawned, the ownership permission changes are synchronized with the other connected clients. (#2863) - Added distributed authority mode specific `NetworkObject.HasOwnershipStatus` method that will return (true or false) whether one or more ownership flags is set. (#2863) - Added distributed authority mode specific `NetworkObject.SetOwnershipLock` method that locks ownership of a `NetworkObject` to prevent ownership from changing until the current owner releases the lock. (#2863) - Added distributed authority mode specific `NetworkObject.RequestOwnership` method that sends an ownership request to the current owner of a spawned `NetworkObject` instance. (#2863) - Added distributed authority mode specific `NetworkObject.OnOwnershipRequested` callback handler that is invoked on the owner/authoritative side when a non-owner requests ownership. Depending upon the boolean returned value depends upon whether the request is approved or denied. (#2863) - Added distributed authority mode specific `NetworkObject.OnOwnershipRequestResponse` callback handler that is invoked when a non-owner's request has been processed. This callback includes a `NetworkObjet.OwnershipRequestResponseStatus` response parameter that describes whether the request was approved or the reason why it was not approved. (#2863) - Added distributed authority mode specific `NetworkObject.DeferDespawn` method that defers the despawning of `NetworkObject` instances on non-authoritative clients based on the tick offset parameter. (#2863) - Added distributed authority mode specific `NetworkObject.OnDeferredDespawnComplete` callback handler that can be used to further control when deferring the despawning of a `NetworkObject` on non-authoritative instances. (#2863) - Added `NetworkClient.SessionModeType` as one way to determine the current session mode of the network session a client is connected to. (#2863) - Added distributed authority mode specific `NetworkClient.IsSessionOwner` property to determine if the current local client is the current session owner of a distributed authority session. (#2863) - Added distributed authority mode specific client side spawning capabilities. When running in distributed authority mode, clients can instantiate and spawn `NetworkObject` instances (the local client is authomatically the owner of the spawned object). (#2863) - This is useful to better visually synchronize owner authoritative motion models and newly spawned `NetworkObject` instances (i.e. projectiles for example). - Added distributed authority mode specific client side player spawning capabilities. Clients will automatically spawn their associated player object locally. (#2863) - Added distributed authority mode specific `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property (default is true) to provide control over the automatic spawning of player prefabs on the local client side. (#2863) - Added distributed authority mode specific `NetworkManager.OnFetchLocalPlayerPrefabToSpawn` callback that, when assigned, will allow the local client to provide the player prefab to be spawned for the local client. (#2863) - This is only invoked if the `NetworkConfig.AutoSpawnPlayerPrefabClientSide` property is set to true. - Added distributed authority mode specific `NetworkBehaviour.HasAuthority` property that determines if the local client has authority over the associated `NetworkObject` instance (typical use case is within a `NetworkBehaviour` script much like that of `IsServer` or `IsClient`). (#2863) - Added distributed authority mode specific `NetworkBehaviour.IsSessionOwner` property that determines if the local client is the session owner (typical use case would be to determine if the local client can has scene management authority within a `NetworkBehaviour` script). (#2863) - Added support for distributed authority mode scene management where the currently assigned session owner can start scene events (i.e. scene loading and scene unloading). (#2863) ### Fixed - Fixed issue where the host was not invoking `OnClientDisconnectCallback` for its own local client when internally shutting down. (#2822) - Fixed issue where NetworkTransform could potentially attempt to "unregister" a named message prior to it being registered. (#2807) - Fixed issue where in-scene placed `NetworkObject`s with complex nested children `NetworkObject`s (more than one child in depth) would not synchronize properly if WorldPositionStays was set to true. (#2796) ### Changed - Changed client side awareness of other clients is now the same as a server or host. (#2863) - Changed `NetworkManager.ConnectedClients` can now be accessed by both server and clients. (#2863) - Changed `NetworkManager.ConnectedClientsList` can now be accessed by both server and clients. (#2863) - Changed `NetworkTransform` defaults to owner authoritative when connected to a distributed authority session. (#2863) - Changed `NetworkVariable` defaults to owner write and everyone read permissions when connected to a distributed authority session (even if declared with server read or write permissions). (#2863) - Changed `NetworkObject` no longer implements the `MonoBehaviour.Update` method in order to determine whether a `NetworkObject` instance has been migrated to a different scene. Instead, only `NetworkObjects` with the `SceneMigrationSynchronization` property set will be updated internally during the `NetworkUpdateStage.PostLateUpdate` by `NetworkManager`. (#2863) - Changed `NetworkManager` inspector view layout where properties are now organized by category. (#2863) - Changed `NetworkTransform` to now use `NetworkTransformMessage` as opposed to named messages for NetworkTransformState updates. (#2810) - Changed `CustomMessageManager` so it no longer attempts to register or "unregister" a null or empty string and will log an error if this condition occurs. (#2807)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Unity.Collections;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
@@ -114,7 +115,10 @@ namespace Unity.Netcode
|
||||
|
||||
/// Only used for <see cref="SceneEventType.Synchronize"/> scene events, this assures permissions when writing
|
||||
/// NetworkVariable information. If that process changes, then we need to update this
|
||||
/// In distributed authority mode this is used to route messages to the appropriate destination client
|
||||
internal ulong TargetClientId;
|
||||
/// Only used with a DAHost
|
||||
internal ulong SenderClientId;
|
||||
|
||||
private Dictionary<uint, List<NetworkObject>> m_SceneNetworkObjects;
|
||||
private Dictionary<uint, long> m_SceneNetworkObjectDataOffsets;
|
||||
@@ -243,8 +247,76 @@ namespace Unity.Netcode
|
||||
{
|
||||
SceneHandlesToSynchronize.Clear();
|
||||
}
|
||||
ForwardSynchronization = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used with SortParentedNetworkObjects to sort the children of the root parent NetworkObject
|
||||
/// </summary>
|
||||
/// <param name="first">object to be sorted</param>
|
||||
/// <param name="second">object to be compared to for sorting the first object</param>
|
||||
/// <returns></returns>
|
||||
private int SortChildrenNetworkObjects(NetworkObject first, NetworkObject second)
|
||||
{
|
||||
var firstParent = first.GetCachedParent()?.GetComponent<NetworkObject>();
|
||||
// If the second is the first's parent then move the first down
|
||||
if (firstParent != null && firstParent == second)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
var secondParent = second.GetCachedParent()?.GetComponent<NetworkObject>();
|
||||
// If the first is the second's parent then move the first up
|
||||
if (secondParent != null && secondParent == first)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Otherwise, don't move the first at all
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the synchronization order of the NetworkObjects to be serialized
|
||||
/// by parents before children order
|
||||
/// </summary>
|
||||
private void SortParentedNetworkObjects()
|
||||
{
|
||||
var networkObjectList = m_NetworkObjectsSync.ToList();
|
||||
foreach (var networkObject in networkObjectList)
|
||||
{
|
||||
// Find only the root parent NetworkObjects
|
||||
if (networkObject.transform.childCount > 0 && networkObject.transform.parent == null)
|
||||
{
|
||||
// Get all child NetworkObjects of the root
|
||||
var childNetworkObjects = networkObject.GetComponentsInChildren<NetworkObject>().ToList();
|
||||
|
||||
childNetworkObjects.Sort(SortChildrenNetworkObjects);
|
||||
|
||||
// Remove the root from the children list
|
||||
childNetworkObjects.Remove(networkObject);
|
||||
|
||||
// Remove the root's children from the primary list
|
||||
foreach (var childObject in childNetworkObjects)
|
||||
{
|
||||
m_NetworkObjectsSync.Remove(childObject);
|
||||
}
|
||||
// Insert or Add the sorted children list
|
||||
var nextIndex = m_NetworkObjectsSync.IndexOf(networkObject) + 1;
|
||||
if (nextIndex == m_NetworkObjectsSync.Count)
|
||||
{
|
||||
m_NetworkObjectsSync.AddRange(childNetworkObjects);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NetworkObjectsSync.InsertRange(nextIndex, childNetworkObjects);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool LogSerializationOrder = false;
|
||||
|
||||
internal void AddSpawnedNetworkObjects()
|
||||
{
|
||||
m_NetworkObjectsSync.Clear();
|
||||
@@ -256,22 +328,22 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by parents before children
|
||||
m_NetworkObjectsSync.Sort(SortParentedNetworkObjects);
|
||||
|
||||
// Sort by INetworkPrefabInstanceHandler implementation before the
|
||||
// NetworkObjects spawned by the implementation
|
||||
m_NetworkObjectsSync.Sort(SortNetworkObjects);
|
||||
|
||||
// The last thing we sort is parents before children
|
||||
SortParentedNetworkObjects();
|
||||
|
||||
// This is useful to know what NetworkObjects a client is going to be synchronized with
|
||||
// as well as the order in which they will be deserialized
|
||||
if (m_NetworkManager.LogLevel == LogLevel.Developer)
|
||||
if (LogSerializationOrder && m_NetworkManager.LogLevel == LogLevel.Developer)
|
||||
{
|
||||
var messageBuilder = new System.Text.StringBuilder(0xFFFF);
|
||||
messageBuilder.Append("[Server-Side Client-Synchronization] NetworkObject serialization order:");
|
||||
var messageBuilder = new StringBuilder(0xFFFF);
|
||||
messageBuilder.AppendLine("[Server-Side Client-Synchronization] NetworkObject serialization order:");
|
||||
foreach (var networkObject in m_NetworkObjectsSync)
|
||||
{
|
||||
messageBuilder.Append($"{networkObject.name}");
|
||||
messageBuilder.AppendLine($"{networkObject.name}");
|
||||
}
|
||||
NetworkLog.LogInfo(messageBuilder.ToString());
|
||||
}
|
||||
@@ -362,31 +434,35 @@ namespace Unity.Netcode
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the synchronization order of the NetworkObjects to be serialized
|
||||
/// by parents before children.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This only handles late joining players. Spawning and nesting several children
|
||||
/// dynamically is still handled by the orphaned child list when deserialized out of
|
||||
/// hierarchical order (i.e. Spawn parent and child dynamically, parent message is
|
||||
/// dropped and re-sent but child object is received and processed)
|
||||
/// </remarks>
|
||||
private int SortParentedNetworkObjects(NetworkObject first, NetworkObject second)
|
||||
internal bool EnableSerializationLogs = false;
|
||||
|
||||
private void LogArray(byte[] data, int start = 0, int stop = 0, StringBuilder builder = null)
|
||||
{
|
||||
// If the first has a parent, move the first down
|
||||
if (first.transform.parent != null)
|
||||
var usingExternalBuilder = builder != null;
|
||||
if (!usingExternalBuilder)
|
||||
{
|
||||
return 1;
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
else // If the second has a parent and the first does not, then move the first up
|
||||
if (second.transform.parent != null)
|
||||
|
||||
if (stop == 0)
|
||||
{
|
||||
return -1;
|
||||
stop = data.Length;
|
||||
}
|
||||
|
||||
builder.AppendLine($"[Start Data Dump][Start = {start}][Stop = {stop}] Size ({stop - start})");
|
||||
for (int i = start; i < stop; i++)
|
||||
{
|
||||
builder.Append($"{data[i]:X2} ");
|
||||
}
|
||||
builder.Append("\n");
|
||||
|
||||
if (!usingExternalBuilder)
|
||||
{
|
||||
UnityEngine.Debug.Log(builder.ToString());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal bool ForwardSynchronization;
|
||||
|
||||
/// <summary>
|
||||
/// Client and Server Side:
|
||||
@@ -398,6 +474,12 @@ namespace Unity.Netcode
|
||||
// Write the scene event type
|
||||
writer.WriteValueSafe(SceneEventType);
|
||||
|
||||
if (m_NetworkManager.DistributedAuthorityMode)
|
||||
{
|
||||
BytePacker.WriteValueBitPacked(writer, TargetClientId);
|
||||
BytePacker.WriteValueBitPacked(writer, SenderClientId);
|
||||
}
|
||||
|
||||
if (SceneEventType == SceneEventType.ActiveSceneChanged)
|
||||
{
|
||||
writer.WriteValueSafe(ActiveSceneHash);
|
||||
@@ -432,12 +514,25 @@ namespace Unity.Netcode
|
||||
case SceneEventType.Synchronize:
|
||||
{
|
||||
writer.WriteValueSafe(ActiveSceneHash);
|
||||
|
||||
WriteSceneSynchronizationData(writer);
|
||||
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
LogArray(writer.ToArray(), 0, writer.Length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SceneEventType.Load:
|
||||
{
|
||||
SerializeScenePlacedObjects(writer);
|
||||
if (m_NetworkManager.DistributedAuthorityMode && IsForwarding && m_NetworkManager.DAHost)
|
||||
{
|
||||
CopyInternalBuffer(ref writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializeScenePlacedObjects(writer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SceneEventType.SynchronizeComplete:
|
||||
@@ -459,6 +554,11 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void CopyInternalBuffer(ref FastBufferWriter writer)
|
||||
{
|
||||
writer.WriteBytesSafe(InternalBuffer.GetUnsafePtrAtCurrentPosition(), InternalBuffer.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server Side:
|
||||
/// Called at the end of a <see cref="SceneEventType.Load"/> event once the scene is loaded and scene placed NetworkObjects
|
||||
@@ -466,13 +566,29 @@ namespace Unity.Netcode
|
||||
/// </summary>
|
||||
internal void WriteSceneSynchronizationData(FastBufferWriter writer)
|
||||
{
|
||||
var builder = (StringBuilder)null;
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
builder = new StringBuilder();
|
||||
builder.AppendLine($"[Write][Synchronize-Start][WPos: {writer.Position}] Begin:");
|
||||
}
|
||||
// Write the scenes we want to load, in the order we want to load them
|
||||
writer.WriteValueSafe(ScenesToSynchronize.ToArray());
|
||||
writer.WriteValueSafe(SceneHandlesToSynchronize.ToArray());
|
||||
|
||||
// Store our current position in the stream to come back and say how much data we have written
|
||||
var positionStart = writer.Position;
|
||||
|
||||
if (m_NetworkManager.DistributedAuthorityMode && ForwardSynchronization && m_NetworkManager.DAHost)
|
||||
{
|
||||
writer.WriteValueSafe(m_InternalBufferSize);
|
||||
CopyInternalBuffer(ref writer);
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
LogArray(writer.ToArray(), positionStart);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Size Place Holder -- Start
|
||||
// !!NOTE!!: Since this is a placeholder to be set after we know how much we have written,
|
||||
// for stream offset purposes this MUST not be a packed value!
|
||||
@@ -481,15 +597,34 @@ namespace Unity.Netcode
|
||||
|
||||
// Write the number of NetworkObjects we are serializing
|
||||
writer.WriteValueSafe(m_NetworkObjectsSync.Count);
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
builder.AppendLine($"[Synchronize Objects][positionStart: {positionStart}][WPos: {writer.Position}][NO-Count: {m_NetworkObjectsSync.Count}] Begin:");
|
||||
}
|
||||
var distributedAuthority = m_NetworkManager.DistributedAuthorityMode;
|
||||
|
||||
// Serialize all NetworkObjects that are spawned
|
||||
for (var i = 0; i < m_NetworkObjectsSync.Count; ++i)
|
||||
{
|
||||
var networkObject = m_NetworkObjectsSync[i];
|
||||
var noStart = writer.Position;
|
||||
var sceneObject = m_NetworkObjectsSync[i].GetMessageSceneObject(TargetClientId);
|
||||
// In distributed authority mode, we send the currently known observers of each NetworkObject to the client being synchronized.
|
||||
var sceneObject = m_NetworkObjectsSync[i].GetMessageSceneObject(TargetClientId, distributedAuthority);
|
||||
|
||||
sceneObject.Serialize(writer);
|
||||
var noStop = writer.Position;
|
||||
totalBytes += noStop - noStart;
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
var offStart = noStart - (positionStart + sizeof(int));
|
||||
var offStop = noStop - (positionStart + sizeof(int));
|
||||
builder.AppendLine($"[Head: {offStart}][Tail: {offStop}][Size: {offStop - offStart}][{networkObject.name}][NID-{networkObject.NetworkObjectId}][Children: {networkObject.ChildNetworkBehaviours.Count}]");
|
||||
LogArray(writer.ToArray(), noStart, noStop, builder);
|
||||
}
|
||||
}
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
UnityEngine.Debug.Log(builder.ToString());
|
||||
}
|
||||
|
||||
// Write the number of despawned in-scene placed NetworkObjects
|
||||
@@ -511,6 +646,10 @@ namespace Unity.Netcode
|
||||
// Write the total size written to the stream by NetworkObjects being serialized
|
||||
writer.WriteValueSafe(bytesWritten);
|
||||
writer.Seek(positionEnd);
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
LogArray(writer.ToArray(), positionStart);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -526,6 +665,7 @@ namespace Unity.Netcode
|
||||
|
||||
// Write our count place holder (must not be packed!)
|
||||
writer.WriteValueSafe((ushort)0);
|
||||
var distributedAuthority = m_NetworkManager.DistributedAuthorityMode;
|
||||
|
||||
foreach (var keyValuePairByGlobalObjectIdHash in m_NetworkManager.SceneManager.ScenePlacedObjects)
|
||||
{
|
||||
@@ -534,7 +674,7 @@ namespace Unity.Netcode
|
||||
if (keyValuePairBySceneHandle.Value.Observers.Contains(TargetClientId))
|
||||
{
|
||||
// Serialize the NetworkObject
|
||||
var sceneObject = keyValuePairBySceneHandle.Value.GetMessageSceneObject(TargetClientId);
|
||||
var sceneObject = keyValuePairBySceneHandle.Value.GetMessageSceneObject(TargetClientId, distributedAuthority);
|
||||
sceneObject.Serialize(writer);
|
||||
numberOfObjects++;
|
||||
}
|
||||
@@ -567,6 +707,12 @@ namespace Unity.Netcode
|
||||
internal void Deserialize(FastBufferReader reader)
|
||||
{
|
||||
reader.ReadValueSafe(out SceneEventType);
|
||||
if (m_NetworkManager.DistributedAuthorityMode)
|
||||
{
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out TargetClientId);
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out SenderClientId);
|
||||
}
|
||||
|
||||
if (SceneEventType == SceneEventType.ActiveSceneChanged)
|
||||
{
|
||||
reader.ReadValueSafe(out ActiveSceneHash);
|
||||
@@ -607,6 +753,10 @@ namespace Unity.Netcode
|
||||
case SceneEventType.Synchronize:
|
||||
{
|
||||
reader.ReadValueSafe(out ActiveSceneHash);
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
LogArray(reader.ToArray(), 0, reader.Length);
|
||||
}
|
||||
CopySceneSynchronizationData(reader);
|
||||
break;
|
||||
}
|
||||
@@ -641,6 +791,8 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
private int m_InternalBufferSize;
|
||||
|
||||
/// <summary>
|
||||
/// Client Side:
|
||||
/// Prepares for a scene synchronization event and copies the scene synchronization data
|
||||
@@ -657,6 +809,8 @@ namespace Unity.Netcode
|
||||
|
||||
// is not packed!
|
||||
reader.ReadValueSafe(out int sizeToCopy);
|
||||
m_InternalBufferSize = sizeToCopy;
|
||||
|
||||
unsafe
|
||||
{
|
||||
if (!reader.TryBeginRead(sizeToCopy))
|
||||
@@ -667,6 +821,10 @@ namespace Unity.Netcode
|
||||
m_HasInternalBuffer = true;
|
||||
// We use Allocator.Persistent since scene synchronization will most likely take longer than 4 frames
|
||||
InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.Persistent, sizeToCopy);
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
LogArray(InternalBuffer.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -916,12 +1074,24 @@ namespace Unity.Netcode
|
||||
/// <param name="networkManager"></param>
|
||||
internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager)
|
||||
{
|
||||
var builder = (StringBuilder)null;
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
builder = new StringBuilder();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Process all spawned NetworkObjects for this network session
|
||||
InternalBuffer.ReadValueSafe(out int newObjectsCount);
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
builder.AppendLine($"[Read][Synchronize Objects][WPos: {InternalBuffer.Position}][NO-Count: {newObjectsCount}] Begin:");
|
||||
}
|
||||
|
||||
for (int i = 0; i < newObjectsCount; i++)
|
||||
{
|
||||
var noStart = InternalBuffer.Position;
|
||||
var sceneObject = new NetworkObject.SceneObject();
|
||||
sceneObject.Deserialize(InternalBuffer);
|
||||
|
||||
@@ -932,6 +1102,12 @@ namespace Unity.Netcode
|
||||
}
|
||||
var spawnedNetworkObject = NetworkObject.AddSceneObject(sceneObject, InternalBuffer, networkManager);
|
||||
|
||||
var noStop = InternalBuffer.Position;
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
builder.AppendLine($"[Head: {noStart}][Tail: {noStop}][Size: {noStop - noStart}][{spawnedNetworkObject.name}][NID-{spawnedNetworkObject.NetworkObjectId}][Children: {spawnedNetworkObject.ChildNetworkBehaviours.Count}]");
|
||||
LogArray(InternalBuffer.ToArray(), noStart, noStop, builder);
|
||||
}
|
||||
// If we failed to deserialize the NetowrkObject then don't add null to the list
|
||||
if (spawnedNetworkObject != null)
|
||||
{
|
||||
@@ -941,11 +1117,20 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
}
|
||||
if (EnableSerializationLogs)
|
||||
{
|
||||
UnityEngine.Debug.Log(builder.ToString());
|
||||
}
|
||||
|
||||
// Now deserialize the despawned in-scene placed NetworkObjects list (if any)
|
||||
DeserializeDespawnedInScenePlacedNetworkObjects();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogException(ex);
|
||||
UnityEngine.Debug.Log(builder.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
InternalBuffer.Dispose();
|
||||
@@ -999,24 +1184,38 @@ namespace Unity.Netcode
|
||||
/// Serialize scene handles and associated NetworkObjects that were migrated
|
||||
/// into a new scene.
|
||||
/// </summary>
|
||||
internal bool IsForwarding;
|
||||
private ulong m_OwnerId;
|
||||
|
||||
private void SerializeObjectsMovedIntoNewScene(FastBufferWriter writer)
|
||||
{
|
||||
var sceneManager = m_NetworkManager.SceneManager;
|
||||
var ownerId = m_NetworkManager.LocalClientId;
|
||||
if (IsForwarding)
|
||||
{
|
||||
ownerId = m_OwnerId;
|
||||
}
|
||||
|
||||
// Write the owner identifier
|
||||
writer.WriteValueSafe(ownerId);
|
||||
|
||||
// Write the number of scene handles
|
||||
writer.WriteValueSafe(sceneManager.ObjectsMigratedIntoNewScene.Count);
|
||||
foreach (var sceneHandleObjects in sceneManager.ObjectsMigratedIntoNewScene)
|
||||
{
|
||||
if (!sceneManager.ObjectsMigratedIntoNewScene[sceneHandleObjects.Key].ContainsKey(ownerId))
|
||||
{
|
||||
throw new Exception($"Trying to send object scene migration for Client-{ownerId} but the client has no entries to send!");
|
||||
}
|
||||
// Write the scene handle
|
||||
writer.WriteValueSafe(sceneHandleObjects.Key);
|
||||
// Write the number of NetworkObjectIds to expect
|
||||
writer.WriteValueSafe(sceneHandleObjects.Value.Count);
|
||||
foreach (var networkObject in sceneHandleObjects.Value)
|
||||
writer.WriteValueSafe(sceneHandleObjects.Value[ownerId].Count);
|
||||
foreach (var networkObject in sceneHandleObjects.Value[ownerId])
|
||||
{
|
||||
writer.WriteValueSafe(networkObject.NetworkObjectId);
|
||||
}
|
||||
}
|
||||
// Once we are done, clear the table
|
||||
sceneManager.ObjectsMigratedIntoNewScene.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1027,17 +1226,30 @@ namespace Unity.Netcode
|
||||
{
|
||||
var sceneManager = m_NetworkManager.SceneManager;
|
||||
var spawnManager = m_NetworkManager.SpawnManager;
|
||||
// Just always assure this has no entries
|
||||
sceneManager.ObjectsMigratedIntoNewScene.Clear();
|
||||
|
||||
var numberOfScenes = 0;
|
||||
var sceneHandle = 0;
|
||||
var objectCount = 0;
|
||||
var networkObjectId = (ulong)0;
|
||||
|
||||
var ownerID = (ulong)0;
|
||||
reader.ReadValueSafe(out ownerID);
|
||||
m_OwnerId = ownerID;
|
||||
reader.ReadValueSafe(out numberOfScenes);
|
||||
|
||||
for (int i = 0; i < numberOfScenes; i++)
|
||||
{
|
||||
reader.ReadValueSafe(out sceneHandle);
|
||||
sceneManager.ObjectsMigratedIntoNewScene.Add(sceneHandle, new List<NetworkObject>());
|
||||
if (!sceneManager.ObjectsMigratedIntoNewScene.ContainsKey(sceneHandle))
|
||||
{
|
||||
sceneManager.ObjectsMigratedIntoNewScene.Add(sceneHandle, new Dictionary<ulong, List<NetworkObject>>());
|
||||
}
|
||||
|
||||
if (!sceneManager.ObjectsMigratedIntoNewScene[sceneHandle].ContainsKey(ownerID))
|
||||
{
|
||||
sceneManager.ObjectsMigratedIntoNewScene[sceneHandle].Add(ownerID, new List<NetworkObject>());
|
||||
}
|
||||
|
||||
reader.ReadValueSafe(out objectCount);
|
||||
for (int j = 0; j < objectCount; j++)
|
||||
{
|
||||
@@ -1047,9 +1259,9 @@ namespace Unity.Netcode
|
||||
NetworkLog.LogError($"[Object Scene Migration] Trying to synchronize NetworkObjectId ({networkObjectId}) but it was not spawned or no longer exists!!");
|
||||
continue;
|
||||
}
|
||||
var networkObject = spawnManager.SpawnedObjects[networkObjectId];
|
||||
// Add NetworkObject scene migration to ObjectsMigratedIntoNewScene dictionary that is processed
|
||||
//
|
||||
sceneManager.ObjectsMigratedIntoNewScene[sceneHandle].Add(spawnManager.SpawnedObjects[networkObjectId]);
|
||||
sceneManager.ObjectsMigratedIntoNewScene[sceneHandle][ownerID].Add(networkObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1065,16 +1277,22 @@ namespace Unity.Netcode
|
||||
{
|
||||
var sceneManager = m_NetworkManager.SceneManager;
|
||||
var spawnManager = m_NetworkManager.SpawnManager;
|
||||
var ownerId = (ulong)0;
|
||||
var numberOfScenes = 0;
|
||||
var sceneHandle = 0;
|
||||
var objectCount = 0;
|
||||
var networkObjectId = (ulong)0;
|
||||
|
||||
reader.ReadValueSafe(out ownerId);
|
||||
|
||||
|
||||
var deferredObjectsMovedEvent = new NetworkSceneManager.DeferredObjectsMovedEvent()
|
||||
{
|
||||
ObjectsMigratedTable = new Dictionary<int, List<ulong>>()
|
||||
OwnerId = ownerId,
|
||||
ObjectsMigratedTable = new Dictionary<int, List<ulong>>(),
|
||||
};
|
||||
|
||||
|
||||
reader.ReadValueSafe(out numberOfScenes);
|
||||
for (int i = 0; i < numberOfScenes; i++)
|
||||
{
|
||||
@@ -1104,8 +1322,13 @@ namespace Unity.Netcode
|
||||
{
|
||||
if (!sceneManager.ObjectsMigratedIntoNewScene.ContainsKey(keyEntry.Key))
|
||||
{
|
||||
sceneManager.ObjectsMigratedIntoNewScene.Add(keyEntry.Key, new List<NetworkObject>());
|
||||
sceneManager.ObjectsMigratedIntoNewScene.Add(keyEntry.Key, new Dictionary<ulong, List<NetworkObject>>());
|
||||
}
|
||||
if (!sceneManager.ObjectsMigratedIntoNewScene[keyEntry.Key].ContainsKey(objectsMovedEvent.OwnerId))
|
||||
{
|
||||
sceneManager.ObjectsMigratedIntoNewScene[keyEntry.Key].Add(objectsMovedEvent.OwnerId, new List<NetworkObject>());
|
||||
}
|
||||
|
||||
foreach (var objectId in keyEntry.Value)
|
||||
{
|
||||
if (!spawnManager.SpawnedObjects.ContainsKey(objectId))
|
||||
@@ -1114,22 +1337,15 @@ namespace Unity.Netcode
|
||||
continue;
|
||||
}
|
||||
var networkObject = spawnManager.SpawnedObjects[objectId];
|
||||
if (!sceneManager.ObjectsMigratedIntoNewScene[keyEntry.Key].Contains(networkObject))
|
||||
if (!sceneManager.ObjectsMigratedIntoNewScene[keyEntry.Key][objectsMovedEvent.OwnerId].Contains(networkObject))
|
||||
{
|
||||
sceneManager.ObjectsMigratedIntoNewScene[keyEntry.Key].Add(networkObject);
|
||||
sceneManager.ObjectsMigratedIntoNewScene[keyEntry.Key][objectsMovedEvent.OwnerId].Add(networkObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
objectsMovedEvent.ObjectsMigratedTable.Clear();
|
||||
}
|
||||
|
||||
sceneManager.DeferredObjectsMovedEvents.Clear();
|
||||
|
||||
// If there are any pending objects to migrate, then migrate them
|
||||
if (sceneManager.ObjectsMigratedIntoNewScene.Count > 0)
|
||||
{
|
||||
sceneManager.MigrateNetworkObjectsIntoScenes();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user