From 346039fab2ccc10aecb6fb3c6034783f94107bfc Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Sun, 6 Nov 2022 16:57:45 -0600
Subject: [PATCH 01/26] fix and refactor
This includes the changes to provide users with a way to synchronize NetworkBehaviours with custom data prior to their associated NetworkObject is spawned.
This also includes some fixes that allows for NetworkObjects to fail, NetworkVariables to fail, and NetworkBehaviour synchronization to fail without impacting the rest of the synchronization process.
---
.../Runtime/Core/NetworkBehaviour.cs | 191 ++++++++++++++++--
.../Runtime/Core/NetworkObject.cs | 111 +++++++++-
.../Runtime/SceneManagement/SceneEventData.cs | 58 +++---
.../Runtime/Spawning/NetworkSpawnManager.cs | 5 +-
4 files changed, 311 insertions(+), 54 deletions(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
index 8289676383..5495f8a269 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
@@ -762,6 +762,16 @@ internal void MarkVariablesDirty(bool dirty)
}
}
+ ///
+ /// Synchronizes by writing all NetworkVariable fields' values the client can read that are
+ /// defined within the NetworkBehaviour.
+ /// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject)
+ ///
+ ///
+ /// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled
+ /// each NetworkVariable field will be proceeded by the number of bytes
+ /// written for that specific field.
+ ///
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
{
if (NetworkVariableFields.Count == 0)
@@ -771,27 +781,43 @@ internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClie
for (int j = 0; j < NetworkVariableFields.Count; j++)
{
- bool canClientRead = NetworkVariableFields[j].CanClientRead(targetClientId);
- if (canClientRead)
+ if (NetworkVariableFields[j].CanClientRead(targetClientId))
{
- var writePos = writer.Position;
- writer.WriteValueSafe((ushort)0);
- var startPos = writer.Position;
- NetworkVariableFields[j].WriteField(writer);
- var size = writer.Position - startPos;
- writer.Seek(writePos);
- writer.WriteValueSafe((ushort)size);
- writer.Seek(startPos + size);
+ if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
+ {
+ var writePos = writer.Position;
+ writer.WriteValueSafe((ushort)0);
+ var startPos = writer.Position;
+ NetworkVariableFields[j].WriteField(writer);
+ var size = writer.Position - startPos;
+ writer.Seek(writePos);
+ writer.WriteValueSafe((ushort)size);
+ writer.Seek(startPos + size);
+ }
+ else
+ {
+ NetworkVariableFields[j].WriteField(writer);
+ }
}
- else
+ else // Only if EnsureNetworkVariableLengthSafety, otherwise just skip
+ if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
{
writer.WriteValueSafe((ushort)0);
}
}
}
- internal void SetNetworkVariableData(FastBufferReader reader)
+ ///
+ /// Synchronizes by setting all NetworkVariable fields' values that the client can read
+ /// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject)
+ ///
+ ///
+ /// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled
+ /// each NetworkVariable field will be proceeded by the number of bytes
+ /// written for that specific field.
+ ///
+ internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
{
if (NetworkVariableFields.Count == 0)
{
@@ -800,13 +826,23 @@ internal void SetNetworkVariableData(FastBufferReader reader)
for (int j = 0; j < NetworkVariableFields.Count; j++)
{
- reader.ReadValueSafe(out ushort varSize);
- if (varSize == 0)
+ var varSize = (ushort)0;
+ var readStartPos = 0;
+ if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
+ {
+ reader.ReadValueSafe(out varSize);
+ if (varSize == 0)
+ {
+ continue;
+ }
+ readStartPos = reader.Position;
+ }
+ else // If the client cannot read this field, then skip it
+ if (!NetworkVariableFields[j].CanClientRead(clientId))
{
continue;
}
- var readStartPos = reader.Position;
NetworkVariableFields[j].ReadField(reader);
if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
@@ -843,6 +879,131 @@ protected NetworkObject GetNetworkObject(ulong networkId)
return NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(networkId, out NetworkObject networkObject) ? networkObject : null;
}
+ ///
+ /// When overridden, this method is invoked on a NetworkBehaviour during client synchronization in order
+ /// to provide the ability to inject custom synchronization information specific to the derived class.
+ ///
+ /// The serializer to use to read and write the data.
+ ///
+ /// Either BufferSerializerReader or BufferSerializerWriter, depending whether the serializer
+ /// is in read mode or write mode.
+ ///
+ protected virtual void OnSynchronize(ref BufferSerializer serializer) where T : IReaderWriter
+ {
+
+ }
+
+ ///
+ /// Internal method that determines if a NetworkBehaviour has additional synchronization data to
+ /// be synchronized when first instantiated prior to its associated NetworkObject being spawned.
+ ///
+ ///
+ /// Only invoked during the initial synchronization (i.e. late join client or newly spawned NetworkObject).
+ /// This includes try-catch to assure if user code fails the rest of the synchronization process will not
+ /// be impacted (i.e. if one NetworkBehaviour fails the rest will still be synchronized)
+ ///
+ /// true if it wrote synchronization data and false if it did not
+ internal bool Synchronize(ref BufferSerializer serializer) where T : IReaderWriter
+ {
+ if (serializer.IsWriter)
+ {
+ // Get the writer to handle seeking and determining how many bytes were written
+ var writer = serializer.GetFastBufferWriter();
+ // Save our position before we attempt to write anything so we can seek back to it (i.e. error occurs)
+ var positionBeforeWrite = writer.Position;
+ var networkBehaviourId = NetworkBehaviourId;
+ writer.WriteValueSafe(networkBehaviourId);
+
+ // Save our position where we will write the final size being written so we can skip over it in the
+ // event an exception occurs when deserializing.
+ var sizePosition = writer.Position;
+ writer.WriteValueSafe((ushort)0);
+ // Save our position before synchronizing to determine
+ var positionBeforeSynchronize = writer.Position;
+ var threwException = false;
+ try
+ {
+ OnSynchronize(ref serializer);
+ }
+ catch (Exception ex)
+ {
+ threwException = true;
+ if (NetworkManager.LogLevel <= LogLevel.Normal)
+ {
+ NetworkLog.LogWarning($"{name} threw an exception during synchronization serialization, this {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!");
+ if (NetworkManager.LogLevel == LogLevel.Developer)
+ {
+ NetworkLog.LogError($"{ex.Message}\n {ex.StackTrace}");
+ }
+ }
+ }
+ var finalPosition = writer.Position;
+
+ // If we wrote nothing then skip writing anything for this NetworkBehaviour
+ if (finalPosition == positionBeforeSynchronize || threwException)
+ {
+ writer.Seek(positionBeforeWrite);
+ return false;
+ }
+ else
+ {
+ // Write the number of bytes serialized to handle exceptions on the deserialization side
+ var bytesWritten = finalPosition - positionBeforeSynchronize;
+ writer.Seek(sizePosition);
+ writer.WriteValueSafe((ushort)bytesWritten);
+ writer.Seek(finalPosition);
+ }
+ return true;
+ }
+ else
+ {
+ var reader = serializer.GetFastBufferReader();
+ // We will always read the expected byte count
+ reader.ReadValueSafe(out ushort expectedBytesToRead);
+
+ // Save our position before we begin synchronization deserialization
+ var positionBeforeSynchronize = reader.Position;
+ var synchronizationError = false;
+ try
+ {
+ // Invoke synchronization
+ OnSynchronize(ref serializer);
+ }
+ catch (Exception ex)
+ {
+ if (NetworkManager.LogLevel <= LogLevel.Normal)
+ {
+ NetworkLog.LogWarning($"{name} threw an exception during synchronization deserialization, this {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!");
+ if (NetworkManager.LogLevel == LogLevel.Developer)
+ {
+ NetworkLog.LogError($"{ex.Message}\n {ex.StackTrace}");
+ }
+ }
+ synchronizationError = true;
+ }
+
+ var totalBytesRead = reader.Position - positionBeforeSynchronize;
+ if (totalBytesRead != expectedBytesToRead)
+ {
+ if (NetworkManager.LogLevel <= LogLevel.Normal)
+ {
+ NetworkLog.LogWarning($"{name} read {totalBytesRead} bytes but was expected to read {expectedBytesToRead} bytes during synchronization deserialization! This {nameof(NetworkBehaviour)} is being skipped and will not be synchronized!");
+ }
+ synchronizationError = true;
+ }
+
+ // Skip over the entry if deserialization fails
+ if (synchronizationError)
+ {
+ var skipToPosition = positionBeforeSynchronize + expectedBytesToRead;
+ reader.Seek(skipToPosition);
+ return false;
+ }
+ return true;
+ }
+ }
+
+
///
/// Invoked when the the is attached to.
/// NOTE: If you override this, you will want to always invoke this base class version of this
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
index 8b9d6b5f52..35787a4d3f 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
@@ -1008,13 +1008,17 @@ internal static void VerifyParentingStatus()
}
}
}
- internal void SetNetworkVariableData(FastBufferReader reader)
+
+ ///
+ /// Only invoked during first synchronization
+ ///
+ 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);
}
}
@@ -1161,7 +1165,9 @@ public unsafe void Serialize(FastBufferWriter writer)
writer.WriteValue(OwnerObject.GetSceneOriginHandle());
}
- OwnerObject.WriteNetworkVariableData(writer, TargetClientId);
+ // Synchronize NetworkBehaviours
+ var bufferSerializer = new BufferSerializer(new BufferSerializerWriter(writer));
+ OwnerObject.SynchronizeNetworkBehaviours(ref bufferSerializer, TargetClientId);
}
public unsafe void Deserialize(FastBufferReader reader)
@@ -1171,6 +1177,7 @@ public unsafe void Deserialize(FastBufferReader reader)
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
}
reader.ReadValue(out Header);
+
var readSize = 0;
if (Header.HasParent)
{
@@ -1233,6 +1240,74 @@ internal void PostNetworkVariableWrite()
}
}
+ ///
+ /// Handles synchronizing NetworkVariables as well as custom user synchronization code for NetworkBeahviours
+ ///
+ internal void SynchronizeNetworkBehaviours(ref BufferSerializer 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;
+
+ // Each NetworkBehaviour stores its NetworkBehaviourId if it writes data
+ // so when deserializing the first value is the NetworkBehaviourId that
+ // will have the synchronization data applied to it.
+ 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
@@ -1325,7 +1400,7 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId)
/// reader for the NetworkVariable data
/// NetworkManager instance
/// optional to use NetworkObject deserialized
- 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);
@@ -1333,18 +1408,36 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
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.Header.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);
+ var networkBehaviourSynchronizationData = (ushort)0;
+ try
+ {
+ // If we failed to load this NetworkObject, then skip past the NetworkBehaviour synchronization data
+ reader.ReadValueSafe(out networkBehaviourSynchronizationData);
+ reader.Seek(reader.Position + networkBehaviourSynchronizationData);
+ }
+ 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.Header.OwnerClientId;
+
+ // Synchronize NetworkBehaviours
+ var bufferSerializer = new BufferSerializer(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;
}
diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
index 222e437ca2..88e5b65214 100644
--- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
+++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
@@ -444,13 +444,14 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer)
int totalBytes = 0;
// Write the number of NetworkObjects we are serializing
- BytePacker.WriteValuePacked(writer, m_NetworkObjectsSync.Count);
+ //BytePacker.WriteValuePacked(writer, m_NetworkObjectsSync.Count);
+ writer.WriteValueSafe(m_NetworkObjectsSync.Count);
+
// Serialize all NetworkObjects that are spawned
for (var i = 0; i < m_NetworkObjectsSync.Count; ++i)
{
var noStart = writer.Position;
var sceneObject = m_NetworkObjectsSync[i].GetMessageSceneObject(TargetClientId);
- BytePacker.WriteValuePacked(writer, m_NetworkObjectsSync[i].GetSceneOriginHandle());
sceneObject.Serialize(writer);
var noStop = writer.Position;
totalBytes += (int)(noStop - noStart);
@@ -462,8 +463,8 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer)
for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i)
{
var noStart = writer.Position;
- BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
- BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
+ writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
+ writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
var noStop = writer.Position;
totalBytes += (int)(noStop - noStart);
}
@@ -497,8 +498,6 @@ internal void SerializeScenePlacedObjects(FastBufferWriter writer)
{
if (keyValuePairBySceneHandle.Value.Observers.Contains(TargetClientId))
{
- // Write our server relative scene handle for the NetworkObject being serialized
- writer.WriteValueSafe(keyValuePairBySceneHandle.Key);
// Serialize the NetworkObject
var sceneObject = keyValuePairBySceneHandle.Value.GetMessageSceneObject(TargetClientId);
sceneObject.Serialize(writer);
@@ -512,8 +511,11 @@ internal void SerializeScenePlacedObjects(FastBufferWriter writer)
// Write the scene handle and GlobalObjectIdHash value
for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i)
{
- BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
- BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
+ //BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
+ //BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
+
+ writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
+ writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
}
var tailPosition = writer.Position;
@@ -624,13 +626,15 @@ internal void DeserializeScenePlacedObjects()
for (ushort i = 0; i < newObjectsCount; i++)
{
- InternalBuffer.ReadValueSafe(out int sceneHandle);
- // Set our relative scene to the NetworkObject
- m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneHandle);
-
- // Deserialize the NetworkObject
var sceneObject = new NetworkObject.SceneObject();
sceneObject.Deserialize(InternalBuffer);
+
+ if (sceneObject.Header.IsSceneObject)
+ {
+ // Set our relative scene to the NetworkObject
+ m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle);
+ }
+
NetworkObject.AddSceneObject(sceneObject, InternalBuffer, m_NetworkManager);
}
// Now deserialize the despawned in-scene placed NetworkObjects list (if any)
@@ -771,8 +775,8 @@ private void DeserializeDespawnedInScenePlacedNetworkObjects()
for (int i = 0; i < despawnedObjectsCount; i++)
{
// We just need to get the scene
- ByteUnpacker.ReadValuePacked(InternalBuffer, out int networkSceneHandle);
- ByteUnpacker.ReadValuePacked(InternalBuffer, out uint globalObjectIdHash);
+ InternalBuffer.ReadValueSafe(out int networkSceneHandle);
+ InternalBuffer.ReadValueSafe(out uint globalObjectIdHash);
var sceneRelativeNetworkObjects = new Dictionary();
if (!sceneCache.ContainsKey(networkSceneHandle))
{
@@ -847,24 +851,26 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager)
try
{
// Process all spawned NetworkObjects for this network session
- ByteUnpacker.ReadValuePacked(InternalBuffer, out int newObjectsCount);
-
-
+ InternalBuffer.ReadValueSafe(out int newObjectsCount);
for (int i = 0; i < newObjectsCount; i++)
{
- // We want to make sure for each NetworkObject we have the appropriate scene selected as the scene that is
- // currently being synchronized. This assures in-scene placed NetworkObjects will use the right NetworkObject
- // from the list of populated
- ByteUnpacker.ReadValuePacked(InternalBuffer, out int handle);
- m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(handle);
-
var sceneObject = new NetworkObject.SceneObject();
sceneObject.Deserialize(InternalBuffer);
+ // If the sceneObject is in-scene placed, then set the scene being synchronized
+ if (sceneObject.Header.IsSceneObject)
+ {
+ m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle);
+ }
var spawnedNetworkObject = NetworkObject.AddSceneObject(sceneObject, InternalBuffer, networkManager);
- if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject))
+
+ // If we failed to deserialize the NetowrkObject then don't add null to the list
+ if (spawnedNetworkObject != null)
{
- m_NetworkObjectsSync.Add(spawnedNetworkObject);
+ if (!m_NetworkObjectsSync.Contains(spawnedNetworkObject))
+ {
+ m_NetworkObjectsSync.Add(spawnedNetworkObject);
+ }
}
}
diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
index 8e21e532c0..53e145fc47 100644
--- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
@@ -495,8 +495,7 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, ulong netwo
}
// Ran on both server and client
- internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject,
- FastBufferReader variableData, bool destroyWithScene)
+ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkObject.SceneObject sceneObject, bool destroyWithScene)
{
if (networkObject == null)
{
@@ -508,8 +507,6 @@ internal void SpawnNetworkObjectLocally(NetworkObject networkObject, in NetworkO
throw new SpawnStateException("Object is already spawned");
}
- networkObject.SetNetworkVariableData(variableData);
-
SpawnNetworkObjectLocallyCommon(networkObject, sceneObject.Header.NetworkObjectId, sceneObject.Header.IsSceneObject, sceneObject.Header.IsPlayerObject, sceneObject.Header.OwnerClientId, destroyWithScene);
}
From cc96852855b33a6cc04bb312d227d997e430e1b8 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Sun, 6 Nov 2022 17:02:03 -0600
Subject: [PATCH 02/26] test
An updated version of this test that includes additional tests related to the refactoring.
This test was not properly testing the entire serialization pipeline and it only tested for one NetworkVariable. If you added another NetworkVariable to the NetworkBehaviourWithNetworkVariables component the synchronization process would halt. There was also a bug in SceneEventData that would occur if a NetworkObject failed to be instantiated. The updates included resolve all of these issues for the original intention behind the test.
---
.../NetworkObjectSceneSerializationTests.cs | 505 ++++++++++++------
1 file changed, 341 insertions(+), 164 deletions(-)
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs
index 8781faf2bd..2c69a409cb 100644
--- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs
@@ -1,233 +1,410 @@
+using System.Collections;
using System.Collections.Generic;
using UnityEngine;
-using UnityEngine.SceneManagement;
+using UnityEngine.TestTools;
using NUnit.Framework;
-using Unity.Collections;
using Unity.Netcode.TestHelpers.Runtime;
+using Random = UnityEngine.Random;
namespace Unity.Netcode.RuntimeTests
{
- public class NetworkObjectSceneSerializationTests
+ [TestFixture(VariableLengthSafety.DisableNetVarSafety)]
+ [TestFixture(VariableLengthSafety.EnabledNetVarSafety)]
+ public class NetworkObjectSceneSerializationTests : NetcodeIntegrationTest
{
+ private const int k_NumberToSpawn = 30;
+ protected override int NumberOfClients => 0;
- ///
- /// The purpose behind this test is to assure that in-scene NetworkObjects
- /// that are serialized into a single stream (approval or switch scene this happens)
- /// will continue to be processed even if one of the NetworkObjects is invalid.
- ///
- [Test]
- public void NetworkObjectSceneSerializationFailure()
+ private GameObject m_NetworkPrefab;
+ private GameObject m_InValidNetworkPrefab;
+ private GameObject m_SynchronizationPrefab;
+ private VariableLengthSafety m_VariableLengthSafety;
+
+ private LogLevel m_CurrentLogLevel;
+
+ public enum VariableLengthSafety
{
- var networkObjectsToTest = new List();
+ DisableNetVarSafety,
+ EnabledNetVarSafety,
+ }
+
+ public NetworkObjectSceneSerializationTests(VariableLengthSafety variableLengthSafety)
+ {
+ m_VariableLengthSafety = variableLengthSafety;
+ }
+
+ protected override void OnCreatePlayerPrefab()
+ {
+ m_PlayerPrefab.AddComponent();
+ base.OnCreatePlayerPrefab();
+ }
+
+ protected override void OnServerAndClientsCreated()
+ {
+
+ // Set the NetworkVariable Safety Check setting
+ m_ServerNetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
+
+ // Ignore the errors generated during this test (they are expected)
+ m_ServerNetworkManager.LogLevel = LogLevel.Nothing;
- var writer = new FastBufferWriter(1300, Allocator.Temp, 4096000);
- var invalidNetworkObjectOffsets = new List();
- var invalidNetworkObjectIdCount = new List();
- var invalidNetworkObjects = new List();
- var invalidNetworkObjectFrequency = 3;
- using (writer)
+ // Disable forcing the same prefabs to avoid failed connections
+ m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false;
+
+ // Create the valid network prefab
+ m_NetworkPrefab = CreateNetworkObjectPrefab("ValidObject");
+ m_NetworkPrefab.AddComponent();
+
+ // Create the invalid network prefab (that will fail on client side)
+ m_InValidNetworkPrefab = CreateNetworkObjectPrefab("InvalidObject");
+ m_InValidNetworkPrefab.AddComponent();
+
+ // Create the synchronization network prefab (some pass and some fail)
+ m_SynchronizationPrefab = CreateNetworkObjectPrefab("SyncObject");
+ m_SynchronizationPrefab.AddComponent();
+
+ base.OnServerAndClientsCreated();
+ }
+
+ protected override void OnNewClientCreated(NetworkManager networkManager)
+ {
+ networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
+ networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
+ foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.NetworkPrefabs)
{
- // Construct 50 NetworkObjects
- for (int i = 0; i < 50; i++)
+ // To simulate a failure, we exclude the m_InValidNetworkPrefab from the connecting
+ // client's side.
+ if (networkPrefab.Prefab.name != m_InValidNetworkPrefab.name)
{
- // Inject an invalid NetworkObject every [invalidNetworkObjectFrequency] entry
- if ((i % invalidNetworkObjectFrequency) == 0)
- {
- // Create the invalid NetworkObject
- var gameObject = new GameObject($"InvalidTestObject{i}");
+ networkManager.NetworkConfig.NetworkPrefabs.Add(networkPrefab);
+ }
+ }
+ // Disable forcing the same prefabs to avoid failed connections
+ networkManager.NetworkConfig.ForceSamePrefabs = false;
+ networkManager.LogLevel = m_CurrentLogLevel;
+ base.OnNewClientCreated(networkManager);
+ }
- Assert.IsNotNull(gameObject);
+ [UnityTest]
+ public IEnumerator NetworkObjectDeserializationFailure()
+ {
+ m_CurrentLogLevel = LogLevel.Nothing;
+ var validSpawnedNetworkObjects = new List();
+ NetworkBehaviourWithNetworkVariables.ResetSpawnCount();
- var networkObject = gameObject.AddComponent();
+ // Spawn NetworkObjects on the server side with half of them being the
+ // invalid network prefabs to simulate NetworkObject synchronization failure
+ for (int i = 0; i < k_NumberToSpawn; i++)
+ {
+ if (i % 2 == 0)
+ {
+ SpawnObject(m_InValidNetworkPrefab, m_ServerNetworkManager);
+ }
+ else
+ {
+ // Keep track of the prefabs that should successfully spawn on the client side
+ validSpawnedNetworkObjects.Add(SpawnObject(m_NetworkPrefab, m_ServerNetworkManager));
+ }
+ }
- Assert.IsNotNull(networkObject);
+ // Assure the server-side spawned all NetworkObjects
+ yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.ServerSpawnCount == k_NumberToSpawn);
- var networkVariableComponent = gameObject.AddComponent();
- Assert.IsNotNull(networkVariableComponent);
+ // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
+ yield return CreateAndStartNewClient();
- // Add invalid NetworkObject's starting position before serialization to handle trapping for the Debug.LogError message
- // that we know will be thrown
- invalidNetworkObjectOffsets.Add(writer.Position);
+ yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.ClientSpawnCount == validSpawnedNetworkObjects.Count);
- networkObject.GlobalObjectIdHash = (uint)(i);
- invalidNetworkObjectIdCount.Add(i);
+ var serverSidePlayerComponent = NetworkBehaviourWithOwnerNetworkVariables.ServerSideClientInstance;
+ var clientSidePlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent();
- invalidNetworkObjects.Add(gameObject);
+ Assert.IsTrue(serverSidePlayerComponent.NetworkVariableData1.Value == clientSidePlayerComponent.NetworkVariableData1.Value);
+ Assert.IsTrue(serverSidePlayerComponent.NetworkVariableData2.Value == clientSidePlayerComponent.NetworkVariableData2.Value);
+ Assert.IsTrue(serverSidePlayerComponent.NetworkVariableData3.Value == clientSidePlayerComponent.NetworkVariableData3.Value);
- writer.WriteValueSafe((int)networkObject.GetSceneOriginHandle());
- // Serialize the invalid NetworkObject
- var sceneObject = networkObject.GetMessageSceneObject(0);
- var prePosition = writer.Position;
- sceneObject.Serialize(writer);
+ //Validate that the connected client has spawned the
+ var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
- Debug.Log(
- $"Invalid {nameof(NetworkObject)} Size {writer.Position - prePosition}");
+ foreach (var spawnedObject in validSpawnedNetworkObjects)
+ {
+ var spawnedNetworkObject = spawnedObject.GetComponent();
+ Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedNetworkObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a {nameof(NetworkObject.NetworkObjectId)} of {spawnedNetworkObject.NetworkObjectId}");
+ var clientSideObject = clientSideNetworkObjects[spawnedNetworkObject.NetworkObjectId];
+ Assert.IsTrue(clientSideObject.NetworkManager == m_ClientNetworkManagers[0], $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!");
+ var serverSideComponent = spawnedObject.GetComponent();
+ var clientSideComponent = clientSideObject.GetComponent();
+
+ Assert.IsTrue(serverSideComponent.NetworkVariableData1.Count == clientSideComponent.NetworkVariableData1.Count);
+ for (int i = 0; i < serverSideComponent.NetworkVariableData1.Count; i++)
+ {
+ Assert.IsTrue(serverSideComponent.NetworkVariableData1[i] == clientSideComponent.NetworkVariableData1[i]);
+ }
- // Now adjust how frequent we will inject invalid NetworkObjects
- invalidNetworkObjectFrequency = Random.Range(2, 5);
+ Assert.IsTrue(serverSideComponent.NetworkVariableData2.Value == clientSideComponent.NetworkVariableData2.Value);
+ Assert.IsTrue(serverSideComponent.NetworkVariableData3.Value == clientSideComponent.NetworkVariableData3.Value);
+ Assert.IsTrue(serverSideComponent.NetworkVariableData4.Value == clientSideComponent.NetworkVariableData4.Value);
+ }
+ }
- }
- else
- {
- // Create a valid NetworkObject
- var gameObject = new GameObject($"TestObject{i}");
+ ///
+ /// This tests a mixture of failed an valid NetworkBeahviour synchronization all while synchronizing a newly connected client
+ ///
+ [UnityTest]
+ public IEnumerator NetworkBehaviourSynchronization()
+ {
+ m_ServerNetworkManager.LogLevel = LogLevel.Normal;
+ m_CurrentLogLevel = LogLevel.Normal;
+ NetworkBehaviourSynchronizeFailureComponent.ResetBehaviour();
- Assert.IsNotNull(gameObject);
+ var synchronizationObject = SpawnObject(m_SynchronizationPrefab, m_ServerNetworkManager);
+ var synchronizationBehaviour = synchronizationObject.GetComponent();
+ synchronizationBehaviour.AssignNextFailureType();
+
+ // Spawn 11 more NetworkObjects where there should be 4 of each failure type
+ for (int i = 0; i < 11; i++)
+ {
+ synchronizationObject = SpawnObject(m_SynchronizationPrefab, m_ServerNetworkManager);
+ synchronizationBehaviour = synchronizationObject.GetComponent();
+ synchronizationBehaviour.AssignNextFailureType();
+ }
+
+ // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
+ yield return CreateAndStartNewClient();
+ }
+ }
+
+ ///
+ /// A test NetworkBeahviour that provides a varying NetworkList size as well as
+ /// additional NetworkVariables to assure if a NetworkObject fails to be created
+ /// the synchronization process will continue (i.e. it will skip over that block
+ /// of the reader buffer).
+ ///
+ public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour
+ {
+ public static int ServerSpawnCount { get; internal set; }
+ public static int ClientSpawnCount { get; internal set; }
+
+ public static void ResetSpawnCount()
+ {
+ ServerSpawnCount = 0;
+ ClientSpawnCount = 0;
+ }
- var networkObject = gameObject.AddComponent();
+ private const uint k_MinDataBlocks = 1;
+ private const uint k_MaxDataBlocks = 64;
- var networkVariableComponent = gameObject.AddComponent();
- Assert.IsNotNull(networkVariableComponent);
+ // Add various types of NetworkVariables
+ public NetworkList NetworkVariableData1;
+ public NetworkVariable NetworkVariableData2;
+ public NetworkVariable NetworkVariableData3;
+ public NetworkVariable NetworkVariableData4;
- Assert.IsNotNull(networkObject);
+ private void Awake()
+ {
+ var dataBlocksAssigned = new List();
+ var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks);
+ for (var i = 0; i < numberDataBlocks; i++)
+ {
+ dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue));
+ }
- networkObject.GlobalObjectIdHash = (uint)(i + 4096);
+ NetworkVariableData1 = new NetworkList(dataBlocksAssigned);
+ NetworkVariableData2 = new NetworkVariable(Random.Range(1, 1000));
+ NetworkVariableData3 = new NetworkVariable(Random.Range(1, 1000));
+ NetworkVariableData4 = new NetworkVariable((byte)Random.Range(1, 255));
+ }
- networkObjectsToTest.Add(gameObject);
+ public override void OnNetworkSpawn()
+ {
+ if (IsServer)
+ {
+ ServerSpawnCount++;
+ }
+ else
+ {
+ ClientSpawnCount++;
+ }
- writer.WriteValueSafe(networkObject.GetSceneOriginHandle());
+ base.OnNetworkSpawn();
+ }
+ }
- // Handle populating the scenes loaded list
- var scene = networkObject.gameObject.scene;
+ ///
+ /// A test NetworkBeahviour that simulates various types of synchronization failures
+ /// and provides a synchronization success version to validate that synchronization
+ /// will continue if user synchronization code fails.
+ ///
+ public class NetworkBehaviourSynchronizeFailureComponent : NetworkBehaviour
+ {
+ public static int ServerSpawnCount { get; internal set; }
+ public static int ClientSpawnCount { get; internal set; }
- if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded.ContainsKey(
- scene.handle))
- {
- NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenesLoaded
- .Add(scene.handle, scene);
- }
- var handle = networkObject.GetSceneOriginHandle();
- // Since this is a unit test, we will fake the server to client handle lookup by just adding the same handle key and value
- if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle
- .ContainsKey(handle))
- {
- NetworkManagerHelper.NetworkManagerObject.SceneManager.ServerSceneHandleToClientSceneHandle
- .Add(handle, handle);
- }
+ private static FailureTypes s_FailureType = FailureTypes.None;
- // Serialize the valid NetworkObject
- var sceneObject = networkObject.GetMessageSceneObject(0);
- sceneObject.Serialize(writer);
+ public enum FailureTypes
+ {
+ None,
+ DuringWriting,
+ DuringReading
+ }
- if (!NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.ContainsKey(
- networkObject.GlobalObjectIdHash))
- {
- NetworkManagerHelper.NetworkManagerObject.SceneManager.ScenePlacedObjects.Add(
- networkObject.GlobalObjectIdHash, new Dictionary());
- }
+ public static void ResetBehaviour()
+ {
+ ServerSpawnCount = 0;
+ ClientSpawnCount = 0;
+ s_FailureType = FailureTypes.None;
+ }
- // Add this valid NetworkObject into the ScenePlacedObjects list
- NetworkManagerHelper.NetworkManagerObject.SceneManager
- .ScenePlacedObjects[networkObject.GlobalObjectIdHash]
- .Add(SceneManager.GetActiveScene().handle, networkObject);
- }
- }
+ private MyCustomData m_MyCustomData;
- var totalBufferSize = writer.Position;
+ private struct MyCustomData : INetworkSerializable
+ {
+ public FailureTypes FailureType;
+ private ushort m_DataSize;
+ private byte[] m_DataBlock;
- var reader = new FastBufferReader(writer, Allocator.Temp);
- using (reader)
+ public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter
+ {
+ if (serializer.IsWriter)
{
-
- var networkObjectsDeSerialized = new List();
- var currentLogLevel = NetworkManager.Singleton.LogLevel;
- var invalidNetworkObjectCount = 0;
- while (reader.Position != totalBufferSize)
+ var writer = serializer.GetFastBufferWriter();
+ switch (FailureType)
{
- // If we reach the point where we expect it to fail, then make sure we let TestRunner know it should expect this log error message
- if (invalidNetworkObjectOffsets.Count > 0 &&
- reader.Position == invalidNetworkObjectOffsets[0])
- {
- invalidNetworkObjectOffsets.RemoveAt(0);
-
- // Turn off Network Logging to avoid other errors that we know will happen after the below LogAssert.Expect message occurs.
- NetworkManager.Singleton.LogLevel = LogLevel.Nothing;
-
- // Trap for this specific error message so we don't make Test Runner think we failed (it will fail on Debug.LogError)
- UnityEngine.TestTools.LogAssert.Expect(LogType.Error,
- $"Failed to spawn {nameof(NetworkObject)} for Hash {invalidNetworkObjectIdCount[invalidNetworkObjectCount]}.");
-
- invalidNetworkObjectCount++;
- }
-
-
- reader.ReadValueSafe(out int handle);
- NetworkManagerHelper.NetworkManagerObject.SceneManager.SetTheSceneBeingSynchronized(handle);
- var sceneObject = new NetworkObject.SceneObject();
- sceneObject.Deserialize(reader);
-
- var deserializedNetworkObject = NetworkObject.AddSceneObject(sceneObject, reader,
- NetworkManagerHelper.NetworkManagerObject);
- if (deserializedNetworkObject != null)
- {
- networkObjectsDeSerialized.Add(deserializedNetworkObject);
- }
- else
- {
- // Under this condition, we are expecting null (i.e. no NetworkObject instantiated)
- // and will set our log level back to the original value to assure the valid NetworkObjects
- // aren't causing any log Errors to occur
- NetworkManager.Singleton.LogLevel = currentLogLevel;
- }
+ case FailureTypes.None:
+ case FailureTypes.DuringReading:
+ {
+ writer.WriteValueSafe(m_DataSize);
+ for (int i = 0; i < m_DataSize; i++)
+ {
+ writer.WriteValueSafe(m_DataBlock[i]);
+ }
+ break;
+ }
+ case FailureTypes.DuringWriting:
+ {
+ writer.WriteValueSafe(m_DataSize);
+ // Try to write past the allocated size to generate an exception
+ // while also filling the buffer to verify that the buffer will be
+ // reset back to the original position.
+ for (int i = 0; i <= m_DataSize; i++)
+ {
+ writer.WriteValueSafe(m_DataBlock[i]);
+ }
+ break;
+ }
}
-
- // Now validate all NetworkObjects returned against the original NetworkObjects we created
- // after they validate, destroy the objects
- foreach (var entry in networkObjectsToTest)
+ }
+ else
+ {
+ var reader = serializer.GetFastBufferReader();
+ switch (FailureType)
{
- var entryNetworkObject = entry.GetComponent();
- Assert.IsTrue(networkObjectsDeSerialized.Contains(entryNetworkObject));
- Object.Destroy(entry);
+ case FailureTypes.None:
+ {
+ reader.ReadValueSafe(out m_DataSize);
+ m_DataBlock = new byte[m_DataSize];
+ for (int i = 0; i < m_DataSize; i++)
+ {
+ reader.ReadValueSafe(out m_DataBlock[i]);
+ }
+ break;
+ }
+ case FailureTypes.DuringReading:
+ {
+ reader.ReadValueSafe(out m_DataSize);
+ // Allocate more space than needed
+ m_DataBlock = new byte[(int)(m_DataSize * 1.5f)];
+ // Now read past the size of this message to verify
+ // that the reader will get rest back to the appropriate
+ // position and an error will be generated for this
+ for (int i = 0; i < m_DataBlock.Length; i++)
+ {
+ reader.ReadValueSafe(out m_DataBlock[i]);
+ }
+ break;
+ }
}
}
}
- // Destroy the invalid network objects
- foreach (var entry in invalidNetworkObjects)
+ public void GenerateData(ushort size)
{
- Object.Destroy(entry);
+ m_DataSize = size;
+ m_DataBlock = new byte[size];
+ for (int i = 0; i < m_DataSize; i++)
+ {
+ m_DataBlock[i] = (byte)Random.Range(0, 512);
+ }
}
}
- [SetUp]
- public void Setup()
+ // This NetworkVariable is synchronized before OnSynchronize is invoked
+ // which enables us to perform the tests.
+ // Users could follow the same pattern for game assets and synchronize
+ // clients based on NetworkVariable settings. (i.e. a specific NPC type or the like)
+ private NetworkVariable m_FailureType;
+
+ public void AssignNextFailureType()
+ {
+ var currentPosition = (int)s_FailureType;
+ currentPosition = (++currentPosition) % 3;
+ s_FailureType = (FailureTypes)currentPosition;
+ m_FailureType.Value = s_FailureType;
+ }
+
+
+ private void Awake()
+ {
+ m_FailureType = new NetworkVariable();
+ m_MyCustomData = new MyCustomData();
+ }
+
+ public override void OnNetworkSpawn()
{
- // Create, instantiate, and host
- NetworkManagerHelper.StartNetworkManager(out NetworkManager networkManager, NetworkManagerHelper.NetworkManagerOperatingMode.None);
- networkManager.NetworkConfig.EnableSceneManagement = true;
- networkManager.StartHost();
+ if (IsServer)
+ {
+ ServerSpawnCount++;
+ m_MyCustomData.GenerateData((ushort)Random.Range(1, 512));
+ }
+ else
+ {
+ ClientSpawnCount++;
+ }
+ base.OnNetworkSpawn();
}
- [TearDown]
- public void TearDown()
+ protected override void OnSynchronize(ref BufferSerializer serializer)
{
- // Stop, shutdown, and destroy
- NetworkManagerHelper.ShutdownNetworkManager();
+ // Assign the failure type first
+ m_MyCustomData.FailureType = m_FailureType.Value;
+ // Now handle the serialization for this failure type
+ m_MyCustomData.NetworkSerialize(serializer);
}
}
///
- /// A simple test class that will provide varying NetworkBuffer stream sizes
- /// when the NetworkVariable is serialized
+ /// A test NetworkBeahviour that has varying permissions in order to validate that
+ /// when variable length safety checks are off NetworkVariables still are updated
+ /// properly.
///
- public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour
+ public class NetworkBehaviourWithOwnerNetworkVariables : NetworkBehaviour
{
- private const uint k_MinDataBlocks = 1;
- private const uint k_MaxDataBlocks = 64;
+ public static NetworkBehaviourWithOwnerNetworkVariables ServerSideClientInstance;
- public NetworkList NetworkVariableData;
+ public NetworkVariable NetworkVariableData1 = new NetworkVariable(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
+ public NetworkVariable NetworkVariableData2 = new NetworkVariable();
+ public NetworkVariable NetworkVariableData3 = new NetworkVariable(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
- private void Awake()
+ public override void OnNetworkSpawn()
{
- var dataBlocksAssigned = new List();
- var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks);
- for (var i = 0; i < numberDataBlocks; i++)
+ if (IsServer && !IsOwner)
{
- dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue));
+ ServerSideClientInstance = this;
+ NetworkVariableData1.Value = Random.Range(1, 1000);
+ NetworkVariableData2.Value = Random.Range(1, 1000);
+ NetworkVariableData3.Value = (byte)Random.Range(1, 255);
}
-
- NetworkVariableData = new NetworkList(dataBlocksAssigned);
}
}
}
From 86f80876494a2d1dd97acf42d9d83b14f4280106 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Sun, 6 Nov 2022 17:03:09 -0600
Subject: [PATCH 03/26] update
This updates NetworkTransform so that it will properly synchronize when placed on nested NetworkBehaviours (i.e. their GameObject has no NetworkObject component).
---
.../Components/NetworkTransform.cs | 28 +++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs
index f8d4dbd6a7..0234a0d168 100644
--- a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs
+++ b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs
@@ -226,6 +226,7 @@ internal void ClearBitSetForNextTick()
public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter
{
+
serializer.SerializeValue(ref SentTime);
// InLocalSpace + HasXXX Bits
serializer.SerializeValue(ref m_Bitset);
@@ -451,6 +452,33 @@ internal NetworkTransformState GetLastSentState()
return m_LastSentState;
}
+ ///
+ /// This is invoked when a new client joins (server and client sides)
+ /// Server Side: Serializes as if we were teleporting (everything is sent via NetworkTransformState)
+ /// Client Side: Adds the interpolated state which applies the NetworkTransformState as well
+ ///
+ protected override void OnSynchronize(ref BufferSerializer serializer)
+ {
+ // We don't need to synchronize NetworkTransforms that are on the same
+ // GameObject as the NetworkObject.
+ if (NetworkObject.gameObject == gameObject)
+ {
+ return;
+ }
+ var synchronizationState = new NetworkTransformState();
+ if (serializer.IsWriter)
+ {
+ synchronizationState.IsTeleportingNextFrame = true;
+ ApplyTransformToNetworkStateWithInfo(ref synchronizationState, m_CachedNetworkManager.LocalTime.Time, transform);
+ synchronizationState.NetworkSerialize(serializer);
+ }
+ else
+ {
+ synchronizationState.NetworkSerialize(serializer);
+ AddInterpolatedState(synchronizationState);
+ }
+ }
+
///
/// This will try to send/commit the current transform delta states (if any)
///
From 097ecbbfd13e265a3fce1255bb9b9b331fb16819 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Sun, 6 Nov 2022 17:04:24 -0600
Subject: [PATCH 04/26] test manual
This include a nested NetworkTransform manual test to visually validate the nested NetworkTransform update.
---
.../Assets/Tests/Manual/ManualTestsMenu.unity | 2 +
.../Tests/Manual/NestedNetworkTransforms.meta | 8 +
.../NestedNetworkTransforms/ChildMover.cs | 25 +
.../ChildMover.cs.meta | 11 +
.../ChildMoverManager.cs | 98 ++
.../ChildMoverManager.cs.meta | 11 +
.../NestedNetworkTransforms.unity | 1115 +++++++++++++++++
.../NestedNetworkTransforms.unity.meta | 7 +
.../NestedNetworkTransformsReference.asset | 19 +
...estedNetworkTransformsReference.asset.meta | 8 +
.../PlayerCube Variant.prefab | 398 ++++++
.../PlayerCube Variant.prefab.meta | 7 +
.../ProjectSettings/EditorBuildSettings.asset | 3 +
13 files changed, 1712 insertions(+)
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms.meta
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs.meta
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs.meta
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity.meta
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset.meta
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab
create mode 100644 testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab.meta
diff --git a/testproject/Assets/Tests/Manual/ManualTestsMenu.unity b/testproject/Assets/Tests/Manual/ManualTestsMenu.unity
index 0781d337fa..8ca598ab66 100644
--- a/testproject/Assets/Tests/Manual/ManualTestsMenu.unity
+++ b/testproject/Assets/Tests/Manual/ManualTestsMenu.unity
@@ -698,6 +698,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
m_Name:
m_EditorClassIdentifier:
+ m_SendPointerHoverToParent: 1
m_HorizontalAxis: Horizontal
m_VerticalAxis: Vertical
m_SubmitButton: Submit
@@ -923,6 +924,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_SceneMenus:
+ - {fileID: 11400000, guid: 2c0ff1138526d4041a875c84f7114513, type: 2}
- {fileID: 11400000, guid: 83bc07221c884b24e968f464eaccce26, type: 2}
- {fileID: 11400000, guid: 644bd35b81cd40c4d88002a24e223462, type: 2}
- {fileID: 11400000, guid: 33e25ac2a2f551c4db75a9e960a6bc2f, type: 2}
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms.meta
new file mode 100644
index 0000000000..9f0f5283cd
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8a96c655551fade40adc673dd12bf67a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
new file mode 100644
index 0000000000..f59fe06ddb
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
@@ -0,0 +1,25 @@
+using UnityEngine;
+using Unity.Netcode;
+
+namespace TestProject.ManualTests
+{
+ public class ChildMover : NetworkBehaviour
+ {
+ [Range(0.1f, 30.0f)]
+ public float RotationSpeed = 5.0f;
+
+ public void PlayerIsMoving(bool forward)
+ {
+ if (IsSpawned && IsOwner)
+ {
+ var rotateDirection = forward ? RotationSpeed : -1 * RotationSpeed;
+ transform.RotateAround(transform.parent.position, Vector3.up, rotateDirection);
+ }
+ }
+
+ public override void OnNetworkSpawn()
+ {
+ base.OnNetworkSpawn();
+ }
+ }
+}
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs.meta
new file mode 100644
index 0000000000..8d284ce0c3
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0d8ad30fca3f9a240bdce16f0166033b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
new file mode 100644
index 0000000000..e4afa47470
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
@@ -0,0 +1,98 @@
+using System.Collections.Generic;
+using UnityEngine;
+using Unity.Netcode;
+
+namespace TestProject.ManualTests
+{
+ public class ChildMoverManager : NetworkBehaviour
+ {
+ public List ChildMovers;
+
+ [Range(0.001f, 5.0f)]
+ public float TriggerDistanceToMove = 0.2f;
+ private Vector3 m_LastPosition;
+ private Vector3 m_LastForward;
+ private Camera m_MainCamera;
+ private Camera m_PlayerViewCamera;
+
+
+ public override void OnNetworkSpawn()
+ {
+ if (IsOwner)
+ {
+ m_LastPosition = transform.position;
+ m_LastForward = transform.forward;
+
+ for (int i = 0; i < Camera.allCamerasCount; i++)
+ {
+ var camera = Camera.allCameras[i];
+ if (camera.name == "Main Camera")
+ {
+ m_MainCamera = Camera.allCameras[i];
+ }
+ else if (camera.name == "PlayerView")
+ {
+ m_PlayerViewCamera = camera;
+ }
+ }
+
+ if (m_PlayerViewCamera != null)
+ {
+ m_PlayerViewCamera.enabled = false;
+ m_PlayerViewCamera.transform.parent = transform;
+ }
+ }
+ base.OnNetworkSpawn();
+ }
+
+ private void Update()
+ {
+ if (IsOwner && IsSpawned)
+ {
+ var deltaPosition = (transform.position - m_LastPosition);
+ if (deltaPosition.sqrMagnitude >= (TriggerDistanceToMove * TriggerDistanceToMove))
+ {
+ // Get our movement direction
+ var movementDirection = Vector3.Dot(deltaPosition.normalized, transform.forward);
+ var rotationDirection = Vector3.zero;
+ if (movementDirection >= 0)
+ {
+ rotationDirection = Vector3.Cross(m_LastForward, transform.forward);
+ }
+ else
+ {
+ rotationDirection = Vector3.Cross(transform.forward, m_LastForward);
+ }
+
+ movementDirection *= rotationDirection.y < 0 ? -1 : 1;
+ bool forward = true;
+ if (movementDirection < 0 )
+ {
+ forward = false;
+ }
+ m_LastPosition = transform.position;
+ m_LastForward = transform.forward;
+ foreach (var childMover in ChildMovers)
+ {
+ childMover.PlayerIsMoving(forward);
+ }
+ }
+
+ if (Input.GetKeyDown(KeyCode.P) && m_PlayerViewCamera != null && m_MainCamera != null)
+ {
+ if(m_MainCamera.enabled)
+ {
+ m_MainCamera.enabled = false;
+ m_PlayerViewCamera.enabled = true;
+ }
+ else
+ {
+ m_MainCamera.enabled = true;
+ m_PlayerViewCamera.enabled = false;
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs.meta
new file mode 100644
index 0000000000..b17549dd15
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 482bb1f796fe43348bcbfd8161ed3825
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
new file mode 100644
index 0000000000..6a38a5c843
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
@@ -0,0 +1,1115 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 9
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+ m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+ m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 0
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 0}
+ m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1}
+ m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 12
+ m_GIWorkflowMode: 1
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 1
+ m_EnableRealtimeLightmaps: 0
+ m_LightmapEditorSettings:
+ serializedVersion: 12
+ m_Resolution: 2
+ m_BakeResolution: 40
+ m_AtlasSize: 1024
+ m_AO: 0
+ m_AOMaxDistance: 1
+ m_CompAOExponent: 1
+ m_CompAOExponentDirect: 0
+ m_ExtractAmbientOcclusion: 0
+ m_Padding: 2
+ m_LightmapParameters: {fileID: 0}
+ m_LightmapsBakeMode: 1
+ m_TextureCompression: 1
+ m_FinalGather: 0
+ m_FinalGatherFiltering: 1
+ m_FinalGatherRayCount: 256
+ m_ReflectionCompression: 2
+ m_MixedBakeMode: 2
+ m_BakeBackend: 1
+ m_PVRSampling: 1
+ m_PVRDirectSampleCount: 32
+ m_PVRSampleCount: 512
+ m_PVRBounces: 2
+ m_PVREnvironmentSampleCount: 256
+ m_PVREnvironmentReferencePointCount: 2048
+ m_PVRFilteringMode: 1
+ m_PVRDenoiserTypeDirect: 1
+ m_PVRDenoiserTypeIndirect: 1
+ m_PVRDenoiserTypeAO: 1
+ m_PVRFilterTypeDirect: 0
+ m_PVRFilterTypeIndirect: 0
+ m_PVRFilterTypeAO: 0
+ m_PVREnvironmentMIS: 1
+ m_PVRCulling: 1
+ m_PVRFilteringGaussRadiusDirect: 1
+ m_PVRFilteringGaussRadiusIndirect: 5
+ m_PVRFilteringGaussRadiusAO: 2
+ m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+ m_PVRFilteringAtrousPositionSigmaIndirect: 2
+ m_PVRFilteringAtrousPositionSigmaAO: 1
+ m_ExportTrainingData: 0
+ m_TrainingDataDestination: TrainingData
+ m_LightProbeSampleCountMultiplier: 4
+ m_LightingDataAsset: {fileID: 0}
+ m_LightingSettings: {fileID: 0}
+--- !u!196 &4
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 2
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666667
+ manualTileSize: 0
+ tileSize: 256
+ accuratePlacement: 0
+ maxJobWorkers: 0
+ preserveTilesOutsideBounds: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1 &635877837
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 635877840}
+ - component: {fileID: 635877839}
+ - component: {fileID: 635877838}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &635877838
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 635877837}
+ m_Enabled: 1
+--- !u!20 &635877839
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 635877837}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &635877840
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 635877837}
+ m_LocalRotation: {x: 0.60876137, y: 0, z: 0, w: 0.7933534}
+ m_LocalPosition: {x: 0, y: 60, z: -11}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 75, y: 0, z: 0}
+--- !u!1 &691147452
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 691147454}
+ - component: {fileID: 691147453}
+ m_Layer: 0
+ m_Name: Directional Light
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!108 &691147453
+Light:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 691147452}
+ m_Enabled: 1
+ serializedVersion: 10
+ m_Type: 1
+ m_Shape: 0
+ m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
+ m_Intensity: 1
+ m_Range: 10
+ m_SpotAngle: 30
+ m_InnerSpotAngle: 21.80208
+ m_CookieSize: 10
+ m_Shadows:
+ m_Type: 2
+ m_Resolution: -1
+ m_CustomResolution: -1
+ m_Strength: 1
+ m_Bias: 0.05
+ m_NormalBias: 0.4
+ m_NearPlane: 0.2
+ m_CullingMatrixOverride:
+ e00: 1
+ e01: 0
+ e02: 0
+ e03: 0
+ e10: 0
+ e11: 1
+ e12: 0
+ e13: 0
+ e20: 0
+ e21: 0
+ e22: 1
+ e23: 0
+ e30: 0
+ e31: 0
+ e32: 0
+ e33: 1
+ m_UseCullingMatrixOverride: 0
+ m_Cookie: {fileID: 0}
+ m_DrawHalo: 0
+ m_Flare: {fileID: 0}
+ m_RenderMode: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingLayerMask: 1
+ m_Lightmapping: 4
+ m_LightShadowCasterMode: 0
+ m_AreaSize: {x: 1, y: 1}
+ m_BounceIntensity: 1
+ m_ColorTemperature: 6570
+ m_UseColorTemperature: 0
+ m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+ m_UseBoundingSphereOverride: 0
+ m_UseViewFrustumForShadowCasterCull: 1
+ m_ShadowRadius: 0
+ m_ShadowAngle: 0
+--- !u!4 &691147454
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 691147452}
+ m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
+ m_LocalPosition: {x: 0, y: 3, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
+--- !u!1001 &759287026
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 1107190083}
+ m_Modifications:
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_Pivot.x
+ value: 0.5
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_Pivot.y
+ value: 0.5
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_RootOrder
+ value: 2
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_AnchorMax.x
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_AnchorMax.y
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_AnchorMin.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_AnchorMin.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_SizeDelta.x
+ value: -952
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_SizeDelta.y
+ value: -344
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_AnchoredPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_AnchoredPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 6963777608485144162, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ propertyPath: m_Name
+ value: ConnectionModeButtons
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: d725b5588e1b956458798319e6541d84, type: 3}
+--- !u!224 &759287027 stripped
+RectTransform:
+ m_CorrespondingSourceObject: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84,
+ type: 3}
+ m_PrefabInstance: {fileID: 759287026}
+ m_PrefabAsset: {fileID: 0}
+--- !u!1 &1107190079
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1107190083}
+ - component: {fileID: 1107190082}
+ - component: {fileID: 1107190081}
+ - component: {fileID: 1107190080}
+ m_Layer: 5
+ m_Name: Canvas
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1107190080
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1107190079}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreReversedGraphics: 1
+ m_BlockingObjects: 0
+ m_BlockingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+--- !u!114 &1107190081
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1107190079}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_UiScaleMode: 0
+ m_ReferencePixelsPerUnit: 100
+ m_ScaleFactor: 1
+ m_ReferenceResolution: {x: 800, y: 600}
+ m_ScreenMatchMode: 0
+ m_MatchWidthOrHeight: 0
+ m_PhysicalUnit: 3
+ m_FallbackScreenDPI: 96
+ m_DefaultSpriteDPI: 96
+ m_DynamicPixelsPerUnit: 1
+ m_PresetInfoIsWorld: 0
+--- !u!223 &1107190082
+Canvas:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1107190079}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_RenderMode: 0
+ m_Camera: {fileID: 0}
+ m_PlaneDistance: 100
+ m_PixelPerfect: 0
+ m_ReceivesEvents: 1
+ m_OverrideSorting: 0
+ m_OverridePixelPerfect: 0
+ m_SortingBucketNormalizedSize: 0
+ m_AdditionalShaderChannelsFlag: 0
+ m_SortingLayerID: 0
+ m_SortingOrder: 0
+ m_TargetDisplay: 0
+--- !u!224 &1107190083
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1107190079}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0, y: 0, z: 0}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1136626022}
+ - {fileID: 1200545880}
+ - {fileID: 759287027}
+ - {fileID: 1622867933}
+ m_Father: {fileID: 0}
+ m_RootOrder: 3
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0, y: 0}
+--- !u!1 &1136626021
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1136626022}
+ - component: {fileID: 1136626026}
+ - component: {fileID: 1136626025}
+ - component: {fileID: 1136626024}
+ - component: {fileID: 1136626023}
+ m_Layer: 5
+ m_Name: ServerHostClientDisplay
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &1136626022
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1136626021}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1107190083}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 0}
+ m_AnchorMax: {x: 0.5, y: 0}
+ m_AnchoredPosition: {x: 0, y: 31}
+ m_SizeDelta: {x: 200, y: 30}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!114 &1136626023
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1136626021}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ GlobalObjectIdHash: 1789729126
+ AlwaysReplicateAsRoot: 0
+ DontDestroyWithOwner: 0
+ AutoObjectParentSync: 1
+--- !u!114 &1136626024
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1136626021}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 7ea6e834b18e2c840a76ce574eb4b144, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_DisplayText: {fileID: 1136626025}
+--- !u!114 &1136626025
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1136626021}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 0.5058824, b: 0.003921569, a: 1}
+ m_RaycastTarget: 1
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 24
+ m_FontStyle: 1
+ m_BestFit: 0
+ m_MinSize: 1
+ m_MaxSize: 40
+ m_Alignment: 4
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 0
+ m_Text: Server
+--- !u!222 &1136626026
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1136626021}
+ m_CullTransparentMesh: 1
+--- !u!1001 &1200545879
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 1107190083}
+ m_Modifications:
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_Pivot.x
+ value: 0.5
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_Pivot.y
+ value: 0.5
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_RootOrder
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_AnchorMax.x
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_AnchorMax.y
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_AnchorMin.x
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_AnchorMin.y
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_SizeDelta.x
+ value: 20
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_SizeDelta.y
+ value: 25
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalScale.x
+ value: 0.9806
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalScale.y
+ value: 0.9806
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalScale.z
+ value: 0.9806
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: -0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_AnchoredPosition.x
+ value: -22.946533
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_AnchoredPosition.y
+ value: -23.338623
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2848221156307247795, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_Name
+ value: ExitButton
+ objectReference: {fileID: 0}
+ - target: {fileID: 5266522511616468950, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ propertyPath: m_SceneMenuToLoad
+ value:
+ objectReference: {fileID: 11400000, guid: 4a3cdce12e998384f8aca207b5a2c700,
+ type: 2}
+ m_RemovedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: 3200770c16e3b2b4ebe7f604154faac7, type: 3}
+--- !u!224 &1200545880 stripped
+RectTransform:
+ m_CorrespondingSourceObject: {fileID: 2848221156307247792, guid: 3200770c16e3b2b4ebe7f604154faac7,
+ type: 3}
+ m_PrefabInstance: {fileID: 1200545879}
+ m_PrefabAsset: {fileID: 0}
+--- !u!1 &1460331769
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1460331772}
+ - component: {fileID: 1460331771}
+ - component: {fileID: 1460331770}
+ m_Layer: 0
+ m_Name: PlayerView
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &1460331770
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1460331769}
+ m_Enabled: 1
+--- !u!20 &1460331771
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1460331769}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &1460331772
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1460331769}
+ m_LocalRotation: {x: 0.17364816, y: -0, z: -0, w: 0.9848078}
+ m_LocalPosition: {x: 0, y: 8, z: -10}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 5
+ m_LocalEulerAnglesHint: {x: 20, y: 0, z: 0}
+--- !u!1001 &1525117742
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_RootOrder
+ value: 4
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalScale.x
+ value: 1.5
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalScale.z
+ value: 1.5
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511854, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_Name
+ value: SceneLevelGeometry
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: fe5fd652408224242a6fea8a6f8e6d05, type: 3}
+--- !u!1 &1622867932
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1622867933}
+ - component: {fileID: 1622867935}
+ - component: {fileID: 1622867934}
+ m_Layer: 5
+ m_Name: EventSystem
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1622867933
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1622867932}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1107190083}
+ m_RootOrder: 3
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1622867934
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1622867932}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_SendPointerHoverToParent: 1
+ m_HorizontalAxis: Horizontal
+ m_VerticalAxis: Vertical
+ m_SubmitButton: Submit
+ m_CancelButton: Cancel
+ m_InputActionsPerSecond: 10
+ m_RepeatDelay: 0.5
+ m_ForceModuleActive: 0
+--- !u!114 &1622867935
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1622867932}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_FirstSelected: {fileID: 0}
+ m_sendNavigationEvents: 1
+ m_DragThreshold: 10
+--- !u!1 &1909864228
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1909864230}
+ - component: {fileID: 1909864229}
+ - component: {fileID: 1909864231}
+ m_Layer: 0
+ m_Name: NetworkManager
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1909864229
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1909864228}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 593a2fe42fa9d37498c96f9a383b6521, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ RunInBackground: 1
+ LogLevel: 0
+ NetworkConfig:
+ ProtocolVersion: 0
+ NetworkTransport: {fileID: 1909864231}
+ PlayerPrefab: {fileID: 296612175404815451, guid: 61e89146f864ef2478af3da9d7dda1e2,
+ type: 3}
+ NetworkPrefabs: []
+ TickRate: 30
+ ClientConnectionBufferTimeout: 10
+ ConnectionApproval: 0
+ ConnectionData:
+ EnableTimeResync: 0
+ TimeResyncInterval: 30
+ EnsureNetworkVariableLengthSafety: 0
+ EnableSceneManagement: 1
+ ForceSamePrefabs: 1
+ RecycleNetworkIds: 1
+ NetworkIdRecycleDelay: 120
+ RpcHashSize: 0
+ LoadSceneTimeOut: 120
+ SpawnTimeout: 1
+ EnableNetworkLogs: 1
+--- !u!4 &1909864230
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1909864228}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1909864231
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1909864228}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 6960e84d07fb87f47956e7a81d71c4e6, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_ProtocolType: 0
+ m_MaxPacketQueueSize: 128
+ m_MaxPayloadSize: 6144
+ m_HeartbeatTimeoutMS: 500
+ m_ConnectTimeoutMS: 1000
+ m_MaxConnectAttempts: 60
+ m_DisconnectTimeoutMS: 30000
+ ConnectionData:
+ Address: 127.0.0.1
+ Port: 7777
+ ServerListenAddress:
+ DebugSimulator:
+ PacketDelayMS: 0
+ PacketJitterMS: 0
+ PacketDropRate: 0
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity.meta
new file mode 100644
index 0000000000..1fe34e9afa
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 92b8cccf28cbaba40854a025b66e2ac3
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset
new file mode 100644
index 0000000000..3414672af6
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset
@@ -0,0 +1,19 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 39a16938ffb5cd846a9f6df7a686a9c4, type: 3}
+ m_Name: NestedNetworkTransformsReference
+ m_EditorClassIdentifier:
+ SceneToReference: {fileID: 102900000, guid: 92b8cccf28cbaba40854a025b66e2ac3, type: 3}
+ m_IncludedScenes: []
+ m_DisplayName: Nested NetworkTransforms
+ m_ReferencedScenes:
+ - NestedNetworkTransforms
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset.meta
new file mode 100644
index 0000000000..d242a32947
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransformsReference.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2c0ff1138526d4041a875c84f7114513
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab
new file mode 100644
index 0000000000..d67baefd50
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab
@@ -0,0 +1,398 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &772585991204072682
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1522619104359096714}
+ - component: {fileID: 6327137497379236391}
+ - component: {fileID: 2204728973112518521}
+ - component: {fileID: 1866518356433686547}
+ - component: {fileID: 2645854474244200924}
+ m_Layer: 0
+ m_Name: ChildOne
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1522619104359096714
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 2, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 296612175404815447}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!23 &6327137497379236391
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 62ff4693d71b55440a645bbccd83ac8a, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &2204728973112518521
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!114 &1866518356433686547
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0d8ad30fca3f9a240bdce16f0166033b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ RotationSpeed: 5
+--- !u!114 &2645854474244200924
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: cf01cca54b77c0241ad6d9da5ef6a709, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ SyncPositionX: 1
+ SyncPositionY: 1
+ SyncPositionZ: 1
+ SyncRotAngleX: 1
+ SyncRotAngleY: 1
+ SyncRotAngleZ: 1
+ SyncScaleX: 1
+ SyncScaleY: 1
+ SyncScaleZ: 1
+ PositionThreshold: 0.001
+ RotAngleThreshold: 0.01
+ ScaleThreshold: 0.01
+ InLocalSpace: 0
+ Interpolate: 1
+--- !u!1 &2771624607751045562
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5485086383386216104}
+ m_Layer: 0
+ m_Name: FirstLevel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &5485086383386216104
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2771624607751045562}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 4974009855568796650}
+ m_Father: {fileID: 296612175404815447}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &6292214655028195304
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4974009855568796650}
+ - component: {fileID: 3062926429781172158}
+ - component: {fileID: 1595868777393624541}
+ - component: {fileID: 2914594030718603294}
+ - component: {fileID: 2888046383252644210}
+ m_Layer: 0
+ m_Name: ChildTwo
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4974009855568796650
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: -2, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 5485086383386216104}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!23 &3062926429781172158
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 62ff4693d71b55440a645bbccd83ac8a, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &1595868777393624541
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!114 &2914594030718603294
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0d8ad30fca3f9a240bdce16f0166033b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ RotationSpeed: 5
+--- !u!114 &2888046383252644210
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: cf01cca54b77c0241ad6d9da5ef6a709, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ SyncPositionX: 1
+ SyncPositionY: 1
+ SyncPositionZ: 1
+ SyncRotAngleX: 1
+ SyncRotAngleY: 1
+ SyncRotAngleZ: 1
+ SyncScaleX: 1
+ SyncScaleY: 1
+ SyncScaleZ: 1
+ PositionThreshold: 0.001
+ RotAngleThreshold: 0.01
+ ScaleThreshold: 0.01
+ InLocalSpace: 0
+ Interpolate: 1
+--- !u!1001 &8977898853425847701
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 947981134, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+ propertyPath: GlobalObjectIdHash
+ value: 951099334
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_RootOrder
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalScale.x
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalScale.y
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalScale.z
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_Name
+ value: PlayerCube Variant
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+--- !u!4 &296612175404815447 stripped
+Transform:
+ m_CorrespondingSourceObject: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ m_PrefabInstance: {fileID: 8977898853425847701}
+ m_PrefabAsset: {fileID: 0}
+--- !u!1 &296612175404815451 stripped
+GameObject:
+ m_CorrespondingSourceObject: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ m_PrefabInstance: {fileID: 8977898853425847701}
+ m_PrefabAsset: {fileID: 0}
+--- !u!114 &4389916208190318681
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 296612175404815451}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 482bb1f796fe43348bcbfd8161ed3825, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ ChildMovers:
+ - {fileID: 1866518356433686547}
+ - {fileID: 2914594030718603294}
+ TriggerDistanceToMove: 0.25
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab.meta b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab.meta
new file mode 100644
index 0000000000..c6d325e64c
--- /dev/null
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 61e89146f864ef2478af3da9d7dda1e2
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset
index a1496f313c..15c557f6ca 100644
--- a/testproject/ProjectSettings/EditorBuildSettings.asset
+++ b/testproject/ProjectSettings/EditorBuildSettings.asset
@@ -113,6 +113,9 @@ EditorBuildSettings:
- enabled: 1
path: Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjects.unity
guid: 49fd14bff1eceda4f9299721a9029750
+ - enabled: 1
+ path: Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
+ guid: 92b8cccf28cbaba40854a025b66e2ac3
m_configObjects:
com.unity.addressableassets: {fileID: 11400000, guid: 5a3d5c53c25349c48912726ae850f3b0,
type: 2}
From d2cc250dabe0f0fcc7a52a454ec915cd307f77f8 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Sun, 6 Nov 2022 19:08:29 -0600
Subject: [PATCH 05/26] fix
Minor fix for in-scene placed parenting under a non-NetworkObject to prevent from being added to the orphaned child list.
---
.../Runtime/Spawning/NetworkSpawnManager.cs | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
index 53e145fc47..115623ac65 100644
--- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
@@ -454,8 +454,9 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneO
if (sceneObject.Header.HasParent)
{
- // Go ahead and set network parenting properties
- networkObject.SetNetworkParenting(parentNetworkId, worldPositionStays);
+ // Go ahead and set network parenting properties, if the latest parent is not set then pass in null
+ // (we always want to set worldPositionStays)
+ networkObject.SetNetworkParenting(sceneObject.IsLatestParentSet ? parentNetworkId : null, worldPositionStays);
}
From 5a44cbc35972f33aed4bb126dfd1210a17a80c30 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Sun, 6 Nov 2022 19:10:00 -0600
Subject: [PATCH 06/26] test update
minor camera related updates to the nested NetworkTransform manual test.
---
.../ChildMoverManager.cs | 28 +++----
.../NestedNetworkTransforms.unity | 84 -------------------
.../PlayerCube Variant.prefab | 76 +++++++++++++++++
3 files changed, 89 insertions(+), 99 deletions(-)
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
index e4afa47470..917528f881 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
@@ -10,11 +10,19 @@ public class ChildMoverManager : NetworkBehaviour
[Range(0.001f, 5.0f)]
public float TriggerDistanceToMove = 0.2f;
+
+ public Camera PlayerCamera;
private Vector3 m_LastPosition;
private Vector3 m_LastForward;
private Camera m_MainCamera;
- private Camera m_PlayerViewCamera;
+ private void Awake()
+ {
+ if (PlayerCamera != null)
+ {
+ PlayerCamera.enabled = false;
+ }
+ }
public override void OnNetworkSpawn()
{
@@ -30,16 +38,6 @@ public override void OnNetworkSpawn()
{
m_MainCamera = Camera.allCameras[i];
}
- else if (camera.name == "PlayerView")
- {
- m_PlayerViewCamera = camera;
- }
- }
-
- if (m_PlayerViewCamera != null)
- {
- m_PlayerViewCamera.enabled = false;
- m_PlayerViewCamera.transform.parent = transform;
}
}
base.OnNetworkSpawn();
@@ -78,17 +76,17 @@ private void Update()
}
}
- if (Input.GetKeyDown(KeyCode.P) && m_PlayerViewCamera != null && m_MainCamera != null)
+ if (Input.GetKeyDown(KeyCode.C) && PlayerCamera != null && m_MainCamera != null)
{
- if(m_MainCamera.enabled)
+ if (m_MainCamera.isActiveAndEnabled)
{
+ PlayerCamera.enabled = true;
m_MainCamera.enabled = false;
- m_PlayerViewCamera.enabled = true;
}
else
{
m_MainCamera.enabled = true;
- m_PlayerViewCamera.enabled = false;
+ PlayerCamera.enabled = false;
}
}
}
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
index 6a38a5c843..214ec8540c 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
@@ -787,90 +787,6 @@ RectTransform:
type: 3}
m_PrefabInstance: {fileID: 1200545879}
m_PrefabAsset: {fileID: 0}
---- !u!1 &1460331769
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 1460331772}
- - component: {fileID: 1460331771}
- - component: {fileID: 1460331770}
- m_Layer: 0
- m_Name: PlayerView
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!81 &1460331770
-AudioListener:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1460331769}
- m_Enabled: 1
---- !u!20 &1460331771
-Camera:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1460331769}
- m_Enabled: 1
- serializedVersion: 2
- m_ClearFlags: 1
- m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
- m_projectionMatrixMode: 1
- m_GateFitMode: 2
- m_FOVAxisMode: 0
- m_SensorSize: {x: 36, y: 24}
- m_LensShift: {x: 0, y: 0}
- m_FocalLength: 50
- m_NormalizedViewPortRect:
- serializedVersion: 2
- x: 0
- y: 0
- width: 1
- height: 1
- near clip plane: 0.3
- far clip plane: 1000
- field of view: 60
- orthographic: 0
- orthographic size: 5
- m_Depth: -1
- m_CullingMask:
- serializedVersion: 2
- m_Bits: 4294967295
- m_RenderingPath: -1
- m_TargetTexture: {fileID: 0}
- m_TargetDisplay: 0
- m_TargetEye: 3
- m_HDR: 1
- m_AllowMSAA: 1
- m_AllowDynamicResolution: 0
- m_ForceIntoRT: 0
- m_OcclusionCulling: 1
- m_StereoConvergence: 10
- m_StereoSeparation: 0.022
---- !u!4 &1460331772
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 1460331769}
- m_LocalRotation: {x: 0.17364816, y: -0, z: -0, w: 0.9848078}
- m_LocalPosition: {x: 0, y: 8, z: -10}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 0}
- m_RootOrder: 5
- m_LocalEulerAnglesHint: {x: 20, y: 0, z: 0}
--- !u!1001 &1525117742
PrefabInstance:
m_ObjectHideFlags: 0
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab
index d67baefd50..7dd9279aa0 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/PlayerCube Variant.prefab
@@ -156,6 +156,81 @@ Transform:
m_Father: {fileID: 296612175404815447}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &4147667212972069939
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7199215624742357828}
+ - component: {fileID: 8372809022110481992}
+ m_Layer: 0
+ m_Name: PlayerView
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &7199215624742357828
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4147667212972069939}
+ m_LocalRotation: {x: 0.17364816, y: -0, z: -0, w: 0.9848078}
+ m_LocalPosition: {x: 0, y: 8, z: -10}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 296612175404815447}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 20, y: 0, z: 0}
+--- !u!20 &8372809022110481992
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4147667212972069939}
+ m_Enabled: 0
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
--- !u!1 &6292214655028195304
GameObject:
m_ObjectHideFlags: 0
@@ -396,3 +471,4 @@ MonoBehaviour:
- {fileID: 1866518356433686547}
- {fileID: 2914594030718603294}
TriggerDistanceToMove: 0.25
+ PlayerCamera: {fileID: 8372809022110481992}
From 3a7d550994a066fcd1f36612ca6351a5d22be051 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Sun, 6 Nov 2022 19:15:22 -0600
Subject: [PATCH 07/26] style
finishing a comment
---
com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
index 5495f8a269..582c334772 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
@@ -918,7 +918,8 @@ internal bool Synchronize(ref BufferSerializer serializer) where T : IRead
// event an exception occurs when deserializing.
var sizePosition = writer.Position;
writer.WriteValueSafe((ushort)0);
- // Save our position before synchronizing to determine
+
+ // Save our position before synchronizing to determine how much was written
var positionBeforeSynchronize = writer.Position;
var threwException = false;
try
From 044ea50f033f03fd83caac2589e643c10979d6ee Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Sun, 6 Nov 2022 19:25:30 -0600
Subject: [PATCH 08/26] style
Updated comments, formatting, and organizational related issues.
---
.../Runtime/Core/NetworkObject.cs | 31 +++++++++----------
.../Runtime/SceneManagement/SceneEventData.cs | 4 ---
2 files changed, 14 insertions(+), 21 deletions(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
index 35787a4d3f..f3a281350b 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
@@ -1010,7 +1010,7 @@ internal static void VerifyParentingStatus()
}
///
- /// Only invoked during first synchronization
+ /// Only invoked during first synchronization of a NetworkObject (late join or newly spawned)
///
internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
{
@@ -1156,16 +1156,15 @@ public unsafe void Serialize(FastBufferWriter writer)
}
// 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.
+ // 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 (Header.IsSceneObject)
{
writer.WriteValue(OwnerObject.GetSceneOriginHandle());
}
- // Synchronize NetworkBehaviours
+ // Synchronize NetworkVariables and NetworkBehaviours
var bufferSerializer = new BufferSerializer(new BufferSerializerWriter(writer));
OwnerObject.SynchronizeNetworkBehaviours(ref bufferSerializer, TargetClientId);
}
@@ -1177,7 +1176,6 @@ public unsafe void Deserialize(FastBufferReader reader)
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
}
reader.ReadValue(out Header);
-
var readSize = 0;
if (Header.HasParent)
{
@@ -1221,10 +1219,9 @@ public unsafe void Deserialize(FastBufferReader reader)
}
// 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.
+ // 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 (Header.IsSceneObject)
{
reader.ReadValueSafe(out NetworkSceneHandle);
@@ -1296,9 +1293,9 @@ internal void SynchronizeNetworkBehaviours(ref BufferSerializer serializer
reader.ReadValueSafe(out byte numberSynchronized);
var networkBehaviourId = (ushort)0;
- // Each NetworkBehaviour stores its NetworkBehaviourId if it writes data
- // so when deserializing the first value is the NetworkBehaviourId that
- // will have the synchronization data applied to it.
+ // 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);
@@ -1413,11 +1410,10 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Header.Hash}.");
}
- var networkBehaviourSynchronizationData = (ushort)0;
try
{
- // If we failed to load this NetworkObject, then skip past the NetworkBehaviour synchronization data
- reader.ReadValueSafe(out networkBehaviourSynchronizationData);
+ // If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data
+ reader.ReadValueSafe(out ushort networkBehaviourSynchronizationData);
reader.Seek(reader.Position + networkBehaviourSynchronizationData);
}
catch (Exception ex)
@@ -1436,6 +1432,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
// Synchronize NetworkBehaviours
var bufferSerializer = new BufferSerializer(new BufferSerializerReader(reader));
networkObject.SynchronizeNetworkBehaviours(ref bufferSerializer, networkManager.LocalClientId);
+
// Spawn the NetworkObject
networkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, sceneObject, false);
diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
index 88e5b65214..e0e77da3cc 100644
--- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
+++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
@@ -444,7 +444,6 @@ internal void WriteSceneSynchronizationData(FastBufferWriter writer)
int totalBytes = 0;
// Write the number of NetworkObjects we are serializing
- //BytePacker.WriteValuePacked(writer, m_NetworkObjectsSync.Count);
writer.WriteValueSafe(m_NetworkObjectsSync.Count);
// Serialize all NetworkObjects that are spawned
@@ -511,9 +510,6 @@ internal void SerializeScenePlacedObjects(FastBufferWriter writer)
// Write the scene handle and GlobalObjectIdHash value
for (var i = 0; i < m_DespawnedInSceneObjectsSync.Count; ++i)
{
- //BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
- //BytePacker.WriteValuePacked(writer, m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
-
writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GetSceneOriginHandle());
writer.WriteValueSafe(m_DespawnedInSceneObjectsSync[i].GlobalObjectIdHash);
}
From d9a979844ef269425d301533df50c316784edf13 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Mon, 7 Nov 2022 08:54:10 -0600
Subject: [PATCH 09/26] fix
compilation issue.
---
.../Runtime/Spawning/NetworkSpawnManager.cs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
index 115623ac65..101e615738 100644
--- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
@@ -456,7 +456,8 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneO
{
// Go ahead and set network parenting properties, if the latest parent is not set then pass in null
// (we always want to set worldPositionStays)
- networkObject.SetNetworkParenting(sceneObject.IsLatestParentSet ? parentNetworkId : null, worldPositionStays);
+ ulong? parentId = sceneObject.IsLatestParentSet ? parentNetworkId : null;
+ networkObject.SetNetworkParenting(parentId, worldPositionStays);
}
From dfa332eed1b931bdf9e1a31f3af1a641a0e716e3 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Mon, 7 Nov 2022 09:17:17 -0600
Subject: [PATCH 10/26] style
variable name and comment adjustments.
---
.../Runtime/Core/NetworkBehaviour.cs | 6 +++---
com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
index 582c334772..fea9563b8f 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
@@ -769,7 +769,7 @@ internal void MarkVariablesDirty(bool dirty)
///
///
/// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled
- /// each NetworkVariable field will be proceeded by the number of bytes
+ /// each NetworkVariable field will be preceded by the number of bytes
/// written for that specific field.
///
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
@@ -814,7 +814,7 @@ internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClie
///
///
/// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled
- /// each NetworkVariable field will be proceeded by the number of bytes
+ /// each NetworkVariable field will be preceded by the number of bytes
/// written for that specific field.
///
internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
@@ -881,7 +881,7 @@ protected NetworkObject GetNetworkObject(ulong networkId)
///
/// When overridden, this method is invoked on a NetworkBehaviour during client synchronization in order
- /// to provide the ability to inject custom synchronization information specific to the derived class.
+ /// to provide the ability to inject custom synchronization information for derived NetworkBehaviours.
///
/// The serializer to use to read and write the data.
///
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
index f3a281350b..169f175b34 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
@@ -1413,8 +1413,8 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
try
{
// If we failed to load this NetworkObject, then skip past the Network Variable and (if any) synchronization data
- reader.ReadValueSafe(out ushort networkBehaviourSynchronizationData);
- reader.Seek(reader.Position + networkBehaviourSynchronizationData);
+ reader.ReadValueSafe(out ushort networkBehaviourSynchronizationDataLength);
+ reader.Seek(reader.Position + networkBehaviourSynchronizationDataLength);
}
catch (Exception ex)
{
From 015ef326bbbd994bc9e396a29b9574fb0b3dc3b8 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Mon, 7 Nov 2022 09:59:26 -0600
Subject: [PATCH 11/26] fix
Yamato really doesn't like that conditional even though it compiles just fine.
---
.../Runtime/Spawning/NetworkSpawnManager.cs | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
index 101e615738..cbc1b5c933 100644
--- a/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
@@ -456,7 +456,11 @@ internal NetworkObject CreateLocalNetworkObject(NetworkObject.SceneObject sceneO
{
// Go ahead and set network parenting properties, if the latest parent is not set then pass in null
// (we always want to set worldPositionStays)
- ulong? parentId = sceneObject.IsLatestParentSet ? parentNetworkId : null;
+ ulong? parentId = null;
+ if (sceneObject.IsLatestParentSet)
+ {
+ parentId = parentNetworkId;
+ }
networkObject.SetNetworkParenting(parentId, worldPositionStays);
}
From 97c48af6ff97eae54560753f53c211e1ae2087f7 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Mon, 7 Nov 2022 11:55:29 -0600
Subject: [PATCH 12/26] style
white space removal
---
.../Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
index 917528f881..50af5cb009 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
@@ -48,7 +48,7 @@ private void Update()
if (IsOwner && IsSpawned)
{
var deltaPosition = (transform.position - m_LastPosition);
- if (deltaPosition.sqrMagnitude >= (TriggerDistanceToMove * TriggerDistanceToMove))
+ if (deltaPosition.sqrMagnitude >= (TriggerDistanceToMove * TriggerDistanceToMove))
{
// Get our movement direction
var movementDirection = Vector3.Dot(deltaPosition.normalized, transform.forward);
@@ -64,7 +64,7 @@ private void Update()
movementDirection *= rotationDirection.y < 0 ? -1 : 1;
bool forward = true;
- if (movementDirection < 0 )
+ if (movementDirection < 0)
{
forward = false;
}
From c469baaa95a71896b71d273cbaf7596407e5f099 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Mon, 7 Nov 2022 13:36:05 -0600
Subject: [PATCH 13/26] update
MTT-5024
updating changelog with the addition, fixes, and change
---
com.unity.netcode.gameobjects/CHANGELOG.md | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md
index 425d9dd1a1..5c88d9e65c 100644
--- a/com.unity.netcode.gameobjects/CHANGELOG.md
+++ b/com.unity.netcode.gameobjects/CHANGELOG.md
@@ -10,17 +10,26 @@ Additional documentation and release notes are available at [Multiplayer Documen
## [Unreleased]
### 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 `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.
+- 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)
+
### Fixed
+- Fixed issue where `NetworkTransform` components nested under a parent with a `NetworkObect` 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)
- 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)
+### Changed
+- 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)
+
### Removed
- Removed the `NetworkObject` auto-add and Multiplayer Tools install reminder settings from the Menu interface. (#2285)
From bad3d70cd9087226286517806ac4c1dae33d8f13 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Mon, 7 Nov 2022 16:36:09 -0600
Subject: [PATCH 14/26] test manual
Minor updates to some manual test assets.
---
testproject/Assets/Scripts/ServerHostClientText.cs | 7 +++++++
.../Tests/Manual/NestedNetworkTransforms/ChildMover.cs | 4 ++--
.../Manual/NestedNetworkTransforms/ChildMoverManager.cs | 8 ++------
.../NestedNetworkTransforms/NestedNetworkTransforms.unity | 4 ++--
4 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/testproject/Assets/Scripts/ServerHostClientText.cs b/testproject/Assets/Scripts/ServerHostClientText.cs
index a394fbf03e..4c49ca8ca1 100644
--- a/testproject/Assets/Scripts/ServerHostClientText.cs
+++ b/testproject/Assets/Scripts/ServerHostClientText.cs
@@ -7,6 +7,12 @@ public class ServerHostClientText : NetworkBehaviour
[SerializeField]
private Text m_DisplayText;
private Color m_Color;
+ private Vector3 m_LocalPosition;
+
+ private void Awake()
+ {
+ m_LocalPosition = transform.localPosition;
+ }
private void Start()
{
@@ -30,6 +36,7 @@ public override void OnNetworkSpawn()
m_DisplayText.text = "Client";
}
}
+ transform.localPosition = m_LocalPosition;
}
private void OnGUI()
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
index f59fe06ddb..6a0ead6aac 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
@@ -8,11 +8,11 @@ public class ChildMover : NetworkBehaviour
[Range(0.1f, 30.0f)]
public float RotationSpeed = 5.0f;
- public void PlayerIsMoving(bool forward)
+ public void PlayerIsMoving(float movementDirection)
{
if (IsSpawned && IsOwner)
{
- var rotateDirection = forward ? RotationSpeed : -1 * RotationSpeed;
+ var rotateDirection = movementDirection * RotationSpeed;
transform.RotateAround(transform.parent.position, Vector3.up, rotateDirection);
}
}
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
index 50af5cb009..c763918a94 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
@@ -63,16 +63,12 @@ private void Update()
}
movementDirection *= rotationDirection.y < 0 ? -1 : 1;
- bool forward = true;
- if (movementDirection < 0)
- {
- forward = false;
- }
+
m_LastPosition = transform.position;
m_LastForward = transform.forward;
foreach (var childMover in ChildMovers)
{
- childMover.PlayerIsMoving(forward);
+ childMover.PlayerIsMoving(Mathf.Sign(movementDirection));
}
}
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
index 214ec8540c..342f1a7b70 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
@@ -585,7 +585,7 @@ MonoBehaviour:
GlobalObjectIdHash: 1789729126
AlwaysReplicateAsRoot: 0
DontDestroyWithOwner: 0
- AutoObjectParentSync: 1
+ AutoObjectParentSync: 0
--- !u!114 &1136626024
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -965,7 +965,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
RunInBackground: 1
- LogLevel: 0
+ LogLevel: 1
NetworkConfig:
ProtocolVersion: 0
NetworkTransport: {fileID: 1909864231}
From 5c522a4b36839bb40e987252761e57493dc5b284 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Tue, 8 Nov 2022 13:24:13 -0600
Subject: [PATCH 15/26] fix - merge issues
fixing some minor merge issues with the byte packing updates.
---
com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 4 ++--
.../Runtime/SceneManagement/SceneEventData.cs | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
index 3a60baf42d..e39dc98766 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
@@ -1403,7 +1403,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
// Log the error that the NetworkObject failed to construct
if (networkManager.LogLevel <= LogLevel.Normal)
{
- NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Header.Hash}.");
+ NetworkLog.LogError($"Failed to spawn {nameof(NetworkObject)} for Hash {sceneObject.Hash}.");
}
try
@@ -1423,7 +1423,7 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf
// 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.Header.OwnerClientId;
+ networkObject.OwnerClientId = sceneObject.OwnerClientId;
// Synchronize NetworkBehaviours
var bufferSerializer = new BufferSerializer(new BufferSerializerReader(reader));
diff --git a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
index 04c7e562ed..8ccb84f725 100644
--- a/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
+++ b/com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs
@@ -626,7 +626,7 @@ internal void DeserializeScenePlacedObjects()
var sceneObject = new NetworkObject.SceneObject();
sceneObject.Deserialize(InternalBuffer);
- if (sceneObject.Header.IsSceneObject)
+ if (sceneObject.IsSceneObject)
{
// Set our relative scene to the NetworkObject
m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle);
@@ -855,7 +855,7 @@ internal void SynchronizeSceneNetworkObjects(NetworkManager networkManager)
sceneObject.Deserialize(InternalBuffer);
// If the sceneObject is in-scene placed, then set the scene being synchronized
- if (sceneObject.Header.IsSceneObject)
+ if (sceneObject.IsSceneObject)
{
m_NetworkManager.SceneManager.SetTheSceneBeingSynchronized(sceneObject.NetworkSceneHandle);
}
From 4e7c781109cf70ee62b665de2fd49ae8244adf7f Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Tue, 8 Nov 2022 14:45:44 -0600
Subject: [PATCH 16/26] style
removing whitespaces that somehow made their way in when merging.
---
com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
index c9333f849c..e565466804 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
@@ -804,7 +804,7 @@ internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClie
else
{
NetworkVariableFields[j].WriteField(writer);
- }
+ }
}
else // Only if EnsureNetworkVariableLengthSafety, otherwise just skip
if (NetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety)
From 47a85340d9d4858ce8a930fc1e087515afaddbaa Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Tue, 8 Nov 2022 18:14:55 -0600
Subject: [PATCH 17/26] test
Renamed NetworkObjectSceneSerializationTests to NetworkObjectSynchronizationTests.
Added a more robust/wider range of tests, also added running host or server.
Fixed an issue with late joined clients not registering all players in NetcodeIntegrationTest.
---
.../Runtime/NetcodeIntegrationTest.cs | 18 +-
.../NetworkObjectSceneSerializationTests.cs | 410 -------------
.../NetworkObjectSynchronizationTests.cs | 561 ++++++++++++++++++
...NetworkObjectSynchronizationTests.cs.meta} | 0
4 files changed, 578 insertions(+), 411 deletions(-)
delete mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs
create mode 100644 com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
rename com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/{NetworkObjectSceneSerializationTests.cs.meta => NetworkObjectSynchronizationTests.cs.meta} (100%)
diff --git a/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs b/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs
index e91eb28a1e..c924678fc4 100644
--- a/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs
+++ b/com.unity.netcode.gameobjects/TestHelpers/Runtime/NetcodeIntegrationTest.cs
@@ -431,8 +431,14 @@ private void ClientNetworkManagerPostStart(NetworkManager networkManager)
networkManager.name = $"NetworkManager - Client - {networkManager.LocalClientId}";
Assert.NotNull(networkManager.LocalClient.PlayerObject, $"{nameof(StartServerAndClients)} detected that client {networkManager.LocalClientId} does not have an assigned player NetworkObject!");
+ // Go ahead and create an entry for this new client
+ if (!m_PlayerNetworkObjects.ContainsKey(networkManager.LocalClientId))
+ {
+ m_PlayerNetworkObjects.Add(networkManager.LocalClientId, new Dictionary());
+ }
+
// Get all player instances for the current client NetworkManager instance
- var clientPlayerClones = Object.FindObjectsOfType().Where((c) => c.IsPlayerObject && c.OwnerClientId == networkManager.LocalClientId);
+ var clientPlayerClones = Object.FindObjectsOfType().Where((c) => c.IsPlayerObject && c.OwnerClientId == networkManager.LocalClientId).ToList();
// Add this player instance to each client player entry
foreach (var playerNetworkObject in clientPlayerClones)
{
@@ -446,6 +452,16 @@ private void ClientNetworkManagerPostStart(NetworkManager networkManager)
m_PlayerNetworkObjects[playerNetworkObject.NetworkManager.LocalClientId].Add(networkManager.LocalClientId, playerNetworkObject);
}
}
+
+ // For late joining clients, add the remaining (if any) cloned versions of each client's player
+ clientPlayerClones = Object.FindObjectsOfType().Where((c) => c.IsPlayerObject && c.NetworkManager == networkManager).ToList();
+ foreach (var playerNetworkObject in clientPlayerClones)
+ {
+ if (!m_PlayerNetworkObjects[networkManager.LocalClientId].ContainsKey(playerNetworkObject.OwnerClientId))
+ {
+ m_PlayerNetworkObjects[networkManager.LocalClientId].Add(playerNetworkObject.OwnerClientId, playerNetworkObject);
+ }
+ }
}
protected void ClientNetworkManagerPostStartInit()
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs
deleted file mode 100644
index 2c69a409cb..0000000000
--- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs
+++ /dev/null
@@ -1,410 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEngine.TestTools;
-using NUnit.Framework;
-using Unity.Netcode.TestHelpers.Runtime;
-using Random = UnityEngine.Random;
-
-namespace Unity.Netcode.RuntimeTests
-{
- [TestFixture(VariableLengthSafety.DisableNetVarSafety)]
- [TestFixture(VariableLengthSafety.EnabledNetVarSafety)]
- public class NetworkObjectSceneSerializationTests : NetcodeIntegrationTest
- {
- private const int k_NumberToSpawn = 30;
- protected override int NumberOfClients => 0;
-
- private GameObject m_NetworkPrefab;
- private GameObject m_InValidNetworkPrefab;
- private GameObject m_SynchronizationPrefab;
- private VariableLengthSafety m_VariableLengthSafety;
-
- private LogLevel m_CurrentLogLevel;
-
- public enum VariableLengthSafety
- {
- DisableNetVarSafety,
- EnabledNetVarSafety,
- }
-
- public NetworkObjectSceneSerializationTests(VariableLengthSafety variableLengthSafety)
- {
- m_VariableLengthSafety = variableLengthSafety;
- }
-
- protected override void OnCreatePlayerPrefab()
- {
- m_PlayerPrefab.AddComponent();
- base.OnCreatePlayerPrefab();
- }
-
- protected override void OnServerAndClientsCreated()
- {
-
- // Set the NetworkVariable Safety Check setting
- m_ServerNetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
-
- // Ignore the errors generated during this test (they are expected)
- m_ServerNetworkManager.LogLevel = LogLevel.Nothing;
-
- // Disable forcing the same prefabs to avoid failed connections
- m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false;
-
- // Create the valid network prefab
- m_NetworkPrefab = CreateNetworkObjectPrefab("ValidObject");
- m_NetworkPrefab.AddComponent();
-
- // Create the invalid network prefab (that will fail on client side)
- m_InValidNetworkPrefab = CreateNetworkObjectPrefab("InvalidObject");
- m_InValidNetworkPrefab.AddComponent();
-
- // Create the synchronization network prefab (some pass and some fail)
- m_SynchronizationPrefab = CreateNetworkObjectPrefab("SyncObject");
- m_SynchronizationPrefab.AddComponent();
-
- base.OnServerAndClientsCreated();
- }
-
- protected override void OnNewClientCreated(NetworkManager networkManager)
- {
- networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
- networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
- foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.NetworkPrefabs)
- {
- // To simulate a failure, we exclude the m_InValidNetworkPrefab from the connecting
- // client's side.
- if (networkPrefab.Prefab.name != m_InValidNetworkPrefab.name)
- {
- networkManager.NetworkConfig.NetworkPrefabs.Add(networkPrefab);
- }
- }
- // Disable forcing the same prefabs to avoid failed connections
- networkManager.NetworkConfig.ForceSamePrefabs = false;
- networkManager.LogLevel = m_CurrentLogLevel;
- base.OnNewClientCreated(networkManager);
- }
-
- [UnityTest]
- public IEnumerator NetworkObjectDeserializationFailure()
- {
- m_CurrentLogLevel = LogLevel.Nothing;
- var validSpawnedNetworkObjects = new List();
- NetworkBehaviourWithNetworkVariables.ResetSpawnCount();
-
- // Spawn NetworkObjects on the server side with half of them being the
- // invalid network prefabs to simulate NetworkObject synchronization failure
- for (int i = 0; i < k_NumberToSpawn; i++)
- {
- if (i % 2 == 0)
- {
- SpawnObject(m_InValidNetworkPrefab, m_ServerNetworkManager);
- }
- else
- {
- // Keep track of the prefabs that should successfully spawn on the client side
- validSpawnedNetworkObjects.Add(SpawnObject(m_NetworkPrefab, m_ServerNetworkManager));
- }
- }
-
- // Assure the server-side spawned all NetworkObjects
- yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.ServerSpawnCount == k_NumberToSpawn);
-
- // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
- yield return CreateAndStartNewClient();
-
- yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.ClientSpawnCount == validSpawnedNetworkObjects.Count);
-
- var serverSidePlayerComponent = NetworkBehaviourWithOwnerNetworkVariables.ServerSideClientInstance;
- var clientSidePlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent();
-
- Assert.IsTrue(serverSidePlayerComponent.NetworkVariableData1.Value == clientSidePlayerComponent.NetworkVariableData1.Value);
- Assert.IsTrue(serverSidePlayerComponent.NetworkVariableData2.Value == clientSidePlayerComponent.NetworkVariableData2.Value);
- Assert.IsTrue(serverSidePlayerComponent.NetworkVariableData3.Value == clientSidePlayerComponent.NetworkVariableData3.Value);
-
- //Validate that the connected client has spawned the
- var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
-
- foreach (var spawnedObject in validSpawnedNetworkObjects)
- {
- var spawnedNetworkObject = spawnedObject.GetComponent();
- Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedNetworkObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a {nameof(NetworkObject.NetworkObjectId)} of {spawnedNetworkObject.NetworkObjectId}");
- var clientSideObject = clientSideNetworkObjects[spawnedNetworkObject.NetworkObjectId];
- Assert.IsTrue(clientSideObject.NetworkManager == m_ClientNetworkManagers[0], $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!");
- var serverSideComponent = spawnedObject.GetComponent();
- var clientSideComponent = clientSideObject.GetComponent();
-
- Assert.IsTrue(serverSideComponent.NetworkVariableData1.Count == clientSideComponent.NetworkVariableData1.Count);
- for (int i = 0; i < serverSideComponent.NetworkVariableData1.Count; i++)
- {
- Assert.IsTrue(serverSideComponent.NetworkVariableData1[i] == clientSideComponent.NetworkVariableData1[i]);
- }
-
- Assert.IsTrue(serverSideComponent.NetworkVariableData2.Value == clientSideComponent.NetworkVariableData2.Value);
- Assert.IsTrue(serverSideComponent.NetworkVariableData3.Value == clientSideComponent.NetworkVariableData3.Value);
- Assert.IsTrue(serverSideComponent.NetworkVariableData4.Value == clientSideComponent.NetworkVariableData4.Value);
- }
- }
-
- ///
- /// This tests a mixture of failed an valid NetworkBeahviour synchronization all while synchronizing a newly connected client
- ///
- [UnityTest]
- public IEnumerator NetworkBehaviourSynchronization()
- {
- m_ServerNetworkManager.LogLevel = LogLevel.Normal;
- m_CurrentLogLevel = LogLevel.Normal;
- NetworkBehaviourSynchronizeFailureComponent.ResetBehaviour();
-
- var synchronizationObject = SpawnObject(m_SynchronizationPrefab, m_ServerNetworkManager);
- var synchronizationBehaviour = synchronizationObject.GetComponent();
- synchronizationBehaviour.AssignNextFailureType();
-
- // Spawn 11 more NetworkObjects where there should be 4 of each failure type
- for (int i = 0; i < 11; i++)
- {
- synchronizationObject = SpawnObject(m_SynchronizationPrefab, m_ServerNetworkManager);
- synchronizationBehaviour = synchronizationObject.GetComponent();
- synchronizationBehaviour.AssignNextFailureType();
- }
-
- // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
- yield return CreateAndStartNewClient();
- }
- }
-
- ///
- /// A test NetworkBeahviour that provides a varying NetworkList size as well as
- /// additional NetworkVariables to assure if a NetworkObject fails to be created
- /// the synchronization process will continue (i.e. it will skip over that block
- /// of the reader buffer).
- ///
- public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour
- {
- public static int ServerSpawnCount { get; internal set; }
- public static int ClientSpawnCount { get; internal set; }
-
- public static void ResetSpawnCount()
- {
- ServerSpawnCount = 0;
- ClientSpawnCount = 0;
- }
-
- private const uint k_MinDataBlocks = 1;
- private const uint k_MaxDataBlocks = 64;
-
- // Add various types of NetworkVariables
- public NetworkList NetworkVariableData1;
- public NetworkVariable NetworkVariableData2;
- public NetworkVariable NetworkVariableData3;
- public NetworkVariable NetworkVariableData4;
-
- private void Awake()
- {
- var dataBlocksAssigned = new List();
- var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks);
- for (var i = 0; i < numberDataBlocks; i++)
- {
- dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue));
- }
-
- NetworkVariableData1 = new NetworkList(dataBlocksAssigned);
- NetworkVariableData2 = new NetworkVariable(Random.Range(1, 1000));
- NetworkVariableData3 = new NetworkVariable(Random.Range(1, 1000));
- NetworkVariableData4 = new NetworkVariable((byte)Random.Range(1, 255));
- }
-
- public override void OnNetworkSpawn()
- {
- if (IsServer)
- {
- ServerSpawnCount++;
- }
- else
- {
- ClientSpawnCount++;
- }
-
- base.OnNetworkSpawn();
- }
- }
-
- ///
- /// A test NetworkBeahviour that simulates various types of synchronization failures
- /// and provides a synchronization success version to validate that synchronization
- /// will continue if user synchronization code fails.
- ///
- public class NetworkBehaviourSynchronizeFailureComponent : NetworkBehaviour
- {
- public static int ServerSpawnCount { get; internal set; }
- public static int ClientSpawnCount { get; internal set; }
-
- private static FailureTypes s_FailureType = FailureTypes.None;
-
- public enum FailureTypes
- {
- None,
- DuringWriting,
- DuringReading
- }
-
- public static void ResetBehaviour()
- {
- ServerSpawnCount = 0;
- ClientSpawnCount = 0;
- s_FailureType = FailureTypes.None;
- }
-
- private MyCustomData m_MyCustomData;
-
- private struct MyCustomData : INetworkSerializable
- {
- public FailureTypes FailureType;
- private ushort m_DataSize;
- private byte[] m_DataBlock;
-
- public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter
- {
- if (serializer.IsWriter)
- {
- var writer = serializer.GetFastBufferWriter();
- switch (FailureType)
- {
- case FailureTypes.None:
- case FailureTypes.DuringReading:
- {
- writer.WriteValueSafe(m_DataSize);
- for (int i = 0; i < m_DataSize; i++)
- {
- writer.WriteValueSafe(m_DataBlock[i]);
- }
- break;
- }
- case FailureTypes.DuringWriting:
- {
- writer.WriteValueSafe(m_DataSize);
- // Try to write past the allocated size to generate an exception
- // while also filling the buffer to verify that the buffer will be
- // reset back to the original position.
- for (int i = 0; i <= m_DataSize; i++)
- {
- writer.WriteValueSafe(m_DataBlock[i]);
- }
- break;
- }
- }
- }
- else
- {
- var reader = serializer.GetFastBufferReader();
- switch (FailureType)
- {
- case FailureTypes.None:
- {
- reader.ReadValueSafe(out m_DataSize);
- m_DataBlock = new byte[m_DataSize];
- for (int i = 0; i < m_DataSize; i++)
- {
- reader.ReadValueSafe(out m_DataBlock[i]);
- }
- break;
- }
- case FailureTypes.DuringReading:
- {
- reader.ReadValueSafe(out m_DataSize);
- // Allocate more space than needed
- m_DataBlock = new byte[(int)(m_DataSize * 1.5f)];
- // Now read past the size of this message to verify
- // that the reader will get rest back to the appropriate
- // position and an error will be generated for this
- for (int i = 0; i < m_DataBlock.Length; i++)
- {
- reader.ReadValueSafe(out m_DataBlock[i]);
- }
- break;
- }
- }
- }
- }
-
- public void GenerateData(ushort size)
- {
- m_DataSize = size;
- m_DataBlock = new byte[size];
- for (int i = 0; i < m_DataSize; i++)
- {
- m_DataBlock[i] = (byte)Random.Range(0, 512);
- }
- }
- }
-
- // This NetworkVariable is synchronized before OnSynchronize is invoked
- // which enables us to perform the tests.
- // Users could follow the same pattern for game assets and synchronize
- // clients based on NetworkVariable settings. (i.e. a specific NPC type or the like)
- private NetworkVariable m_FailureType;
-
- public void AssignNextFailureType()
- {
- var currentPosition = (int)s_FailureType;
- currentPosition = (++currentPosition) % 3;
- s_FailureType = (FailureTypes)currentPosition;
- m_FailureType.Value = s_FailureType;
- }
-
-
- private void Awake()
- {
- m_FailureType = new NetworkVariable();
- m_MyCustomData = new MyCustomData();
- }
-
- public override void OnNetworkSpawn()
- {
- if (IsServer)
- {
- ServerSpawnCount++;
- m_MyCustomData.GenerateData((ushort)Random.Range(1, 512));
- }
- else
- {
- ClientSpawnCount++;
- }
-
- base.OnNetworkSpawn();
- }
-
- protected override void OnSynchronize(ref BufferSerializer serializer)
- {
- // Assign the failure type first
- m_MyCustomData.FailureType = m_FailureType.Value;
- // Now handle the serialization for this failure type
- m_MyCustomData.NetworkSerialize(serializer);
- }
- }
-
- ///
- /// A test NetworkBeahviour that has varying permissions in order to validate that
- /// when variable length safety checks are off NetworkVariables still are updated
- /// properly.
- ///
- public class NetworkBehaviourWithOwnerNetworkVariables : NetworkBehaviour
- {
- public static NetworkBehaviourWithOwnerNetworkVariables ServerSideClientInstance;
-
- public NetworkVariable NetworkVariableData1 = new NetworkVariable(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
- public NetworkVariable NetworkVariableData2 = new NetworkVariable();
- public NetworkVariable NetworkVariableData3 = new NetworkVariable(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
-
- public override void OnNetworkSpawn()
- {
- if (IsServer && !IsOwner)
- {
- ServerSideClientInstance = this;
- NetworkVariableData1.Value = Random.Range(1, 1000);
- NetworkVariableData2.Value = Random.Range(1, 1000);
- NetworkVariableData3.Value = (byte)Random.Range(1, 255);
- }
- }
- }
-}
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
new file mode 100644
index 0000000000..9b7beb3d52
--- /dev/null
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
@@ -0,0 +1,561 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.TestTools;
+using NUnit.Framework;
+using Unity.Netcode.TestHelpers.Runtime;
+using Random = UnityEngine.Random;
+
+namespace Unity.Netcode.RuntimeTests
+{
+ [TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Host)]
+ [TestFixture(VariableLengthSafety.EnabledNetVarSafety, HostOrServer.Host)]
+ [TestFixture(VariableLengthSafety.DisableNetVarSafety, HostOrServer.Server)]
+ [TestFixture(VariableLengthSafety.EnabledNetVarSafety, HostOrServer.Server)]
+ public class NetworkObjectSynchronizationTests : NetcodeIntegrationTest
+ {
+ private const int k_NumberToSpawn = 30;
+ protected override int NumberOfClients => 0;
+
+ private GameObject m_NetworkPrefab;
+ private GameObject m_InValidNetworkPrefab;
+ private GameObject m_SynchronizationPrefab;
+ private VariableLengthSafety m_VariableLengthSafety;
+
+ private LogLevel m_CurrentLogLevel;
+
+ public enum VariableLengthSafety
+ {
+ DisableNetVarSafety,
+ EnabledNetVarSafety,
+ }
+
+ public NetworkObjectSynchronizationTests(VariableLengthSafety variableLengthSafety, HostOrServer hostOrServer)
+ {
+ m_VariableLengthSafety = variableLengthSafety;
+ m_UseHost = hostOrServer == HostOrServer.Host;
+ }
+
+ protected override void OnCreatePlayerPrefab()
+ {
+ m_PlayerPrefab.AddComponent();
+ base.OnCreatePlayerPrefab();
+ }
+
+ protected override void OnServerAndClientsCreated()
+ {
+
+ // Set the NetworkVariable Safety Check setting
+ m_ServerNetworkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
+
+ // Ignore the errors generated during this test (they are expected)
+ m_ServerNetworkManager.LogLevel = LogLevel.Nothing;
+
+ // Disable forcing the same prefabs to avoid failed connections
+ m_ServerNetworkManager.NetworkConfig.ForceSamePrefabs = false;
+
+ // Create the valid network prefab
+ m_NetworkPrefab = CreateNetworkObjectPrefab("ValidObject");
+ m_NetworkPrefab.AddComponent();
+
+ // Create the invalid network prefab (that will fail on client side)
+ m_InValidNetworkPrefab = CreateNetworkObjectPrefab("InvalidObject");
+ m_InValidNetworkPrefab.AddComponent();
+
+ // Create the synchronization network prefab (some pass and some fail)
+ m_SynchronizationPrefab = CreateNetworkObjectPrefab("SyncObject");
+ m_SynchronizationPrefab.AddComponent();
+ m_SynchronizationPrefab.AddComponent();
+
+ base.OnServerAndClientsCreated();
+ }
+
+ protected override void OnNewClientCreated(NetworkManager networkManager)
+ {
+ networkManager.NetworkConfig.PlayerPrefab = m_PlayerPrefab;
+ networkManager.NetworkConfig.EnsureNetworkVariableLengthSafety = m_VariableLengthSafety == VariableLengthSafety.EnabledNetVarSafety;
+ foreach (var networkPrefab in m_ServerNetworkManager.NetworkConfig.NetworkPrefabs)
+ {
+ // To simulate a failure, we exclude the m_InValidNetworkPrefab from the connecting
+ // client's side.
+ if (networkPrefab.Prefab.name != m_InValidNetworkPrefab.name)
+ {
+ networkManager.NetworkConfig.NetworkPrefabs.Add(networkPrefab);
+ }
+ }
+ // Disable forcing the same prefabs to avoid failed connections
+ networkManager.NetworkConfig.ForceSamePrefabs = false;
+ networkManager.LogLevel = m_CurrentLogLevel;
+ base.OnNewClientCreated(networkManager);
+ }
+
+ [UnityTest]
+ public IEnumerator NetworkObjectDeserializationFailure()
+ {
+ m_CurrentLogLevel = LogLevel.Nothing;
+ var validSpawnedNetworkObjects = new List();
+ NetworkBehaviourWithNetworkVariables.ResetSpawnCount();
+
+ // Spawn NetworkObjects on the server side with half of them being the
+ // invalid network prefabs to simulate NetworkObject synchronization failure
+ for (int i = 0; i < k_NumberToSpawn; i++)
+ {
+ if (i % 2 == 0)
+ {
+ SpawnObject(m_InValidNetworkPrefab, m_ServerNetworkManager);
+ }
+ else
+ {
+ // Keep track of the prefabs that should successfully spawn on the client side
+ validSpawnedNetworkObjects.Add(SpawnObject(m_NetworkPrefab, m_ServerNetworkManager));
+ }
+ }
+
+ // Assure the server-side spawned all NetworkObjects
+ yield return WaitForConditionOrTimeOut(() => NetworkBehaviourWithNetworkVariables.ServerSpawnCount == k_NumberToSpawn);
+
+ // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
+ yield return CreateAndStartNewClient();
+
+ if (m_UseHost)
+ {
+ var serverSideClientPlayerComponent = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].GetComponent();
+ var serverSideHostPlayerComponent = m_ServerNetworkManager.LocalClient.PlayerObject.GetComponent();
+ var clientSidePlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent();
+ var clientSideHostPlayerComponent = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ServerNetworkManager.LocalClientId].GetComponent();
+
+ // Validate that the client side player values match the server side value of the client's player
+ Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData1.Value == clientSidePlayerComponent.NetworkVariableData1.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})" +
+ $" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData1.Value})!");
+ Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData2.Value == clientSidePlayerComponent.NetworkVariableData2.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})" +
+ $" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData2.Value})!");
+ Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData3.Value == clientSidePlayerComponent.NetworkVariableData3.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})" +
+ $" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData3.Value})!");
+ Assert.IsTrue(serverSideClientPlayerComponent.NetworkVariableData4.Value == clientSidePlayerComponent.NetworkVariableData4.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Client Player] Client side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})" +
+ $" does not equal the server side value ({serverSideClientPlayerComponent.NetworkVariableData4.Value})!");
+
+
+ // Validate that only the 2nd and 4th NetworkVariable on the client side instance of the host's player is the same and the other two do not match
+ // (i.e. NetworkVariables owned by the server should not get synchronized on client)
+ Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData1.Value != clientSideHostPlayerComponent.NetworkVariableData1.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData1.Value})" +
+ $" should not be equal to the server side value ({clientSideHostPlayerComponent.NetworkVariableData1.Value})!");
+ Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData2.Value == clientSideHostPlayerComponent.NetworkVariableData2.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData2.Value})" +
+ $" does not equal the server side value ({clientSideHostPlayerComponent.NetworkVariableData2.Value})!");
+ Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData3.Value != clientSideHostPlayerComponent.NetworkVariableData3.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData3.Value})" +
+ $" should not be equal to the server side value ({clientSideHostPlayerComponent.NetworkVariableData3.Value})!");
+ Assert.IsTrue(serverSideHostPlayerComponent.NetworkVariableData4.Value == clientSideHostPlayerComponent.NetworkVariableData4.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Host Player] Client side value ({serverSideHostPlayerComponent.NetworkVariableData4.Value})" +
+ $" does not equal the server side value ({clientSideHostPlayerComponent.NetworkVariableData4.Value})!");
+ }
+ else
+ {
+ // Spawn and connect another client when running as a server
+ yield return CreateAndStartNewClient();
+ yield return WaitForConditionOrTimeOut(() => m_PlayerNetworkObjects[2].Count > 1);
+ AssertOnTimeout($"Timed out waiting for second client to have access to the first client's cloned player object!");
+
+ var clientSide1PlayerComponent = m_ClientNetworkManagers[0].LocalClient.PlayerObject.GetComponent();
+ var clientSide2Player1Clone = m_PlayerNetworkObjects[2][clientSide1PlayerComponent.OwnerClientId].GetComponent();
+ var clientOneId = clientSide1PlayerComponent.OwnerClientId;
+
+ var clientSide2PlayerComponent = m_ClientNetworkManagers[1].LocalClient.PlayerObject.GetComponent();
+ var clientSide1Player2Clone = m_PlayerNetworkObjects[1][clientSide2PlayerComponent.OwnerClientId].GetComponent();
+ var clientTwoId = clientSide2PlayerComponent.OwnerClientId;
+
+ // Validate that client one's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not
+ Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData1.Value != clientSide2Player1Clone.NetworkVariableData1.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData1.Value})" +
+ $" should not be equal to Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData1.Value})!");
+
+ Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData2.Value == clientSide2Player1Clone.NetworkVariableData2.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData2.Value})" +
+ $" does not equal Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData2.Value})!");
+
+ Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData3.Value != clientSide2Player1Clone.NetworkVariableData3.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData3.Value})" +
+ $" should not be equal to Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData3.Value})!");
+
+ Assert.IsTrue(clientSide1PlayerComponent.NetworkVariableData4.Value == clientSide2Player1Clone.NetworkVariableData4.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientOneId}] Client-{clientOneId} value ({clientSide1PlayerComponent.NetworkVariableData4.Value})" +
+ $" does not equal Client-{clientTwoId}'s clone side value ({clientSide2Player1Clone.NetworkVariableData4.Value})!");
+
+
+ // Validate that client two's 2nd and 4th NetworkVariables for the local and clone instances match and the other two do not
+ Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData1.Value != clientSide1Player2Clone.NetworkVariableData1.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData1)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData1.Value})" +
+ $" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData1.Value})!");
+
+ Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData2.Value == clientSide1Player2Clone.NetworkVariableData2.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData2)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData2.Value})" +
+ $" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData2.Value})!");
+
+ Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData3.Value != clientSide1Player2Clone.NetworkVariableData3.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData3)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData3.Value})" +
+ $" should not be equal to Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData3.Value})!");
+
+ Assert.IsTrue(clientSide2PlayerComponent.NetworkVariableData4.Value == clientSide1Player2Clone.NetworkVariableData4.Value,
+ $"[{nameof(NetworkBehaviourWithOwnerNetworkVariables.NetworkVariableData4)}][Player-{clientTwoId}] Client-{clientTwoId} value ({clientSide2PlayerComponent.NetworkVariableData4.Value})" +
+ $" does not equal Client-{clientOneId}'s clone side value ({clientSide1Player2Clone.NetworkVariableData4.Value})!");
+ }
+
+ // Now validate all of the NetworkVariable values match to assure everything synchronized properly
+ foreach (var spawnedObject in validSpawnedNetworkObjects)
+ {
+ foreach (var clientNetworkManager in m_ClientNetworkManagers)
+ {
+ //Validate that the connected client has spawned all of the instances that shouldn't have failed.
+ var clientSideNetworkObjects = s_GlobalNetworkObjects[clientNetworkManager.LocalClientId];
+
+ Assert.IsTrue(NetworkBehaviourWithNetworkVariables.ClientSpawnCount[clientNetworkManager.LocalClientId] == validSpawnedNetworkObjects.Count, $"Client-{clientNetworkManager.LocalClientId} spawned " +
+ $"({NetworkBehaviourWithNetworkVariables.ClientSpawnCount}) {nameof(NetworkObject)}s but the expected number of {nameof(NetworkObject)}s should have been ({validSpawnedNetworkObjects.Count})!");
+
+ var spawnedNetworkObject = spawnedObject.GetComponent();
+ Assert.IsTrue(clientSideNetworkObjects.ContainsKey(spawnedNetworkObject.NetworkObjectId), $"Failed to find valid spawned {nameof(NetworkObject)} on the client-side with a " +
+ $"{nameof(NetworkObject.NetworkObjectId)} of {spawnedNetworkObject.NetworkObjectId}");
+
+ var clientSideObject = clientSideNetworkObjects[spawnedNetworkObject.NetworkObjectId];
+ Assert.IsTrue(clientSideObject.NetworkManager == clientNetworkManager, $"Client-side object {clientSideObject}'s {nameof(NetworkManager)} is not valid!");
+
+ ValidateNetworkBehaviourWithNetworkVariables(spawnedNetworkObject, clientSideObject);
+ }
+ }
+ }
+
+ private void ValidateNetworkBehaviourWithNetworkVariables(NetworkObject serverSideNetworkObject, NetworkObject clientSideNetworkObject)
+ {
+ var serverSideComponent = serverSideNetworkObject.GetComponent();
+ var clientSideComponent = clientSideNetworkObject.GetComponent();
+
+ string netVarName1 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1);
+ string netVarName2 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1);
+ string netVarName3 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1);
+ string netVarName4 = nameof(NetworkBehaviourWithNetworkVariables.NetworkVariableData1);
+
+ Assert.IsTrue(serverSideComponent.NetworkVariableData1.Count == clientSideComponent.NetworkVariableData1.Count, $"[{serverSideComponent.name}:{netVarName1}] Server side {nameof(NetworkList)} " +
+ $"count ({serverSideComponent.NetworkVariableData1.Count}) does not match the client side {nameof(NetworkList)} count ({clientSideComponent.NetworkVariableData1.Count})!");
+
+ for (int i = 0; i < serverSideComponent.NetworkVariableData1.Count; i++)
+ {
+ Assert.IsTrue(serverSideComponent.NetworkVariableData1[i] == clientSideComponent.NetworkVariableData1[i], $"[{serverSideComponent.name}:{netVarName1}][Index:{i}] Server side instance value " +
+ $"({serverSideComponent.NetworkVariableData1[i]}) does not match the client side instance value ({clientSideComponent.NetworkVariableData1[i]})!");
+ }
+
+ Assert.IsTrue(serverSideComponent.NetworkVariableData2.Value == clientSideComponent.NetworkVariableData2.Value, $"[{serverSideComponent.name}:{netVarName2}] Server side instance value ({serverSideComponent.NetworkVariableData2.Value}) " +
+ $"does not match the client side instance value ({clientSideComponent.NetworkVariableData2.Value})!");
+ Assert.IsTrue(serverSideComponent.NetworkVariableData3.Value == clientSideComponent.NetworkVariableData3.Value, $"[{serverSideComponent.name}:{netVarName3}] Server side instance value ({serverSideComponent.NetworkVariableData3.Value}) " +
+ $"does not match the client side instance value ({clientSideComponent.NetworkVariableData3.Value})!");
+ Assert.IsTrue(serverSideComponent.NetworkVariableData4.Value == clientSideComponent.NetworkVariableData4.Value, $"[{serverSideComponent.name}:{netVarName4}] Server side instance value ({serverSideComponent.NetworkVariableData4.Value}) " +
+ $"does not match the client side instance value ({clientSideComponent.NetworkVariableData4.Value})!");
+ }
+
+ ///
+ /// This validates that when a NetworkBehaviour fails serialization or deserialization during synchronizations that other NetworkBehaviours
+ /// will still be initialized properly
+ ///
+ [UnityTest]
+ public IEnumerator NetworkBehaviourSynchronization()
+ {
+ m_ServerNetworkManager.LogLevel = LogLevel.Normal;
+ m_CurrentLogLevel = LogLevel.Normal;
+ NetworkBehaviourSynchronizeFailureComponent.ResetBehaviour();
+
+ var spawnedObjectList = new List();
+ var numberOfObjectsToSpawn = NetworkBehaviourSynchronizeFailureComponent.NumberOfFailureTypes * 4;
+ // Spawn 11 more NetworkObjects where there should be 4 of each failure type
+ for (int i = 0; i < numberOfObjectsToSpawn; i++)
+ {
+ var synchronizationObject = SpawnObject(m_SynchronizationPrefab, m_ServerNetworkManager);
+ var synchronizationBehaviour = synchronizationObject.GetComponent();
+ synchronizationBehaviour.AssignNextFailureType();
+ spawnedObjectList.Add(synchronizationObject);
+ }
+
+ // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
+ yield return CreateAndStartNewClient();
+
+ // Validate that when a NetworkBehaviour fails to synchronize and is skipped over it does not
+ // impact the rest of the NetworkBehaviours.
+ var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
+ foreach (var spawnedObject in spawnedObjectList)
+ {
+ var serverSideSpawnedNetworkObject = spawnedObject.GetComponent();
+ var clientSideObject = clientSideNetworkObjects[serverSideSpawnedNetworkObject.NetworkObjectId];
+ var clientSideSpawnedNetworkObject = clientSideObject.GetComponent();
+
+ ValidateNetworkBehaviourWithNetworkVariables(serverSideSpawnedNetworkObject, clientSideSpawnedNetworkObject);
+ }
+ }
+ }
+
+ ///
+ /// A test NetworkBeahviour that provides a varying NetworkList size as well as
+ /// additional NetworkVariables to assure if a NetworkObject fails to be created
+ /// the synchronization process will continue (i.e. it will skip over that block
+ /// of the reader buffer).
+ ///
+ public class NetworkBehaviourWithNetworkVariables : NetworkBehaviour
+ {
+ public static int ServerSpawnCount { get; internal set; }
+ public static readonly Dictionary ClientSpawnCount = new Dictionary();
+
+ public static void ResetSpawnCount()
+ {
+ ServerSpawnCount = 0;
+ ClientSpawnCount.Clear();
+ }
+
+ private const uint k_MinDataBlocks = 1;
+ private const uint k_MaxDataBlocks = 64;
+
+ // Add various types of NetworkVariables
+ public NetworkList NetworkVariableData1;
+ public NetworkVariable NetworkVariableData2;
+ public NetworkVariable NetworkVariableData3;
+ public NetworkVariable NetworkVariableData4;
+
+ private void Awake()
+ {
+ var dataBlocksAssigned = new List();
+ var numberDataBlocks = Random.Range(k_MinDataBlocks, k_MaxDataBlocks);
+ for (var i = 0; i < numberDataBlocks; i++)
+ {
+ dataBlocksAssigned.Add((ulong)Random.Range(0.0f, float.MaxValue));
+ }
+
+ NetworkVariableData1 = new NetworkList(dataBlocksAssigned);
+ NetworkVariableData2 = new NetworkVariable(Random.Range(1, 1000));
+ NetworkVariableData3 = new NetworkVariable(Random.Range(1, 1000));
+ NetworkVariableData4 = new NetworkVariable((byte)Random.Range(1, 255));
+
+ }
+
+ public override void OnNetworkSpawn()
+ {
+ if (IsServer)
+ {
+ ServerSpawnCount++;
+ }
+ else
+ {
+ if (!ClientSpawnCount.ContainsKey(NetworkManager.LocalClientId))
+ {
+ ClientSpawnCount.Add(NetworkManager.LocalClientId, 0);
+ }
+ ClientSpawnCount[NetworkManager.LocalClientId]++;
+ }
+
+ base.OnNetworkSpawn();
+ }
+ }
+
+ ///
+ /// A test NetworkBeahviour that has varying permissions in order to validate that
+ /// when variable length safety checks are off NetworkVariables still are updated
+ /// properly.
+ ///
+ public class NetworkBehaviourWithOwnerNetworkVariables : NetworkBehaviour
+ {
+
+ // Should not synchronize on non-owners
+ public NetworkVariable NetworkVariableData1 = new NetworkVariable(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
+ // Should synchronize with everyone
+ public NetworkVariable NetworkVariableData2 = new NetworkVariable();
+ // Should not synchronize on non-owners
+ public NetworkVariable NetworkVariableData3 = new NetworkVariable(default, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Server);
+ // Should synchronize with everyone
+ public NetworkVariable NetworkVariableData4 = new NetworkVariable();
+
+ public override void OnNetworkSpawn()
+ {
+ if (IsServer)
+ {
+ NetworkVariableData1.Value = Random.Range(1, 1000);
+ NetworkVariableData2.Value = Random.Range(1, 1000);
+ NetworkVariableData3.Value = (byte)Random.Range(1, 255);
+ NetworkVariableData4.Value = (ushort)Random.Range(1, ushort.MaxValue);
+ }
+ }
+ }
+
+ ///
+ /// A test NetworkBeahviour that simulates various types of synchronization failures
+ /// and provides a synchronization success version to validate that synchronization
+ /// will continue if user synchronization code fails.
+ ///
+ public class NetworkBehaviourSynchronizeFailureComponent : NetworkBehaviour
+ {
+ public static int NumberOfFailureTypes { get; internal set; }
+ public static int ServerSpawnCount { get; internal set; }
+ public static int ClientSpawnCount { get; internal set; }
+
+ private static FailureTypes s_FailureType = FailureTypes.None;
+
+ public enum FailureTypes
+ {
+ None,
+ DuringWriting,
+ DuringReading,
+ DontReadAnything,
+ ThrowWriteSideException,
+ ThrowReadSideException
+ }
+
+ public static void ResetBehaviour()
+ {
+ ServerSpawnCount = 0;
+ ClientSpawnCount = 0;
+ s_FailureType = FailureTypes.None;
+ NumberOfFailureTypes = System.Enum.GetValues(typeof(FailureTypes)).Length;
+ }
+
+ private MyCustomData m_MyCustomData;
+
+ private struct MyCustomData : INetworkSerializable
+ {
+ public FailureTypes FailureType;
+ private ushort m_DataSize;
+ private byte[] m_DataBlock;
+
+ public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter
+ {
+ if (serializer.IsWriter)
+ {
+ var writer = serializer.GetFastBufferWriter();
+ switch (FailureType)
+ {
+ case FailureTypes.None:
+ // We want to write something for these two cases
+ case FailureTypes.DuringReading:
+ case FailureTypes.DontReadAnything:
+ {
+ writer.WriteValueSafe(m_DataSize);
+ for (int i = 0; i < m_DataSize; i++)
+ {
+ writer.WriteValueSafe(m_DataBlock[i]);
+ }
+ break;
+ }
+ case FailureTypes.DuringWriting:
+ {
+ writer.WriteValueSafe(m_DataSize);
+ // Try to write past the allocated size to generate an exception
+ // while also filling the buffer to verify that the buffer will be
+ // reset back to the original position.
+ for (int i = 0; i <= m_DataSize; i++)
+ {
+ writer.WriteValueSafe(m_DataBlock[i]);
+ }
+ break;
+ }
+ case FailureTypes.ThrowWriteSideException:
+ {
+ throw new System.Exception("Write side exception!");
+ }
+ }
+ }
+ else
+ {
+ var reader = serializer.GetFastBufferReader();
+ switch (FailureType)
+ {
+ case FailureTypes.None:
+ {
+ reader.ReadValueSafe(out m_DataSize);
+ m_DataBlock = new byte[m_DataSize];
+ for (int i = 0; i < m_DataSize; i++)
+ {
+ reader.ReadValueSafe(out m_DataBlock[i]);
+ }
+ break;
+ }
+ case FailureTypes.DuringReading:
+ {
+ reader.ReadValueSafe(out m_DataSize);
+ // Allocate more space than needed
+ m_DataBlock = new byte[(int)(m_DataSize * 1.5f)];
+ // Now read past the size of this message to verify
+ // that the reader will get rest back to the appropriate
+ // position and an error will be generated for this
+ for (int i = 0; i < m_DataBlock.Length; i++)
+ {
+ reader.ReadValueSafe(out m_DataBlock[i]);
+ }
+ break;
+ }
+ case FailureTypes.DontReadAnything:
+ {
+ // Don't read anything
+ break;
+ }
+ case FailureTypes.ThrowReadSideException:
+ {
+ throw new System.Exception("Read side exception!");
+ }
+
+ }
+ }
+ }
+
+ public void GenerateData(ushort size)
+ {
+ m_DataSize = size;
+ m_DataBlock = new byte[size];
+ for (int i = 0; i < m_DataSize; i++)
+ {
+ m_DataBlock[i] = (byte)Random.Range(0, 512);
+ }
+ }
+ }
+
+ // This NetworkVariable is synchronized before OnSynchronize is invoked
+ // which enables us to perform the tests.
+ // Users could follow the same pattern for game assets and synchronize
+ // clients based on NetworkVariable settings. (i.e. a specific NPC type or the like)
+ private NetworkVariable m_FailureType;
+
+ public void AssignNextFailureType()
+ {
+ var currentPosition = (int)s_FailureType;
+ currentPosition = (++currentPosition) % NumberOfFailureTypes;
+ s_FailureType = (FailureTypes)currentPosition;
+ m_FailureType.Value = s_FailureType;
+ }
+
+
+ private void Awake()
+ {
+ m_FailureType = new NetworkVariable();
+ m_MyCustomData = new MyCustomData();
+ }
+
+ public override void OnNetworkSpawn()
+ {
+ if (IsServer)
+ {
+ ServerSpawnCount++;
+ m_MyCustomData.GenerateData((ushort)Random.Range(1, 512));
+ }
+ else
+ {
+ ClientSpawnCount++;
+ }
+
+ base.OnNetworkSpawn();
+ }
+
+ protected override void OnSynchronize(ref BufferSerializer serializer)
+ {
+ // Assign the failure type first
+ m_MyCustomData.FailureType = m_FailureType.Value;
+ // Now handle the serialization for this failure type
+ m_MyCustomData.NetworkSerialize(serializer);
+ }
+ }
+}
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs.meta
similarity index 100%
rename from com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSceneSerializationTests.cs.meta
rename to com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs.meta
From 777a8f517b2da207284731744bc4a0388e35d6c1 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Wed, 9 Nov 2022 15:55:32 -0600
Subject: [PATCH 18/26] test
Integration test to validate nested network transforms synchronize properly with the nested GameObjects' transforms.
---
.../NestedNetworkTransforms/ChildMover.cs | 6 +
.../ChildMoverManager.cs | 19 +-
.../ObjectParenting/AutomatedPlayerMover.cs | 93 ++
.../AutomatedPlayerMover.cs.meta | 11 +
.../ObjectParenting/Navigationpoints.cs | 20 +
.../ObjectParenting/Navigationpoints.cs.meta | 11 +
.../NestedNetworkTransformTestScene.unity | 1077 +++++++++++++++++
...NestedNetworkTransformTestScene.unity.meta | 7 +
.../NestedNetworkTransformTests.cs | 178 +++
.../NestedNetworkTransformTests.cs.meta | 11 +
.../Runtime/ObjectParenting/Resources.meta | 8 +
.../Resources/PlayerNestedTransforms.prefab | 515 ++++++++
.../PlayerNestedTransforms.prefab.meta | 7 +
.../ProjectSettings/EditorBuildSettings.asset | 3 +
14 files changed, 1964 insertions(+), 2 deletions(-)
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs.meta
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs.meta
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity.meta
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs.meta
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/Resources.meta
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab
create mode 100644 testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab.meta
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
index 6a0ead6aac..38ad1881ee 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMover.cs
@@ -5,6 +5,8 @@ namespace TestProject.ManualTests
{
public class ChildMover : NetworkBehaviour
{
+ public static bool RandomizeScale;
+
[Range(0.1f, 30.0f)]
public float RotationSpeed = 5.0f;
@@ -19,6 +21,10 @@ public void PlayerIsMoving(float movementDirection)
public override void OnNetworkSpawn()
{
+ if (IsOwner && RandomizeScale)
+ {
+ transform.localScale = transform.localScale * Random.Range(0.5f, 2.0f);
+ }
base.OnNetworkSpawn();
}
}
diff --git a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
index c763918a94..6552943606 100644
--- a/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
+++ b/testproject/Assets/Tests/Manual/NestedNetworkTransforms/ChildMoverManager.cs
@@ -43,6 +43,8 @@ public override void OnNetworkSpawn()
base.OnNetworkSpawn();
}
+ private float m_LastRotDirection = 1.0f;
+ private float m_LastMovementDirection = 1.0f;
private void Update()
{
if (IsOwner && IsSpawned)
@@ -52,16 +54,29 @@ private void Update()
{
// Get our movement direction
var movementDirection = Vector3.Dot(deltaPosition.normalized, transform.forward);
+ if (movementDirection == 0)
+ {
+ movementDirection = m_LastMovementDirection;
+ }
+ else
+ {
+ m_LastMovementDirection = movementDirection;
+ }
var rotationDirection = Vector3.zero;
- if (movementDirection >= 0)
+ if (movementDirection > 0)
{
rotationDirection = Vector3.Cross(m_LastForward, transform.forward);
}
- else
+ else if (movementDirection < 0)
{
rotationDirection = Vector3.Cross(transform.forward, m_LastForward);
}
+ else
+ {
+ rotationDirection.y = m_LastRotDirection;
+ }
+ m_LastRotDirection = rotationDirection.y;
movementDirection *= rotationDirection.y < 0 ? -1 : 1;
m_LastPosition = transform.position;
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs
new file mode 100644
index 0000000000..bd97836562
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs
@@ -0,0 +1,93 @@
+using UnityEngine;
+using Unity.Netcode.Components;
+
+namespace TestProject.RuntimeTests
+{
+ public class AutomatedPlayerMover : NetworkTransform
+ {
+ public static bool StopMovement;
+ private float m_Speed = 15.0f;
+ private float m_RotSpeed = 15.0f;
+
+ private GameObject m_Destination;
+ private Vector3 m_TargetPosition;
+
+
+
+ ///
+ /// Make this PlayerMovement-NetworkTransform component
+ /// Owner Authoritative
+ ///
+ protected override bool OnIsServerAuthoritative()
+ {
+ return false;
+ }
+
+ private void UpdateDestination()
+ {
+ if (Navigationpoints.Instance != null)
+ {
+ var targetNavPointIndex = Random.Range(0, Navigationpoints.Instance.NavPoints.Count - 1);
+ m_Destination = Navigationpoints.Instance.NavPoints[targetNavPointIndex];
+
+ m_TargetPosition = m_Destination.transform.position;
+ m_TargetPosition.y = transform.position.y;
+ }
+ }
+
+ public override void OnNetworkSpawn()
+ {
+ if (IsOwner)
+ {
+ UpdateDestination();
+ var temp = transform.position;
+ temp.y = 0.5f;
+ transform.position = temp;
+ }
+ base.OnNetworkSpawn();
+ }
+
+ public override void OnNetworkDespawn()
+ {
+ base.OnNetworkDespawn();
+ }
+
+ private void LateUpdate()
+ {
+ if (!IsSpawned || !IsOwner || m_Destination == null || StopMovement)
+ {
+ return;
+ }
+ m_TargetPosition.y = transform.position.y;
+ var distance = Vector3.Distance(transform.position, m_TargetPosition);
+ if (distance < 0.25f)
+ {
+ var currentDestination = m_Destination;
+ while (m_Destination == currentDestination)
+ {
+ UpdateDestination();
+ }
+ }
+ }
+
+ private void FixedUpdate()
+ {
+ if (!IsSpawned || !IsOwner || m_Destination == null || StopMovement)
+ {
+ return;
+ }
+ else
+ {
+ transform.position = Vector3.MoveTowards(transform.position, m_Destination.transform.position, m_Speed * Time.fixedDeltaTime);
+ var normalizedDirection = (m_TargetPosition - transform.position).normalized;
+ if (normalizedDirection.magnitude != 0.0f)
+ {
+ var lookRotation = Quaternion.LookRotation(normalizedDirection, transform.up).eulerAngles;
+ var currentEuler = transform.eulerAngles;
+ currentEuler.y = Mathf.LerpAngle(currentEuler.y, lookRotation.y, Time.deltaTime * m_RotSpeed);
+ transform.eulerAngles = currentEuler;
+ }
+ }
+ }
+ }
+}
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs.meta
new file mode 100644
index 0000000000..3ccd51fd66
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/AutomatedPlayerMover.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dfb1af1a9249278438d2daa2877ee2ad
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs
new file mode 100644
index 0000000000..e2a6aecfcf
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace TestProject.RuntimeTests
+{
+ public class Navigationpoints : MonoBehaviour
+ {
+ public static Navigationpoints Instance;
+
+ public List NavPoints;
+
+ private void Awake()
+ {
+ if (Instance == null)
+ {
+ Instance = this;
+ }
+ }
+ }
+}
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs.meta
new file mode 100644
index 0000000000..82f15cd59e
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Navigationpoints.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fb5950beeb672f54fa8b2666aaefa223
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity
new file mode 100644
index 0000000000..e7971f34dd
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity
@@ -0,0 +1,1077 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 9
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+ m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+ m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 0
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 0}
+ m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1}
+ m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 12
+ m_GIWorkflowMode: 1
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 1
+ m_EnableRealtimeLightmaps: 0
+ m_LightmapEditorSettings:
+ serializedVersion: 12
+ m_Resolution: 2
+ m_BakeResolution: 40
+ m_AtlasSize: 1024
+ m_AO: 0
+ m_AOMaxDistance: 1
+ m_CompAOExponent: 1
+ m_CompAOExponentDirect: 0
+ m_ExtractAmbientOcclusion: 0
+ m_Padding: 2
+ m_LightmapParameters: {fileID: 0}
+ m_LightmapsBakeMode: 1
+ m_TextureCompression: 1
+ m_FinalGather: 0
+ m_FinalGatherFiltering: 1
+ m_FinalGatherRayCount: 256
+ m_ReflectionCompression: 2
+ m_MixedBakeMode: 2
+ m_BakeBackend: 1
+ m_PVRSampling: 1
+ m_PVRDirectSampleCount: 32
+ m_PVRSampleCount: 512
+ m_PVRBounces: 2
+ m_PVREnvironmentSampleCount: 256
+ m_PVREnvironmentReferencePointCount: 2048
+ m_PVRFilteringMode: 1
+ m_PVRDenoiserTypeDirect: 1
+ m_PVRDenoiserTypeIndirect: 1
+ m_PVRDenoiserTypeAO: 1
+ m_PVRFilterTypeDirect: 0
+ m_PVRFilterTypeIndirect: 0
+ m_PVRFilterTypeAO: 0
+ m_PVREnvironmentMIS: 1
+ m_PVRCulling: 1
+ m_PVRFilteringGaussRadiusDirect: 1
+ m_PVRFilteringGaussRadiusIndirect: 5
+ m_PVRFilteringGaussRadiusAO: 2
+ m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+ m_PVRFilteringAtrousPositionSigmaIndirect: 2
+ m_PVRFilteringAtrousPositionSigmaAO: 1
+ m_ExportTrainingData: 0
+ m_TrainingDataDestination: TrainingData
+ m_LightProbeSampleCountMultiplier: 4
+ m_LightingDataAsset: {fileID: 0}
+ m_LightingSettings: {fileID: 0}
+--- !u!196 &4
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 2
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666667
+ manualTileSize: 0
+ tileSize: 256
+ accuratePlacement: 0
+ maxJobWorkers: 0
+ preserveTilesOutsideBounds: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1 &75767844
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 75767845}
+ m_Layer: 0
+ m_Name: NavigationPoint1
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &75767845
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 75767844}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0.5, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 582924497}
+ m_Father: {fileID: 0}
+ m_RootOrder: 3
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &147173471
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 147173472}
+ - component: {fileID: 147173476}
+ - component: {fileID: 147173475}
+ m_Layer: 9
+ m_Name: ObjectLabel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &147173472
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 147173471}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 165918550}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!102 &147173475
+TextMesh:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 147173471}
+ m_Text: 3
+ m_OffsetZ: 0
+ m_CharacterSize: 1
+ m_LineSpacing: 1
+ m_Anchor: 7
+ m_Alignment: 0
+ m_TabSize: 4
+ m_FontSize: 32
+ m_FontStyle: 0
+ m_RichText: 1
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_Color:
+ serializedVersion: 2
+ rgba: 4294967295
+--- !u!23 &147173476
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 147173471}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &165918549
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 165918550}
+ m_Layer: 0
+ m_Name: NavigationPoint3
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &165918550
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 165918549}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 50, y: 0.5, z: 50}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 147173472}
+ m_Father: {fileID: 0}
+ m_RootOrder: 5
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &170238949
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 170238951}
+ - component: {fileID: 170238950}
+ m_Layer: 0
+ m_Name: Directional Light
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!108 &170238950
+Light:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 170238949}
+ m_Enabled: 1
+ serializedVersion: 10
+ m_Type: 1
+ m_Shape: 0
+ m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
+ m_Intensity: 1
+ m_Range: 10
+ m_SpotAngle: 30
+ m_InnerSpotAngle: 21.80208
+ m_CookieSize: 10
+ m_Shadows:
+ m_Type: 2
+ m_Resolution: -1
+ m_CustomResolution: -1
+ m_Strength: 1
+ m_Bias: 0.05
+ m_NormalBias: 0.4
+ m_NearPlane: 0.2
+ m_CullingMatrixOverride:
+ e00: 1
+ e01: 0
+ e02: 0
+ e03: 0
+ e10: 0
+ e11: 1
+ e12: 0
+ e13: 0
+ e20: 0
+ e21: 0
+ e22: 1
+ e23: 0
+ e30: 0
+ e31: 0
+ e32: 0
+ e33: 1
+ m_UseCullingMatrixOverride: 0
+ m_Cookie: {fileID: 0}
+ m_DrawHalo: 0
+ m_Flare: {fileID: 0}
+ m_RenderMode: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingLayerMask: 1
+ m_Lightmapping: 4
+ m_LightShadowCasterMode: 0
+ m_AreaSize: {x: 1, y: 1}
+ m_BounceIntensity: 1
+ m_ColorTemperature: 6570
+ m_UseColorTemperature: 0
+ m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+ m_UseBoundingSphereOverride: 0
+ m_UseViewFrustumForShadowCasterCull: 1
+ m_ShadowRadius: 0
+ m_ShadowAngle: 0
+--- !u!4 &170238951
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 170238949}
+ m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
+ m_LocalPosition: {x: 0, y: 3, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
+--- !u!1 &582924496
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 582924497}
+ - component: {fileID: 582924501}
+ - component: {fileID: 582924500}
+ m_Layer: 9
+ m_Name: ObjectLabel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &582924497
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 582924496}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 75767845}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!102 &582924500
+TextMesh:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 582924496}
+ m_Text: 1
+ m_OffsetZ: 0
+ m_CharacterSize: 1
+ m_LineSpacing: 1
+ m_Anchor: 7
+ m_Alignment: 0
+ m_TabSize: 4
+ m_FontSize: 32
+ m_FontStyle: 0
+ m_RichText: 1
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_Color:
+ serializedVersion: 2
+ rgba: 4294967295
+--- !u!23 &582924501
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 582924496}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &1218177454
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1218177455}
+ - component: {fileID: 1218177459}
+ - component: {fileID: 1218177458}
+ m_Layer: 9
+ m_Name: ObjectLabel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1218177455
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1218177454}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 1476695697}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!102 &1218177458
+TextMesh:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1218177454}
+ m_Text: "5\t"
+ m_OffsetZ: 0
+ m_CharacterSize: 1
+ m_LineSpacing: 1
+ m_Anchor: 7
+ m_Alignment: 0
+ m_TabSize: 4
+ m_FontSize: 32
+ m_FontStyle: 0
+ m_RichText: 1
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_Color:
+ serializedVersion: 2
+ rgba: 4294967295
+--- !u!23 &1218177459
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1218177454}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &1222925673
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1222925674}
+ - component: {fileID: 1222925678}
+ - component: {fileID: 1222925677}
+ m_Layer: 9
+ m_Name: ObjectLabel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1222925674
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1222925673}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 2026412476}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!102 &1222925677
+TextMesh:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1222925673}
+ m_Text: "2\t"
+ m_OffsetZ: 0
+ m_CharacterSize: 1
+ m_LineSpacing: 1
+ m_Anchor: 7
+ m_Alignment: 0
+ m_TabSize: 4
+ m_FontSize: 32
+ m_FontStyle: 0
+ m_RichText: 1
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_Color:
+ serializedVersion: 2
+ rgba: 4294967295
+--- !u!23 &1222925678
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1222925673}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &1323194500
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1323194502}
+ - component: {fileID: 1323194501}
+ m_Layer: 0
+ m_Name: NavigationPoints
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1323194501
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1323194500}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fb5950beeb672f54fa8b2666aaefa223, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ NavPoints:
+ - {fileID: 75767844}
+ - {fileID: 2026412475}
+ - {fileID: 165918549}
+ - {fileID: 2050720992}
+ - {fileID: 1476695696}
+--- !u!4 &1323194502
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1323194500}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 53.61696, z: -48.528473}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 8
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &1337449911
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_RootOrder
+ value: 2
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalScale.x
+ value: 2
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalScale.z
+ value: 2
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0.000000059604645
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511849, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4012615692778511854, guid: fe5fd652408224242a6fea8a6f8e6d05,
+ type: 3}
+ propertyPath: m_Name
+ value: SceneLevelGeometry
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: fe5fd652408224242a6fea8a6f8e6d05, type: 3}
+--- !u!1 &1476695696
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1476695697}
+ m_Layer: 0
+ m_Name: NavigationPoint5
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1476695697
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1476695696}
+ m_LocalRotation: {x: 0.2164396, y: 0, z: 0, w: 0.97629607}
+ m_LocalPosition: {x: 50, y: 0.5, z: -50}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1218177455}
+ m_Father: {fileID: 0}
+ m_RootOrder: 7
+ m_LocalEulerAnglesHint: {x: 25, y: 0, z: 0}
+--- !u!1 &1561339197
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1561339198}
+ - component: {fileID: 1561339202}
+ - component: {fileID: 1561339201}
+ m_Layer: 9
+ m_Name: ObjectLabel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1561339198
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1561339197}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 2050720993}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!102 &1561339201
+TextMesh:
+ serializedVersion: 3
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1561339197}
+ m_Text: "4\t"
+ m_OffsetZ: 0
+ m_CharacterSize: 1
+ m_LineSpacing: 1
+ m_Anchor: 7
+ m_Alignment: 0
+ m_TabSize: 4
+ m_FontSize: 32
+ m_FontStyle: 0
+ m_RichText: 1
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_Color:
+ serializedVersion: 2
+ rgba: 4294967295
+--- !u!23 &1561339202
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1561339197}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 10100, guid: 0000000000000000e000000000000000, type: 0}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!1 &1922374989
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1922374992}
+ - component: {fileID: 1922374991}
+ - component: {fileID: 1922374990}
+ m_Layer: 0
+ m_Name: Main Camera
+ m_TagString: MainCamera
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!81 &1922374990
+AudioListener:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1922374989}
+ m_Enabled: 1
+--- !u!20 &1922374991
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1922374989}
+ m_Enabled: 1
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!4 &1922374992
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1922374989}
+ m_LocalRotation: {x: 0.46174863, y: 0, z: 0, w: 0.8870109}
+ m_LocalPosition: {x: 0, y: 70, z: -60}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 55, y: 0, z: 0}
+--- !u!1 &2026412475
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2026412476}
+ m_Layer: 0
+ m_Name: NavigationPoint2
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &2026412476
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2026412475}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: -50, y: 0.5, z: 50}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1222925674}
+ m_Father: {fileID: 0}
+ m_RootOrder: 4
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &2050720992
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2050720993}
+ m_Layer: 0
+ m_Name: NavigationPoint4
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &2050720993
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2050720992}
+ m_LocalRotation: {x: 0.2164396, y: 0, z: 0, w: 0.97629607}
+ m_LocalPosition: {x: -50, y: 0.5, z: -50}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 1561339198}
+ m_Father: {fileID: 0}
+ m_RootOrder: 6
+ m_LocalEulerAnglesHint: {x: 25, y: 0, z: 0}
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity.meta
new file mode 100644
index 0000000000..3623bc6e70
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 14235ed18eff0964cbb3ff7ae2ed1933
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
new file mode 100644
index 0000000000..af49929f29
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
@@ -0,0 +1,178 @@
+using System.Text;
+using System.Collections;
+using Unity.Netcode.Components;
+using NUnit.Framework;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+using UnityEngine.TestTools;
+using Unity.Netcode;
+using TestProject.ManualTests;
+
+namespace TestProject.RuntimeTests
+{
+ public class NestedNetworkTransformTests : IntegrationTestWithApproximation
+ {
+ private const string k_TestScene = "NestedNetworkTransformTestScene";
+ private const string k_PlayerToLoad = "PlayerNestedTransforms";
+
+ protected override int NumberOfClients => 0;
+
+ private Scene m_BaseSceneLoaded;
+ private Scene m_OriginalActiveScene;
+
+ private Object m_PlayerPrefabResource;
+
+ protected override void OnOneTimeSetup()
+ {
+ ChildMover.RandomizeScale = true;
+ m_OriginalActiveScene = SceneManager.GetActiveScene();
+ m_PlayerPrefabResource = Resources.Load(k_PlayerToLoad);
+ Assert.NotNull(m_PlayerPrefabResource, $"Failed to load resource {k_PlayerToLoad}");
+ // Migrate the resource into the DDOL
+ Object.DontDestroyOnLoad(m_PlayerPrefabResource);
+ SceneManager.sceneLoaded += SceneManager_sceneLoaded;
+ SceneManager.LoadScene(k_TestScene, LoadSceneMode.Additive);
+ base.OnOneTimeSetup();
+ }
+
+ protected override void OnOneTimeTearDown()
+ {
+ ChildMover.RandomizeScale = false;
+ if (m_OriginalActiveScene.IsValid() && m_OriginalActiveScene.isLoaded)
+ {
+ SceneManager.SetActiveScene(m_OriginalActiveScene);
+ }
+ if (m_BaseSceneLoaded.IsValid() && m_BaseSceneLoaded.isLoaded)
+ {
+ SceneManager.UnloadSceneAsync(m_BaseSceneLoaded);
+ }
+ base.OnOneTimeTearDown();
+ }
+
+ protected override IEnumerator OnSetup()
+ {
+ yield return WaitForConditionOrTimeOut(() => m_BaseSceneLoaded.IsValid() && m_BaseSceneLoaded.isLoaded);
+ AssertOnTimeout($"Timed out waiting for scene {k_TestScene} to load!");
+ yield return base.OnSetup();
+ }
+
+ private void SceneManager_sceneLoaded(Scene sceneLoaded, LoadSceneMode loadSceneMode)
+ {
+ if (loadSceneMode == LoadSceneMode.Additive && sceneLoaded.name == k_TestScene)
+ {
+ m_BaseSceneLoaded = sceneLoaded;
+ SceneManager.sceneLoaded -= SceneManager_sceneLoaded;
+ SceneManager.SetActiveScene(sceneLoaded);
+ }
+ }
+
+ protected override IEnumerator OnTearDown()
+ {
+ m_PlayerPrefab = null;
+ return base.OnTearDown();
+ }
+
+
+ protected override void OnCreatePlayerPrefab()
+ {
+ Object.DestroyImmediate(m_PlayerPrefab);
+ m_PlayerPrefab = m_PlayerPrefabResource as GameObject;
+ base.OnCreatePlayerPrefab();
+ }
+
+ protected override void OnNewClientStarted(NetworkManager networkManager)
+ {
+ base.OnNewClientStarted(networkManager);
+ }
+
+ private bool VerifySceneServer(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
+ {
+ if (sceneName == k_TestScene)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Increase the threshold as we are just testing that the
+ /// NetworkTransform is synchronizing properly and there are
+ /// known issues with Euler rotation that will be fixed when
+ /// we start synchronizing Quaternions.
+ ///
+ protected override float GetDeltaVarianceThreshold()
+ {
+ return 0.1f;
+ }
+
+ private StringBuilder m_ValidationErrors;
+ ///
+ /// Validates that all player transforms are approximately the
+ /// same when a new client joins.
+ ///
+ ///
+ private bool ValidateNetworkTransforms()
+ {
+ m_ValidationErrors.Clear();
+ foreach (var connectedClient in m_ServerNetworkManager.ConnectedClientsIds)
+ {
+ var playerToValidate = m_PlayerNetworkObjects[connectedClient][connectedClient];
+ var playerNetworkTransforms = playerToValidate.GetComponentsInChildren();
+ foreach (var playerRelative in m_PlayerNetworkObjects)
+ {
+ if (playerRelative.Key == connectedClient)
+ {
+ continue;
+ }
+ var relativeClonedTransforms = playerRelative.Value[connectedClient].GetComponentsInChildren();
+ for (int i = 0; i < playerNetworkTransforms.Length; i++)
+ {
+ if (!Approximately(playerNetworkTransforms[i].transform.position, relativeClonedTransforms[i].transform.position))
+ {
+ m_ValidationErrors.Append($"[Position][Client-{connectedClient} {playerNetworkTransforms[i].transform.position}][Failing on Client-{playerRelative.Key} for Clone-{relativeClonedTransforms[i].OwnerClientId} {relativeClonedTransforms[i].transform.position}]");
+ }
+ if (!Approximately(playerNetworkTransforms[i].transform.eulerAngles, relativeClonedTransforms[i].transform.eulerAngles))
+ {
+ m_ValidationErrors.Append($"[Rotation][Client-{connectedClient} {playerNetworkTransforms[i].transform.eulerAngles}][Failing on Client-{playerRelative.Key} for Clone-{relativeClonedTransforms[i].OwnerClientId} {relativeClonedTransforms[i].transform.eulerAngles}]");
+ }
+ if (!Approximately(playerNetworkTransforms[i].transform.localScale, relativeClonedTransforms[i].transform.localScale))
+ {
+ m_ValidationErrors.Append($"[Scale][Client-{connectedClient} {playerNetworkTransforms[i].transform.localScale}][Failing on Client-{playerRelative.Key} for Clone-{relativeClonedTransforms[i].OwnerClientId} {relativeClonedTransforms[i].transform.localScale}]");
+ }
+ }
+ }
+ }
+ return m_ValidationErrors.Length == 0;
+ }
+
+ [UnityTest]
+ public IEnumerator NestedNetworkTransformSynchronization()
+ {
+ m_ValidationErrors = new StringBuilder();
+ var waitPeriod = new WaitForSeconds(1.0f);
+ m_ServerNetworkManager.SceneManager.VerifySceneBeforeLoading = VerifySceneServer;
+ yield return waitPeriod;
+
+ // Spawn 5 more clients over time
+ for (int i = 0; i < 5; i++)
+ {
+ yield return CreateAndStartNewClient();
+ // Stop all movement for all players
+ AutomatedPlayerMover.StopMovement = true;
+ // Validate that the transforms are approximately the same
+ yield return WaitForConditionOrTimeOut(ValidateNetworkTransforms);
+ AssertOnTimeout($"Timed out waiting for all nested NetworkTransform cloned instances to match:\n {m_ValidationErrors}");
+ // Continue player movement
+ AutomatedPlayerMover.StopMovement = false;
+ // Allow the players to move a bit.
+ yield return waitPeriod;
+ }
+
+ // Just a final sanity check to make sure position and rotation match
+ AutomatedPlayerMover.StopMovement = true;
+ yield return WaitForConditionOrTimeOut(ValidateNetworkTransforms);
+ }
+
+ }
+}
+
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs.meta
new file mode 100644
index 0000000000..cebb30746c
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1623c8082eac2494badd6912c5ca2f7f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Resources.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources.meta
new file mode 100644
index 0000000000..dbf62d950b
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6d5e70d978cb18b4cbca95b8aa1aff40
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab
new file mode 100644
index 0000000000..9eb666bcdf
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab
@@ -0,0 +1,515 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &772585991204072682
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1522619104359096714}
+ - component: {fileID: 6327137497379236391}
+ - component: {fileID: 2204728973112518521}
+ - component: {fileID: 1866518356433686547}
+ - component: {fileID: 2645854474244200924}
+ m_Layer: 0
+ m_Name: ChildOne
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1522619104359096714
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 2, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 296612175404815447}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!23 &6327137497379236391
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 62ff4693d71b55440a645bbccd83ac8a, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &2204728973112518521
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!114 &1866518356433686547
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0d8ad30fca3f9a240bdce16f0166033b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ RotationSpeed: 5
+--- !u!114 &2645854474244200924
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 772585991204072682}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: cf01cca54b77c0241ad6d9da5ef6a709, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ SyncPositionX: 1
+ SyncPositionY: 1
+ SyncPositionZ: 1
+ SyncRotAngleX: 1
+ SyncRotAngleY: 1
+ SyncRotAngleZ: 1
+ SyncScaleX: 1
+ SyncScaleY: 1
+ SyncScaleZ: 1
+ PositionThreshold: 0.001
+ RotAngleThreshold: 0.01
+ ScaleThreshold: 0.01
+ InLocalSpace: 0
+ Interpolate: 1
+--- !u!1 &2771624607751045562
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5485086383386216104}
+ m_Layer: 0
+ m_Name: FirstLevel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &5485086383386216104
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2771624607751045562}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 4974009855568796650}
+ m_Father: {fileID: 296612175404815447}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &4147667212972069939
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7199215624742357828}
+ - component: {fileID: 8372809022110481992}
+ m_Layer: 0
+ m_Name: PlayerView
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &7199215624742357828
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4147667212972069939}
+ m_LocalRotation: {x: 0.17364816, y: -0, z: -0, w: 0.9848078}
+ m_LocalPosition: {x: 0, y: 8, z: -10}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 296612175404815447}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 20, y: 0, z: 0}
+--- !u!20 &8372809022110481992
+Camera:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4147667212972069939}
+ m_Enabled: 0
+ serializedVersion: 2
+ m_ClearFlags: 1
+ m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+ m_projectionMatrixMode: 1
+ m_GateFitMode: 2
+ m_FOVAxisMode: 0
+ m_SensorSize: {x: 36, y: 24}
+ m_LensShift: {x: 0, y: 0}
+ m_FocalLength: 50
+ m_NormalizedViewPortRect:
+ serializedVersion: 2
+ x: 0
+ y: 0
+ width: 1
+ height: 1
+ near clip plane: 0.3
+ far clip plane: 1000
+ field of view: 60
+ orthographic: 0
+ orthographic size: 5
+ m_Depth: -1
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingPath: -1
+ m_TargetTexture: {fileID: 0}
+ m_TargetDisplay: 0
+ m_TargetEye: 3
+ m_HDR: 1
+ m_AllowMSAA: 1
+ m_AllowDynamicResolution: 0
+ m_ForceIntoRT: 0
+ m_OcclusionCulling: 1
+ m_StereoConvergence: 10
+ m_StereoSeparation: 0.022
+--- !u!1 &6292214655028195304
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4974009855568796650}
+ - component: {fileID: 3062926429781172158}
+ - component: {fileID: 1595868777393624541}
+ - component: {fileID: 2914594030718603294}
+ - component: {fileID: 2888046383252644210}
+ m_Layer: 0
+ m_Name: ChildTwo
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &4974009855568796650
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: -2, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 5485086383386216104}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!23 &3062926429781172158
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 62ff4693d71b55440a645bbccd83ac8a, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &1595868777393624541
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!114 &2914594030718603294
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0d8ad30fca3f9a240bdce16f0166033b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ RotationSpeed: 5
+--- !u!114 &2888046383252644210
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6292214655028195304}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: cf01cca54b77c0241ad6d9da5ef6a709, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ SyncPositionX: 1
+ SyncPositionY: 1
+ SyncPositionZ: 1
+ SyncRotAngleX: 1
+ SyncRotAngleY: 1
+ SyncRotAngleZ: 1
+ SyncScaleX: 1
+ SyncScaleY: 1
+ SyncScaleZ: 1
+ PositionThreshold: 0.001
+ RotAngleThreshold: 0.01
+ ScaleThreshold: 0.01
+ InLocalSpace: 0
+ Interpolate: 1
+--- !u!1001 &8977898853425847701
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 947981134, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+ propertyPath: GlobalObjectIdHash
+ value: 951099334
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_RootOrder
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalScale.x
+ value: 2
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalScale.y
+ value: 2
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalScale.z
+ value: 2
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767877, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_Interpolate
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767877, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_IsKinematic
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ propertyPath: m_Name
+ value: PlayerNestedTransforms
+ objectReference: {fileID: 0}
+ m_RemovedComponents:
+ - {fileID: 8685790303553767876, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+ - {fileID: 7138389085065872747, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+ - {fileID: 2744080254494315543, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+ - {fileID: 8685790303553767873, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+ - {fileID: 8685790303553767877, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+ m_SourcePrefab: {fileID: 100100000, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, type: 3}
+--- !u!4 &296612175404815447 stripped
+Transform:
+ m_CorrespondingSourceObject: {fileID: 8685790303553767874, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ m_PrefabInstance: {fileID: 8977898853425847701}
+ m_PrefabAsset: {fileID: 0}
+--- !u!1 &296612175404815451 stripped
+GameObject:
+ m_CorrespondingSourceObject: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807,
+ type: 3}
+ m_PrefabInstance: {fileID: 8977898853425847701}
+ m_PrefabAsset: {fileID: 0}
+--- !u!114 &4389916208190318681
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 296612175404815451}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 482bb1f796fe43348bcbfd8161ed3825, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ ChildMovers:
+ - {fileID: 1866518356433686547}
+ - {fileID: 2914594030718603294}
+ TriggerDistanceToMove: 0.25
+ PlayerCamera: {fileID: 8372809022110481992}
+--- !u!114 &5376990732947334198
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 296612175404815451}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: dfb1af1a9249278438d2daa2877ee2ad, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ SyncPositionX: 1
+ SyncPositionY: 1
+ SyncPositionZ: 1
+ SyncRotAngleX: 0
+ SyncRotAngleY: 1
+ SyncRotAngleZ: 0
+ SyncScaleX: 1
+ SyncScaleY: 1
+ SyncScaleZ: 1
+ PositionThreshold: 0.001
+ RotAngleThreshold: 0.01
+ ScaleThreshold: 0.01
+ InLocalSpace: 0
+ Interpolate: 1
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab.meta b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab.meta
new file mode 100644
index 0000000000..39a6843e92
--- /dev/null
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/Resources/PlayerNestedTransforms.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 0a46b240b37c5b74786b1938e8fd2eae
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/testproject/ProjectSettings/EditorBuildSettings.asset b/testproject/ProjectSettings/EditorBuildSettings.asset
index 15c557f6ca..4b0fb15a49 100644
--- a/testproject/ProjectSettings/EditorBuildSettings.asset
+++ b/testproject/ProjectSettings/EditorBuildSettings.asset
@@ -116,6 +116,9 @@ EditorBuildSettings:
- enabled: 1
path: Assets/Tests/Manual/NestedNetworkTransforms/NestedNetworkTransforms.unity
guid: 92b8cccf28cbaba40854a025b66e2ac3
+ - enabled: 1
+ path: Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTestScene.unity
+ guid: 14235ed18eff0964cbb3ff7ae2ed1933
m_configObjects:
com.unity.addressableassets: {fileID: 11400000, guid: 5a3d5c53c25349c48912726ae850f3b0,
type: 2}
From 18946fb9acff5fe8fa8380c52d9002e961d81aa2 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Wed, 9 Nov 2022 15:56:09 -0600
Subject: [PATCH 19/26] style
removing unused const string value.
---
.../Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs
index 8accb2aba8..c1853c0d0b 100644
--- a/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/ParentingInSceneObjectsTests.cs
@@ -12,7 +12,6 @@ public class ParentingInSceneObjectsTests : IntegrationTestWithApproximation
{
private const string k_BaseSceneToLoad = "UnitTestBaseScene";
private const string k_TestSceneToLoad = "ParentingInSceneObjects";
- private const string k_NestedUndeGameObjectName = "RootParent_GameObject";
private const int k_NumIterationsDeparentReparent = 100;
private const float k_AproximateThresholdValue = 0.001f;
From b1f346ccc35702a999717dce665bedd6264b9f85 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Thu, 10 Nov 2022 11:56:40 -0600
Subject: [PATCH 20/26] style
Updated and added comments for readability
Removed whitespace and a local network variable that was not needed.
---
.../Components/NetworkTransform.cs | 1 -
.../Runtime/Core/NetworkBehaviour.cs | 30 +++++++++----------
.../Runtime/Core/NetworkObject.cs | 8 +++--
.../NestedNetworkTransformTests.cs | 18 +++++++----
4 files changed, 32 insertions(+), 25 deletions(-)
diff --git a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs
index 0234a0d168..d9e8393f34 100644
--- a/com.unity.netcode.gameobjects/Components/NetworkTransform.cs
+++ b/com.unity.netcode.gameobjects/Components/NetworkTransform.cs
@@ -226,7 +226,6 @@ internal void ClearBitSetForNextTick()
public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter
{
-
serializer.SerializeValue(ref SentTime);
// InLocalSpace + HasXXX Bits
serializer.SerializeValue(ref m_Bitset);
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
index e565466804..6dfd4ab908 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
@@ -764,14 +764,12 @@ internal void MarkVariablesDirty(bool dirty)
}
///
- /// Synchronizes by writing all NetworkVariable fields' values the client can read that are
- /// defined within the NetworkBehaviour.
+ /// Synchronizes by setting only the NetworkVariable field values that the client has permission to read.
/// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject)
///
///
- /// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled
- /// each NetworkVariable field will be preceded by the number of bytes
- /// written for that specific field.
+ /// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled each NetworkVariable field will be preceded
+ /// by the number of bytes written for that specific field.
///
internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClientId)
{
@@ -815,13 +813,12 @@ internal void WriteNetworkVariableData(FastBufferWriter writer, ulong targetClie
}
///
- /// Synchronizes by setting all NetworkVariable fields' values that the client can read
+ /// Synchronizes by setting only the NetworkVariable field values that the client has permission to read.
/// Note: This is only invoked when first synchronizing a NetworkBehaviour (i.e. late join or spawned NetworkObject)
///
///
- /// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled
- /// each NetworkVariable field will be preceded by the number of bytes
- /// written for that specific field.
+ /// When NetworkConfig.EnsureNetworkVariableLengthSafety is enabled each NetworkVariable field will be preceded
+ /// by the number of bytes written for that specific field.
///
internal void SetNetworkVariableData(FastBufferReader reader, ulong clientId)
{
@@ -886,8 +883,11 @@ protected NetworkObject GetNetworkObject(ulong networkId)
}
///
- /// When overridden, this method is invoked on a NetworkBehaviour during client synchronization in order
- /// to provide the ability to inject custom synchronization information for derived NetworkBehaviours.
+ /// Override this method if your derived NetworkBehaviour requires custom synchronization data.
+ /// When serializing (writing) this will be invoked during the client synchronization period for
+ /// each instance as well as when spawning a new NetworkObject.
+ /// When deserializing (reading), this will be invoked prior to the NetworkBehaviour's associated
+ /// NetworkObject is spawned.
///
/// The serializer to use to read and write the data.
///
@@ -904,9 +904,8 @@ protected virtual void OnSynchronize(ref BufferSerializer serializer) wher
/// be synchronized when first instantiated prior to its associated NetworkObject being spawned.
///
///
- /// Only invoked during the initial synchronization (i.e. late join client or newly spawned NetworkObject).
- /// This includes try-catch to assure if user code fails the rest of the synchronization process will not
- /// be impacted (i.e. if one NetworkBehaviour fails the rest will still be synchronized)
+ /// This includes try-catch blocks to recover from exceptions that might occur and continue to
+ /// synchronize any remaining NetworkBehaviours.
///
/// true if it wrote synchronization data and false if it did not
internal bool Synchronize(ref BufferSerializer serializer) where T : IReaderWriter
@@ -917,8 +916,7 @@ internal bool Synchronize(ref BufferSerializer serializer) where T : IRead
var writer = serializer.GetFastBufferWriter();
// Save our position before we attempt to write anything so we can seek back to it (i.e. error occurs)
var positionBeforeWrite = writer.Position;
- var networkBehaviourId = NetworkBehaviourId;
- writer.WriteValueSafe(networkBehaviourId);
+ writer.WriteValueSafe(NetworkBehaviourId);
// Save our position where we will write the final size being written so we can skip over it in the
// event an exception occurs when deserializing.
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
index e39dc98766..e3da678461 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
@@ -1237,8 +1237,12 @@ internal void PostNetworkVariableWrite()
}
///
- /// Handles synchronizing NetworkVariables as well as custom user synchronization code for NetworkBeahviours
+ /// Handles synchronizing NetworkVariables as well as custom user synchronization code for NetworkBeahviours.
///
+ ///
+ /// 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.
+ ///
internal void SynchronizeNetworkBehaviours(ref BufferSerializer serializer, ulong targetClientId = 0) where T : IReaderWriter
{
if (serializer.IsWriter)
@@ -1390,7 +1394,7 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId)
/// when the client is approved or during a scene transition
///
/// Deserialized scene object data
- /// reader for the NetworkVariable data
+ /// FastBufferReader for the NetworkVariable data
/// NetworkManager instance
/// optional to use NetworkObject deserialized
internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBufferReader reader, NetworkManager networkManager)
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
index af49929f29..f966c8863f 100644
--- a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
@@ -25,10 +25,13 @@ public class NestedNetworkTransformTests : IntegrationTestWithApproximation
protected override void OnOneTimeSetup()
{
ChildMover.RandomizeScale = true;
+ // Preserve the test runner scene that is currently the active scene
m_OriginalActiveScene = SceneManager.GetActiveScene();
+ // Load our test's scene used by all client players (it gets set as the currently active scene when loaded)
m_PlayerPrefabResource = Resources.Load(k_PlayerToLoad);
Assert.NotNull(m_PlayerPrefabResource, $"Failed to load resource {k_PlayerToLoad}");
- // Migrate the resource into the DDOL
+
+ // Migrate the resource into the DDOL to prevent the server from thinking it is in-scene placed.
Object.DontDestroyOnLoad(m_PlayerPrefabResource);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
SceneManager.LoadScene(k_TestScene, LoadSceneMode.Additive);
@@ -38,10 +41,12 @@ protected override void OnOneTimeSetup()
protected override void OnOneTimeTearDown()
{
ChildMover.RandomizeScale = false;
+ // Set test runner's scene back to the currently active scene
if (m_OriginalActiveScene.IsValid() && m_OriginalActiveScene.isLoaded)
{
SceneManager.SetActiveScene(m_OriginalActiveScene);
}
+ // Unload our base scene if it is still loaded
if (m_BaseSceneLoaded.IsValid() && m_BaseSceneLoaded.isLoaded)
{
SceneManager.UnloadSceneAsync(m_BaseSceneLoaded);
@@ -68,6 +73,7 @@ private void SceneManager_sceneLoaded(Scene sceneLoaded, LoadSceneMode loadScene
protected override IEnumerator OnTearDown()
{
+ // This prevents us from trying to destroy the resource loaded
m_PlayerPrefab = null;
return base.OnTearDown();
}
@@ -75,16 +81,16 @@ protected override IEnumerator OnTearDown()
protected override void OnCreatePlayerPrefab()
{
+ // Destroy the default player prefab
Object.DestroyImmediate(m_PlayerPrefab);
+ // Assign the network prefab resource loaded
m_PlayerPrefab = m_PlayerPrefabResource as GameObject;
base.OnCreatePlayerPrefab();
}
- protected override void OnNewClientStarted(NetworkManager networkManager)
- {
- base.OnNewClientStarted(networkManager);
- }
-
+ ///
+ /// Prevent the server from telling the clients to load our test scene
+ ///
private bool VerifySceneServer(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
{
if (sceneName == k_TestScene)
From f18c4a13fb7019dbaa5befce10768db114a261f6 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Thu, 10 Nov 2022 12:02:04 -0600
Subject: [PATCH 21/26] style
one last minor adjustment to a comment for better clarity.
---
com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
index e3da678461..241e19f63b 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
@@ -1237,7 +1237,7 @@ internal void PostNetworkVariableWrite()
}
///
- /// Handles synchronizing NetworkVariables as well as custom user synchronization code for NetworkBeahviours.
+ /// Handles synchronizing NetworkVariables and custom synchronization data for NetworkBeahviours.
///
///
/// This is where we determine how much data is written after the associated NetworkObject in order to recover
From 8439b497cf3bbebd3a491ccc3baeace0b89f28d9 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Thu, 10 Nov 2022 12:03:02 -0600
Subject: [PATCH 22/26] style
>.<
Spelling issue.
---
com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
index 241e19f63b..122b6f1ee4 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
@@ -1237,7 +1237,7 @@ internal void PostNetworkVariableWrite()
}
///
- /// Handles synchronizing NetworkVariables and custom synchronization data for NetworkBeahviours.
+ /// Handles synchronizing NetworkVariables and custom synchronization data for NetworkBehaviours.
///
///
/// This is where we determine how much data is written after the associated NetworkObject in order to recover
From 56a159af07d15f77ab2fd18c64e0be931268dcbe Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Thu, 10 Nov 2022 12:53:49 -0600
Subject: [PATCH 23/26] style
Removing unused using directive.
---
.../Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
index f966c8863f..d1f145e8f1 100644
--- a/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
+++ b/testproject/Assets/Tests/Runtime/ObjectParenting/NestedNetworkTransformTests.cs
@@ -5,7 +5,6 @@
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.TestTools;
-using Unity.Netcode;
using TestProject.ManualTests;
namespace TestProject.RuntimeTests
From 2afb8996c51751c64f2205c9b4885ef7b7a50309 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Tue, 15 Nov 2022 14:58:41 -0600
Subject: [PATCH 24/26] test
Adding a basic OnSynchronize test.
---
.../NetworkObjectSynchronizationTests.cs | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
index 9b7beb3d52..303a92495d 100644
--- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
@@ -20,6 +20,7 @@ public class NetworkObjectSynchronizationTests : NetcodeIntegrationTest
private GameObject m_NetworkPrefab;
private GameObject m_InValidNetworkPrefab;
private GameObject m_SynchronizationPrefab;
+ private GameObject m_OnSynchronizePrefab;
private VariableLengthSafety m_VariableLengthSafety;
private LogLevel m_CurrentLogLevel;
@@ -67,6 +68,9 @@ protected override void OnServerAndClientsCreated()
m_SynchronizationPrefab.AddComponent();
m_SynchronizationPrefab.AddComponent();
+ m_OnSynchronizePrefab = CreateNetworkObjectPrefab("OnSyncObject");
+ m_OnSynchronizePrefab.AddComponent();
+
base.OnServerAndClientsCreated();
}
@@ -292,6 +296,22 @@ public IEnumerator NetworkBehaviourSynchronization()
ValidateNetworkBehaviourWithNetworkVariables(serverSideSpawnedNetworkObject, clientSideSpawnedNetworkObject);
}
}
+
+ [UnityTest]
+ public IEnumerator NetworkBehaviourOnSynchronize()
+ {
+ var serverSideInstance = SpawnObject(m_OnSynchronizePrefab, m_ServerNetworkManager).GetComponent();
+ // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
+ yield return CreateAndStartNewClient();
+
+ var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
+ var clientSideInstance = clientSideNetworkObjects[serverSideInstance.NetworkObjectId].GetComponent();
+
+ Assert.IsTrue(serverSideInstance.CustomSerializationData.Value1 == clientSideInstance.CustomSerializationData.Value1, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value1}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value1})");
+ Assert.IsTrue(serverSideInstance.CustomSerializationData.Value2 == clientSideInstance.CustomSerializationData.Value2, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value2}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value2})");
+ Assert.IsTrue(serverSideInstance.CustomSerializationData.Value3 == clientSideInstance.CustomSerializationData.Value3, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value3}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value3})");
+ Assert.IsTrue(serverSideInstance.CustomSerializationData.Value4 == clientSideInstance.CustomSerializationData.Value4, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value4}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value4})");
+ }
}
///
@@ -558,4 +578,42 @@ protected override void OnSynchronize(ref BufferSerializer serializer)
m_MyCustomData.NetworkSerialize(serializer);
}
}
+
+ public class NetworkBehaviourOnSynchronizeComponent : NetworkBehaviour
+ {
+ public SomeCustomSerializationData CustomSerializationData = new SomeCustomSerializationData();
+
+ public struct SomeCustomSerializationData : INetworkSerializable
+ {
+ public uint Value1;
+ public bool Value2;
+ public long Value3;
+ public float Value4;
+ public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter
+ {
+ serializer.SerializeValue(ref Value1);
+ serializer.SerializeValue(ref Value2);
+ serializer.SerializeValue(ref Value3);
+ serializer.SerializeValue(ref Value4);
+ }
+ }
+
+ public override void OnNetworkSpawn()
+ {
+ if (IsServer)
+ {
+ CustomSerializationData.Value1 = (uint)Random.Range(0, 10000);
+ CustomSerializationData.Value2 = true;
+ CustomSerializationData.Value3 = Random.Range(0, 10000);
+ CustomSerializationData.Value4 = Random.Range(-1000.0f, 1000.0f);
+ }
+ base.OnNetworkSpawn();
+ }
+
+ protected override void OnSynchronize(ref BufferSerializer serializer)
+ {
+ serializer.SerializeNetworkSerializable(ref CustomSerializationData);
+ base.OnSynchronize(ref serializer);
+ }
+ }
}
From 7ea739f66afd472d7f6cbc19f8c1e4b5f2a14636 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Tue, 15 Nov 2022 15:04:46 -0600
Subject: [PATCH 25/26] style
updated comments
---
.../NetworkObject/NetworkObjectSynchronizationTests.cs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
index 303a92495d..7f58283c27 100644
--- a/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
+++ b/com.unity.netcode.gameobjects/Tests/Runtime/NetworkObject/NetworkObjectSynchronizationTests.cs
@@ -297,16 +297,21 @@ public IEnumerator NetworkBehaviourSynchronization()
}
}
+ ///
+ /// A basic validation for the NetworkBehaviour.OnSynchronize method
+ ///
[UnityTest]
public IEnumerator NetworkBehaviourOnSynchronize()
{
var serverSideInstance = SpawnObject(m_OnSynchronizePrefab, m_ServerNetworkManager).GetComponent();
- // Now spawn and connect a client that will fail to spawn half of the NetworkObjects spawned
+
+ // Now spawn and connect a client that will have custom serialized data applied during the client synchronization process.
yield return CreateAndStartNewClient();
var clientSideNetworkObjects = s_GlobalNetworkObjects[m_ClientNetworkManagers[0].LocalClientId];
var clientSideInstance = clientSideNetworkObjects[serverSideInstance.NetworkObjectId].GetComponent();
+ // Validate the values match
Assert.IsTrue(serverSideInstance.CustomSerializationData.Value1 == clientSideInstance.CustomSerializationData.Value1, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value1}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value1})");
Assert.IsTrue(serverSideInstance.CustomSerializationData.Value2 == clientSideInstance.CustomSerializationData.Value2, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value2}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value2})");
Assert.IsTrue(serverSideInstance.CustomSerializationData.Value3 == clientSideInstance.CustomSerializationData.Value3, $"Client-side instance Value1 ({serverSideInstance.CustomSerializationData.Value3}) does not equal server-side instance Value1 ({clientSideInstance.CustomSerializationData.Value3})");
From decaa5a52e5b3c44f57afdc80d8ee2ffb0214443 Mon Sep 17 00:00:00 2001
From: NoelStephensUnity <73188597+NoelStephensUnity@users.noreply.github.com>
Date: Tue, 15 Nov 2022 21:33:42 -0600
Subject: [PATCH 26/26] style
updating public API xml documentation for NetworkBehaviour.OnSynchronize
---
.../Runtime/Core/NetworkBehaviour.cs | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
index 2139e7b8eb..1a43947852 100644
--- a/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
+++ b/com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
@@ -884,11 +884,16 @@ protected NetworkObject GetNetworkObject(ulong networkId)
///
/// Override this method if your derived NetworkBehaviour requires custom synchronization data.
- /// When serializing (writing) this will be invoked during the client synchronization period for
- /// each instance as well as when spawning a new NetworkObject.
- /// When deserializing (reading), this will be invoked prior to the NetworkBehaviour's associated
- /// NetworkObject is spawned.
+ /// Note: Use of this method is only for the initial client synchronization of NetworkBehaviours
+ /// and will increase the payload size for client synchronization and dynamically spawned
+ /// s.
///
+ ///
+ /// When serializing (writing) this will be invoked during the client synchronization period and
+ /// when spawning new NetworkObjects.
+ /// When deserializing (reading), this will be invoked prior to the NetworkBehaviour's associated
+ /// NetworkObject being spawned.
+ ///
/// The serializer to use to read and write the data.
///
/// Either BufferSerializerReader or BufferSerializerWriter, depending whether the serializer