com.unity.netcode.gameobjects@1.2.0
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). ## [1.2.0] - 2022-11-21 ### Added - Added protected method `NetworkBehaviour.OnSynchronize` which is invoked during the initial `NetworkObject` synchronization process. This provides users the ability to include custom serialization information that will be applied to the `NetworkBehaviour` prior to the `NetworkObject` being spawned. (#2298) - Added support for different versions of the SDK to talk to each other in circumstances where changes permit it. Starting with this version and into future versions, patch versions should be compatible as long as the minor version is the same. (#2290) - Added `NetworkObject` auto-add helper and Multiplayer Tools install reminder settings to Project Settings. (#2285) - Added `public string DisconnectReason` getter to `NetworkManager` and `string Reason` to `ConnectionApprovalResponse`. Allows connection approval to communicate back a reason. Also added `public void DisconnectClient(ulong clientId, string reason)` allowing setting a disconnection reason, when explicitly disconnecting a client. (#2280) ### Changed - Changed 3rd-party `XXHash` (32 & 64) implementation with an in-house reimplementation (#2310) - When `NetworkConfig.EnsureNetworkVariableLengthSafety` is disabled `NetworkVariable` fields do not write the additional `ushort` size value (_which helps to reduce the total synchronization message size_), but when enabled it still writes the additional `ushort` value. (#2298) - Optimized bandwidth usage by encoding most integer fields using variable-length encoding. (#2276) ### Fixed - Fixed issue where `NetworkTransform` components nested under a parent with a `NetworkObject` component (i.e. network prefab) would not have their associated `GameObject`'s transform synchronized. (#2298) - Fixed issue where `NetworkObject`s that failed to instantiate could cause the entire synchronization pipeline to be disrupted/halted for a connecting client. (#2298) - Fixed issue where in-scene placed `NetworkObject`s nested under a `GameObject` would be added to the orphaned children list causing continual console warning log messages. (#2298) - Custom messages are now properly received by the local client when they're sent while running in host mode. (#2296) - Fixed issue where the host would receive more than one event completed notification when loading or unloading a scene only when no clients were connected. (#2292) - Fixed an issue in `UnityTransport` where an error would be logged if the 'Use Encryption' flag was enabled with a Relay configuration that used a secure protocol. (#2289) - Fixed issue where in-scene placed `NetworkObjects` were not honoring the `AutoObjectParentSync` property. (#2281) - Fixed the issue where `NetworkManager.OnClientConnectedCallback` was being invoked before in-scene placed `NetworkObject`s had been spawned when starting `NetworkManager` as a host. (#2277) - Creating a `FastBufferReader` with `Allocator.None` will not result in extra memory being allocated for the buffer (since it's owned externally in that scenario). (#2265) ### Removed - Removed the `NetworkObject` auto-add and Multiplayer Tools install reminder settings from the Menu interface. (#2285)
This commit is contained in:
@@ -830,6 +830,9 @@ namespace Unity.Netcode
|
||||
// parent and then re-parents the child under a GameObject with a NetworkObject component attached.
|
||||
if (parentNetworkObject == null)
|
||||
{
|
||||
// If we are parented under a GameObject, go ahead and mark the world position stays as false
|
||||
// so clients synchronize their transform in local space. (only for in-scene placed NetworkObjects)
|
||||
m_CachedWorldPositionStays = false;
|
||||
return true;
|
||||
}
|
||||
else // If the parent still isn't spawned add this to the orphaned children and return false
|
||||
@@ -841,8 +844,11 @@ namespace Unity.Netcode
|
||||
else
|
||||
{
|
||||
// If we made it this far, go ahead and set the network parenting values
|
||||
// with the default WorldPoisitonSays value
|
||||
SetNetworkParenting(parentNetworkObject.NetworkObjectId, true);
|
||||
// with the WorldPoisitonSays value set to false
|
||||
// Note: Since in-scene placed NetworkObjects are parented in the scene
|
||||
// the default "assumption" is that children are parenting local space
|
||||
// relative.
|
||||
SetNetworkParenting(parentNetworkObject.NetworkObjectId, false);
|
||||
|
||||
// Set the cached parent
|
||||
m_CachedParent = parentNetworkObject.transform;
|
||||
@@ -1002,13 +1008,17 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
}
|
||||
internal void SetNetworkVariableData(FastBufferReader reader)
|
||||
|
||||
/// <summary>
|
||||
/// Only invoked during first synchronization of a NetworkObject (late join or newly spawned)
|
||||
/// </summary>
|
||||
internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
|
||||
{
|
||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||
{
|
||||
var behaviour = ChildNetworkBehaviours[i];
|
||||
behaviour.InitializeVariables();
|
||||
behaviour.SetNetworkVariableData(reader);
|
||||
behaviour.SetNetworkVariableData(reader, clientId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1045,7 +1055,20 @@ namespace Unity.Netcode
|
||||
{
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Error)
|
||||
{
|
||||
NetworkLog.LogError($"Behaviour index was out of bounds. Did you mess up the order of your {nameof(NetworkBehaviour)}s?");
|
||||
NetworkLog.LogError($"{nameof(NetworkBehaviour)} index {index} was out of bounds for {name}. NetworkBehaviours must be the same, and in the same order, between server and client.");
|
||||
}
|
||||
|
||||
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
|
||||
{
|
||||
var currentKnownChildren = new System.Text.StringBuilder();
|
||||
currentKnownChildren.Append($"Known child {nameof(NetworkBehaviour)}s:");
|
||||
for (int i = 0; i < ChildNetworkBehaviours.Count; i++)
|
||||
{
|
||||
var childNetworkBehaviour = ChildNetworkBehaviours[i];
|
||||
currentKnownChildren.Append($" [{i}] {childNetworkBehaviour.__getTypeName()}");
|
||||
currentKnownChildren.Append(i < ChildNetworkBehaviours.Count - 1 ? "," : ".");
|
||||
}
|
||||
NetworkLog.LogInfo(currentKnownChildren.ToString());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -1056,19 +1079,43 @@ namespace Unity.Netcode
|
||||
|
||||
internal struct SceneObject
|
||||
{
|
||||
public struct HeaderData : INetworkSerializeByMemcpy
|
||||
{
|
||||
public ulong NetworkObjectId;
|
||||
public ulong OwnerClientId;
|
||||
public uint Hash;
|
||||
private byte m_BitField;
|
||||
public uint Hash;
|
||||
public ulong NetworkObjectId;
|
||||
public ulong OwnerClientId;
|
||||
|
||||
public bool IsPlayerObject;
|
||||
public bool HasParent;
|
||||
public bool IsSceneObject;
|
||||
public bool HasTransform;
|
||||
public bool IsPlayerObject
|
||||
{
|
||||
get => ByteUtility.GetBit(m_BitField, 0);
|
||||
set => ByteUtility.SetBit(ref m_BitField, 0, value);
|
||||
}
|
||||
public bool HasParent
|
||||
{
|
||||
get => ByteUtility.GetBit(m_BitField, 1);
|
||||
set => ByteUtility.SetBit(ref m_BitField, 1, value);
|
||||
}
|
||||
public bool IsSceneObject
|
||||
{
|
||||
get => ByteUtility.GetBit(m_BitField, 2);
|
||||
set => ByteUtility.SetBit(ref m_BitField, 2, value);
|
||||
}
|
||||
public bool HasTransform
|
||||
{
|
||||
get => ByteUtility.GetBit(m_BitField, 3);
|
||||
set => ByteUtility.SetBit(ref m_BitField, 3, value);
|
||||
}
|
||||
|
||||
public HeaderData Header;
|
||||
public bool IsLatestParentSet
|
||||
{
|
||||
get => ByteUtility.GetBit(m_BitField, 4);
|
||||
set => ByteUtility.SetBit(ref m_BitField, 4, value);
|
||||
}
|
||||
|
||||
public bool WorldPositionStays
|
||||
{
|
||||
get => ByteUtility.GetBit(m_BitField, 5);
|
||||
set => ByteUtility.SetBit(ref m_BitField, 5, value);
|
||||
}
|
||||
|
||||
//If(Metadata.HasParent)
|
||||
public ulong ParentObjectId;
|
||||
@@ -1084,7 +1131,6 @@ namespace Unity.Netcode
|
||||
public TransformData Transform;
|
||||
|
||||
//If(Metadata.IsReparented)
|
||||
public bool IsLatestParentSet;
|
||||
|
||||
//If(IsLatestParentSet)
|
||||
public ulong? LatestParent;
|
||||
@@ -1094,114 +1140,90 @@ namespace Unity.Netcode
|
||||
|
||||
public int NetworkSceneHandle;
|
||||
|
||||
public bool WorldPositionStays;
|
||||
|
||||
public unsafe void Serialize(FastBufferWriter writer)
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
var writeSize = sizeof(HeaderData);
|
||||
if (Header.HasParent)
|
||||
writer.WriteValueSafe(m_BitField);
|
||||
writer.WriteValueSafe(Hash);
|
||||
BytePacker.WriteValueBitPacked(writer, NetworkObjectId);
|
||||
BytePacker.WriteValueBitPacked(writer, OwnerClientId);
|
||||
|
||||
if (HasParent)
|
||||
{
|
||||
writeSize += FastBufferWriter.GetWriteSize(ParentObjectId);
|
||||
writeSize += FastBufferWriter.GetWriteSize(WorldPositionStays);
|
||||
writeSize += FastBufferWriter.GetWriteSize(IsLatestParentSet);
|
||||
writeSize += IsLatestParentSet ? FastBufferWriter.GetWriteSize<ulong>() : 0;
|
||||
BytePacker.WriteValueBitPacked(writer, ParentObjectId);
|
||||
if (IsLatestParentSet)
|
||||
{
|
||||
BytePacker.WriteValueBitPacked(writer, LatestParent.Value);
|
||||
}
|
||||
}
|
||||
writeSize += Header.HasTransform ? FastBufferWriter.GetWriteSize<TransformData>() : 0;
|
||||
writeSize += Header.IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
|
||||
|
||||
var writeSize = 0;
|
||||
writeSize += HasTransform ? FastBufferWriter.GetWriteSize<TransformData>() : 0;
|
||||
writeSize += IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
|
||||
|
||||
if (!writer.TryBeginWrite(writeSize))
|
||||
{
|
||||
throw new OverflowException("Could not serialize SceneObject: Out of buffer space.");
|
||||
}
|
||||
|
||||
writer.WriteValue(Header);
|
||||
|
||||
if (Header.HasParent)
|
||||
{
|
||||
writer.WriteValue(ParentObjectId);
|
||||
writer.WriteValue(WorldPositionStays);
|
||||
writer.WriteValue(IsLatestParentSet);
|
||||
if (IsLatestParentSet)
|
||||
{
|
||||
writer.WriteValue(LatestParent.Value);
|
||||
}
|
||||
}
|
||||
|
||||
if (Header.HasTransform)
|
||||
if (HasTransform)
|
||||
{
|
||||
writer.WriteValue(Transform);
|
||||
}
|
||||
|
||||
// In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their
|
||||
// NetworkSceneHandle and GlobalObjectIdHash. Since each loaded scene has a unique
|
||||
// handle, it provides us with a unique and persistent "scene prefab asset" instance.
|
||||
// This is only set on in-scene placed NetworkObjects to reduce the over-all packet
|
||||
// sizes for dynamically spawned NetworkObjects.
|
||||
if (Header.IsSceneObject)
|
||||
// NetworkSceneHandle and GlobalObjectIdHash. Client-side NetworkSceneManagers use
|
||||
// this to locate their local instance of the in-scene placed NetworkObject instance.
|
||||
// Only written for in-scene placed NetworkObjects.
|
||||
if (IsSceneObject)
|
||||
{
|
||||
writer.WriteValue(OwnerObject.GetSceneOriginHandle());
|
||||
}
|
||||
|
||||
OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
|
||||
// Synchronize NetworkVariables and NetworkBehaviours
|
||||
var bufferSerializer = new BufferSerializer<BufferSerializerWriter>(new BufferSerializerWriter(writer));
|
||||
OwnerObject.SynchronizeNetworkBehaviours(ref bufferSerializer, TargetClientId);
|
||||
}
|
||||
|
||||
public unsafe void Deserialize(FastBufferReader reader)
|
||||
public void Deserialize(FastBufferReader reader)
|
||||
{
|
||||
if (!reader.TryBeginRead(sizeof(HeaderData)))
|
||||
reader.ReadValueSafe(out m_BitField);
|
||||
reader.ReadValueSafe(out Hash);
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out NetworkObjectId);
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out OwnerClientId);
|
||||
|
||||
if (HasParent)
|
||||
{
|
||||
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
|
||||
}
|
||||
reader.ReadValue(out Header);
|
||||
var readSize = 0;
|
||||
if (Header.HasParent)
|
||||
{
|
||||
readSize += FastBufferWriter.GetWriteSize(ParentObjectId);
|
||||
readSize += FastBufferWriter.GetWriteSize(WorldPositionStays);
|
||||
readSize += FastBufferWriter.GetWriteSize(IsLatestParentSet);
|
||||
// We need to read at this point in order to get the IsLatestParentSet value
|
||||
if (!reader.TryBeginRead(readSize))
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out ParentObjectId);
|
||||
if (IsLatestParentSet)
|
||||
{
|
||||
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
|
||||
ByteUnpacker.ReadValueBitPacked(reader, out ulong latestParent);
|
||||
LatestParent = latestParent;
|
||||
}
|
||||
|
||||
// Read the initial parenting related properties
|
||||
reader.ReadValue(out ParentObjectId);
|
||||
reader.ReadValue(out WorldPositionStays);
|
||||
reader.ReadValue(out IsLatestParentSet);
|
||||
|
||||
// Now calculate the remaining bytes to read
|
||||
readSize = 0;
|
||||
readSize += IsLatestParentSet ? FastBufferWriter.GetWriteSize<ulong>() : 0;
|
||||
}
|
||||
|
||||
readSize += Header.HasTransform ? FastBufferWriter.GetWriteSize<TransformData>() : 0;
|
||||
readSize += Header.IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
|
||||
var readSize = 0;
|
||||
readSize += HasTransform ? FastBufferWriter.GetWriteSize<TransformData>() : 0;
|
||||
readSize += IsSceneObject ? FastBufferWriter.GetWriteSize<int>() : 0;
|
||||
|
||||
// Try to begin reading the remaining bytes
|
||||
if (!reader.TryBeginRead(readSize))
|
||||
{
|
||||
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
|
||||
throw new OverflowException("Could not deserialize SceneObject: Reading past the end of the buffer");
|
||||
}
|
||||
|
||||
if (IsLatestParentSet)
|
||||
{
|
||||
reader.ReadValueSafe(out ulong latestParent);
|
||||
LatestParent = latestParent;
|
||||
}
|
||||
|
||||
if (Header.HasTransform)
|
||||
if (HasTransform)
|
||||
{
|
||||
reader.ReadValue(out Transform);
|
||||
}
|
||||
|
||||
// In-Scene NetworkObjects are uniquely identified NetworkPrefabs defined by their
|
||||
// NetworkSceneHandle and GlobalObjectIdHash. Since each loaded scene has a unique
|
||||
// handle, it provides us with a unique and persistent "scene prefab asset" instance.
|
||||
// Client-side NetworkSceneManagers use this to locate their local instance of the
|
||||
// NetworkObject instance.
|
||||
if (Header.IsSceneObject)
|
||||
// NetworkSceneHandle and GlobalObjectIdHash. Client-side NetworkSceneManagers use
|
||||
// this to locate their local instance of the in-scene placed NetworkObject instance.
|
||||
// Only read for in-scene placed NetworkObjects
|
||||
if (IsSceneObject)
|
||||
{
|
||||
reader.ReadValueSafe(out NetworkSceneHandle);
|
||||
reader.ReadValue(out NetworkSceneHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1214,18 +1236,87 @@ namespace Unity.Netcode
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles synchronizing NetworkVariables and custom synchronization data for NetworkBehaviours.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is where we determine how much data is written after the associated NetworkObject in order to recover
|
||||
/// from a failed instantiated NetworkObject without completely disrupting client synchronization.
|
||||
/// </remarks>
|
||||
internal void SynchronizeNetworkBehaviours<T>(ref BufferSerializer<T> serializer, ulong targetClientId = 0) where T : IReaderWriter
|
||||
{
|
||||
if (serializer.IsWriter)
|
||||
{
|
||||
var writer = serializer.GetFastBufferWriter();
|
||||
var positionBeforeSynchronizing = writer.Position;
|
||||
writer.WriteValueSafe((ushort)0);
|
||||
var sizeToSkipCalculationPosition = writer.Position;
|
||||
|
||||
// Synchronize NetworkVariables
|
||||
WriteNetworkVariableData(writer, targetClientId);
|
||||
// Reserve the NetworkBehaviour synchronization count position
|
||||
var networkBehaviourCountPosition = writer.Position;
|
||||
writer.WriteValueSafe((byte)0);
|
||||
|
||||
// Parse through all NetworkBehaviours and any that return true
|
||||
// had additional synchronization data written.
|
||||
// (See notes for reading/deserialization below)
|
||||
var synchronizationCount = (byte)0;
|
||||
foreach (var childBehaviour in ChildNetworkBehaviours)
|
||||
{
|
||||
if (childBehaviour.Synchronize(ref serializer))
|
||||
{
|
||||
synchronizationCount++;
|
||||
}
|
||||
}
|
||||
|
||||
var currentPosition = writer.Position;
|
||||
// Write the total number of bytes written for NetworkVariable and NetworkBehaviour
|
||||
// synchronization.
|
||||
writer.Seek(positionBeforeSynchronizing);
|
||||
// We want the size of everything after our size to skip calculation position
|
||||
var size = (ushort)(currentPosition - sizeToSkipCalculationPosition);
|
||||
writer.WriteValueSafe(size);
|
||||
// Write the number of NetworkBehaviours synchronized
|
||||
writer.Seek(networkBehaviourCountPosition);
|
||||
writer.WriteValueSafe(synchronizationCount);
|
||||
// seek back to the position after writing NetworkVariable and NetworkBehaviour
|
||||
// synchronization data.
|
||||
writer.Seek(currentPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
var reader = serializer.GetFastBufferReader();
|
||||
|
||||
reader.ReadValueSafe(out ushort sizeOfSynchronizationData);
|
||||
var seekToEndOfSynchData = reader.Position + sizeOfSynchronizationData;
|
||||
// Apply the network variable synchronization data
|
||||
SetNetworkVariableData(reader, targetClientId);
|
||||
// Read the number of NetworkBehaviours to synchronize
|
||||
reader.ReadValueSafe(out byte numberSynchronized);
|
||||
var networkBehaviourId = (ushort)0;
|
||||
|
||||
// If a NetworkBehaviour writes synchronization data, it will first
|
||||
// write its NetworkBehaviourId so when deserializing the client-side
|
||||
// can find the right NetworkBehaviour to deserialize the synchronization data.
|
||||
for (int i = 0; i < numberSynchronized; i++)
|
||||
{
|
||||
serializer.SerializeValue(ref networkBehaviourId);
|
||||
var networkBehaviour = GetNetworkBehaviourAtOrderIndex(networkBehaviourId);
|
||||
networkBehaviour.Synchronize(ref serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal SceneObject GetMessageSceneObject(ulong targetClientId)
|
||||
{
|
||||
var obj = new SceneObject
|
||||
{
|
||||
Header = new SceneObject.HeaderData
|
||||
{
|
||||
IsPlayerObject = IsPlayerObject,
|
||||
NetworkObjectId = NetworkObjectId,
|
||||
OwnerClientId = OwnerClientId,
|
||||
IsSceneObject = IsSceneObject ?? true,
|
||||
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
||||
},
|
||||
NetworkObjectId = NetworkObjectId,
|
||||
OwnerClientId = OwnerClientId,
|
||||
IsPlayerObject = IsPlayerObject,
|
||||
IsSceneObject = IsSceneObject ?? true,
|
||||
Hash = HostCheckForGlobalObjectIdHashOverride(),
|
||||
OwnerObject = this,
|
||||
TargetClientId = targetClientId
|
||||
};
|
||||
@@ -1235,11 +1326,18 @@ namespace Unity.Netcode
|
||||
if (!AlwaysReplicateAsRoot && transform.parent != null)
|
||||
{
|
||||
parentNetworkObject = transform.parent.GetComponent<NetworkObject>();
|
||||
// In-scene placed NetworkObjects parented under GameObjects with no NetworkObject
|
||||
// should set the has parent flag and preserve the world position stays value
|
||||
if (parentNetworkObject == null && obj.IsSceneObject)
|
||||
{
|
||||
obj.HasParent = true;
|
||||
obj.WorldPositionStays = m_CachedWorldPositionStays;
|
||||
}
|
||||
}
|
||||
|
||||
if (parentNetworkObject != null)
|
||||
{
|
||||
obj.Header.HasParent = true;
|
||||
obj.HasParent = true;
|
||||
obj.ParentObjectId = parentNetworkObject.NetworkObjectId;
|
||||
obj.WorldPositionStays = m_CachedWorldPositionStays;
|
||||
var latestParent = GetNetworkParenting();
|
||||
@@ -1253,20 +1351,38 @@ namespace Unity.Netcode
|
||||
|
||||
if (IncludeTransformWhenSpawning == null || IncludeTransformWhenSpawning(OwnerClientId))
|
||||
{
|
||||
obj.Header.HasTransform = true;
|
||||
obj.HasTransform = true;
|
||||
|
||||
// We start with the default AutoObjectParentSync values to determine which transform space we will
|
||||
// be synchronizing clients with.
|
||||
var syncRotationPositionLocalSpaceRelative = obj.HasParent && !m_CachedWorldPositionStays;
|
||||
var syncScaleLocalSpaceRelative = obj.HasParent && !m_CachedWorldPositionStays;
|
||||
|
||||
// If auto object synchronization is turned off
|
||||
if (!AutoObjectParentSync)
|
||||
{
|
||||
// We always synchronize position and rotation world space relative
|
||||
syncRotationPositionLocalSpaceRelative = false;
|
||||
// Scale is special, it synchronizes local space relative if it has a
|
||||
// parent since applying the world space scale under a parent with scale
|
||||
// will result in the improper scale for the child
|
||||
syncScaleLocalSpaceRelative = obj.HasParent;
|
||||
}
|
||||
|
||||
|
||||
obj.Transform = new SceneObject.TransformData
|
||||
{
|
||||
// If we are parented and we have the m_CachedWorldPositionStays disabled, then use local space
|
||||
// values as opposed world space values.
|
||||
Position = parentNetworkObject && !m_CachedWorldPositionStays ? transform.localPosition : transform.position,
|
||||
Rotation = parentNetworkObject && !m_CachedWorldPositionStays ? transform.localRotation : transform.rotation,
|
||||
Position = syncRotationPositionLocalSpaceRelative ? transform.localPosition : transform.position,
|
||||
Rotation = syncRotationPositionLocalSpaceRelative ? transform.localRotation : transform.rotation,
|
||||
|
||||
// We only use the lossyScale if the NetworkObject has a parent. Multi-generation nested children scales can
|
||||
// impact the final scale of the child NetworkObject in question. The solution is to use the lossy scale
|
||||
// which can be thought of as "world space scale".
|
||||
// More information:
|
||||
// https://docs.unity3d.com/ScriptReference/Transform-lossyScale.html
|
||||
Scale = parentNetworkObject && !m_CachedWorldPositionStays ? transform.localScale : transform.lossyScale,
|
||||
Scale = syncScaleLocalSpaceRelative ? transform.localScale : transform.lossyScale,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1278,10 +1394,10 @@ namespace Unity.Netcode
|
||||
/// when the client is approved or during a scene transition
|
||||
/// </summary>
|
||||
/// <param name="sceneObject">Deserialized scene object data</param>
|
||||
/// <param name="variableData">reader for the NetworkVariable data</param>
|
||||
/// <param name="reader">FastBufferReader for the NetworkVariable data</param>
|
||||
/// <param name="networkManager">NetworkManager instance</param>
|
||||
/// <returns>optional to use NetworkObject deserialized</returns>
|
||||
internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader variableData, NetworkManager networkManager)
|
||||
internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader reader, NetworkManager networkManager)
|
||||
{
|
||||
//Attempt to create a local NetworkObject
|
||||
var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject);
|
||||
@@ -1289,18 +1405,36 @@ namespace Unity.Netcode
|
||||
if (networkObject == null)
|
||||
{
|
||||
// Log the error that the NetworkObject failed to construct
|
||||
Debug.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Header.Hash}.");
|
||||
if (networkManager.LogLevel <= LogLevel.Normal)
|
||||
{
|
||||
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Hash}.");
|
||||
}
|
||||
|
||||
// If we failed to load this NetworkObject, then skip past the network variable data
|
||||
variableData.ReadValueSafe(out ushort varSize);
|
||||
variableData.Seek(variableData.Position + varSize);
|
||||
try
|
||||
{
|
||||
// If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data
|
||||
reader.ReadValueSafe(out ushort networkBehaviourSynchronizationDataLength);
|
||||
reader.Seek(reader.Position + networkBehaviourSynchronizationDataLength);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
|
||||
// We have nothing left to do here.
|
||||
return null;
|
||||
}
|
||||
|
||||
// This will get set again when the NetworkObject is spawned locally, but we set it here ahead of spawning
|
||||
// in order to be able to determine which NetworkVariables the client will be allowed to read.
|
||||
networkObject.OwnerClientId = sceneObject.OwnerClientId;
|
||||
|
||||
// Synchronize NetworkBehaviours
|
||||
var bufferSerializer = new BufferSerializer<BufferSerializerReader>(new BufferSerializerReader(reader));
|
||||
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
|
||||
|
||||
// Spawn the NetworkObject
|
||||
networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, variableData, false);
|
||||
networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, false);
|
||||
|
||||
return networkObject;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user