com.unity.netcode.gameobjects@1.0.1
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). ## [1.0.1] - 2022-08-23 ### Changed - Changed version to 1.0.1. (#2131) - Updated dependency on `com.unity.transport` to 1.2.0. (#2129) - When using `UnityTransport`, _reliable_ payloads are now allowed to exceed the configured 'Max Payload Size'. Unreliable payloads remain bounded by this setting. (#2081) - Preformance improvements for cases with large number of NetworkObjects, by not iterating over all unchanged NetworkObjects ### Fixed - Fixed an issue where reading/writing more than 8 bits at a time with BitReader/BitWriter would write/read from the wrong place, returning and incorrect result. (#2130) - Fixed issue with the internal `NetworkTransformState.m_Bitset` flag not getting cleared upon the next tick advancement. (#2110) - Fixed interpolation issue with `NetworkTransform.Teleport`. (#2110) - Fixed issue where the authoritative side was interpolating its transform. (#2110) - Fixed Owner-written NetworkVariable infinitely write themselves (#2109) - Fixed NetworkList issue that showed when inserting at the very end of a NetworkList (#2099) - Fixed issue where a client owner of a `NetworkVariable` with both owner read and write permissions would not update the server side when changed. (#2097) - Fixed issue when attempting to spawn a parent `GameObject`, with `NetworkObject` component attached, that has one or more child `GameObject`s, that are inactive in the hierarchy, with `NetworkBehaviour` components it will no longer attempt to spawn the associated `NetworkBehaviour`(s) or invoke ownership changed notifications but will log a warning message. (#2096) - Fixed an issue where destroying a NetworkBehaviour would not deregister it from the parent NetworkObject, leading to exceptions when the parent was later destroyed. (#2091) - Fixed issue where `NetworkObject.NetworkHide` was despawning and destroying, as opposed to only despawning, in-scene placed `NetworkObject`s. (#2086) - Fixed `NetworkAnimator` synchronizing transitions twice due to it detecting the change in animation state once a transition is started by a trigger. (#2084) - Fixed issue where `NetworkAnimator` would not synchronize a looping animation for late joining clients if it was at the very end of its loop. (#2076) - Fixed issue where `NetworkAnimator` was not removing its subscription from `OnClientConnectedCallback` when despawned during the shutdown sequence. (#2074) - Fixed IsServer and IsClient being set to false before object despawn during the shutdown sequence. (#2074) - Fixed NetworkList Value event on the server. PreviousValue is now set correctly when a new value is set through property setter. (#2067) - Fixed NetworkLists not populating on client. NetworkList now uses the most recent list as opposed to the list at the end of previous frame, when sending full updates to dynamically spawned NetworkObject. The difference in behaviour is required as scene management spawns those objects at a different time in the frame, relative to updates. (#2062)
This commit is contained in:
@@ -4,7 +4,6 @@ using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Unity.Netcode.EditorTests
|
||||
{
|
||||
@@ -21,20 +20,20 @@ namespace Unity.Netcode.EditorTests
|
||||
var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget);
|
||||
var buildTargetSupported = BuildPipeline.IsBuildTargetSupported(buildTargetGroup, buildTarget);
|
||||
|
||||
var buildReport = BuildPipeline.BuildPlayer(
|
||||
new[] { Path.Combine(packagePath, DefaultBuildScenePath) },
|
||||
Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", nameof(BuildTests)),
|
||||
buildTarget,
|
||||
BuildOptions.None
|
||||
);
|
||||
|
||||
if (buildTargetSupported)
|
||||
{
|
||||
var buildReport = BuildPipeline.BuildPlayer(
|
||||
new[] { Path.Combine(packagePath, DefaultBuildScenePath) },
|
||||
Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", nameof(BuildTests)),
|
||||
buildTarget,
|
||||
BuildOptions.None
|
||||
);
|
||||
|
||||
Assert.AreEqual(BuildResult.Succeeded, buildReport.summary.result);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAssert.Expect(LogType.Error, "Error building player because build target was unsupported");
|
||||
Debug.Log($"Skipped building player due to Unsupported Build Target");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -179,5 +180,122 @@ namespace Unity.Netcode.EditorTests
|
||||
Assert.AreEqual(handlerFour, systemThree.MessageHandlers[systemThree.GetMessageType(typeof(TestMessageFour))]);
|
||||
}
|
||||
}
|
||||
|
||||
internal class AAAEarlyLexicographicNetworkMessage : INetworkMessage
|
||||
{
|
||||
public void Serialize(FastBufferWriter writer)
|
||||
{
|
||||
}
|
||||
|
||||
public bool Deserialize(FastBufferReader reader, ref NetworkContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Handle(ref NetworkContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable IDE1006
|
||||
internal class zzzLateLexicographicNetworkMessage : AAAEarlyLexicographicNetworkMessage
|
||||
{
|
||||
}
|
||||
#pragma warning restore IDE1006
|
||||
|
||||
internal class OrderingMessageProvider : IMessageProvider
|
||||
{
|
||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||
{
|
||||
var listMessages = new List<MessagingSystem.MessageWithHandler>();
|
||||
|
||||
var messageWithHandler = new MessagingSystem.MessageWithHandler();
|
||||
|
||||
messageWithHandler.MessageType = typeof(zzzLateLexicographicNetworkMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
messageWithHandler.MessageType = typeof(ConnectionRequestMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
messageWithHandler.MessageType = typeof(ConnectionApprovedMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
messageWithHandler.MessageType = typeof(OrderingMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
messageWithHandler.MessageType = typeof(AAAEarlyLexicographicNetworkMessage);
|
||||
listMessages.Add(messageWithHandler);
|
||||
|
||||
return listMessages;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MessagesGetPrioritizedCorrectly()
|
||||
{
|
||||
var sender = new NopMessageSender();
|
||||
var provider = new OrderingMessageProvider();
|
||||
var messagingSystem = new MessagingSystem(sender, null, provider);
|
||||
|
||||
// the 3 priority messages should appear first, in lexicographic order
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[0], typeof(ConnectionApprovedMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[1], typeof(ConnectionRequestMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[2], typeof(OrderingMessage));
|
||||
|
||||
// the other should follow after
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(zzzLateLexicographicNetworkMessage));
|
||||
|
||||
// there should not be any extras
|
||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
||||
|
||||
// reorder the zzz one to position 3
|
||||
messagingSystem.ReorderMessage(3, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
||||
|
||||
// the 3 priority messages should still appear first, in lexicographic order
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[0], typeof(ConnectionApprovedMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[1], typeof(ConnectionRequestMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[2], typeof(OrderingMessage));
|
||||
|
||||
// the other should follow after, but reordered
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||
|
||||
// there should still not be any extras
|
||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
||||
|
||||
// verify we get an exception when asking for an invalid position
|
||||
try
|
||||
{
|
||||
messagingSystem.ReorderMessage(-1, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
}
|
||||
|
||||
// reorder the zzz one to position 3, again, to check nothing bad happens
|
||||
messagingSystem.ReorderMessage(3, XXHash.Hash32(typeof(zzzLateLexicographicNetworkMessage).FullName));
|
||||
|
||||
// the two non-priority should not have moved
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[3], typeof(zzzLateLexicographicNetworkMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||
|
||||
// there should still not be any extras
|
||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 5);
|
||||
|
||||
// 4242 is a random hash that should not match anything
|
||||
messagingSystem.ReorderMessage(3, 4242);
|
||||
|
||||
// that should result in an extra entry
|
||||
Assert.AreEqual(messagingSystem.MessageHandlerCount, 6);
|
||||
|
||||
// with a null handler
|
||||
Assert.AreEqual(messagingSystem.MessageHandlers[3], null);
|
||||
|
||||
// and it should have bumped the previous messages down
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[4], typeof(zzzLateLexicographicNetworkMessage));
|
||||
Assert.AreEqual(messagingSystem.MessageTypes[5], typeof(AAAEarlyLexicographicNetworkMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Random = System.Random;
|
||||
|
||||
namespace Unity.Netcode.EditorTests
|
||||
{
|
||||
@@ -40,11 +44,24 @@ namespace Unity.Netcode.EditorTests
|
||||
}
|
||||
}
|
||||
|
||||
private class TestMessageProvider : IMessageProvider
|
||||
private class TestMessageProvider : IMessageProvider, IDisposable
|
||||
{
|
||||
// Keep track of what we sent
|
||||
private List<List<MessagingSystem.MessageWithHandler>> m_CachedMessages = new List<List<MessagingSystem.MessageWithHandler>>();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var cachedItem in m_CachedMessages)
|
||||
{
|
||||
// Clear out any references to MessagingSystem.MessageWithHandlers
|
||||
cachedItem.Clear();
|
||||
}
|
||||
m_CachedMessages.Clear();
|
||||
}
|
||||
|
||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||
{
|
||||
return new List<MessagingSystem.MessageWithHandler>
|
||||
var messageList = new List<MessagingSystem.MessageWithHandler>
|
||||
{
|
||||
new MessagingSystem.MessageWithHandler
|
||||
{
|
||||
@@ -52,9 +69,13 @@ namespace Unity.Netcode.EditorTests
|
||||
Handler = MessagingSystem.ReceiveMessage<TestMessage>
|
||||
}
|
||||
};
|
||||
// Track messages sent
|
||||
m_CachedMessages.Add(messageList);
|
||||
return messageList;
|
||||
}
|
||||
}
|
||||
|
||||
private TestMessageProvider m_TestMessageProvider;
|
||||
private TestMessageSender m_MessageSender;
|
||||
private MessagingSystem m_MessagingSystem;
|
||||
private ulong[] m_Clients = { 0 };
|
||||
@@ -63,15 +84,16 @@ namespace Unity.Netcode.EditorTests
|
||||
public void SetUp()
|
||||
{
|
||||
TestMessage.Serialized = false;
|
||||
|
||||
m_MessageSender = new TestMessageSender();
|
||||
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, new TestMessageProvider());
|
||||
m_TestMessageProvider = new TestMessageProvider();
|
||||
m_MessagingSystem = new MessagingSystem(m_MessageSender, this, m_TestMessageProvider);
|
||||
m_MessagingSystem.ClientConnected(0);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
m_TestMessageProvider.Dispose();
|
||||
m_MessagingSystem.Dispose();
|
||||
}
|
||||
|
||||
@@ -224,5 +246,56 @@ namespace Unity.Netcode.EditorTests
|
||||
Assert.AreEqual(message2, receivedMessage2);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestNoHandlerMessageProvider : IMessageProvider
|
||||
{
|
||||
public List<MessagingSystem.MessageWithHandler> GetMessages()
|
||||
{
|
||||
return new List<MessagingSystem.MessageWithHandler>
|
||||
{
|
||||
new MessagingSystem.MessageWithHandler
|
||||
{
|
||||
MessageType = typeof(TestMessage),
|
||||
Handler = null
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhenReceivingAMessageWithoutAHandler_ExceptionIsLogged()
|
||||
{
|
||||
// If a MessagingSystem already exists then dispose of it before creating a new MessagingSystem (otherwise memory leak)
|
||||
if (m_MessagingSystem != null)
|
||||
{
|
||||
m_MessagingSystem.Dispose();
|
||||
m_MessagingSystem = null;
|
||||
}
|
||||
|
||||
// Since m_MessagingSystem is disposed during teardown we don't need to worry about that here.
|
||||
m_MessagingSystem = new MessagingSystem(new NopMessageSender(), this, new TestNoHandlerMessageProvider());
|
||||
m_MessagingSystem.ClientConnected(0);
|
||||
|
||||
var messageHeader = new MessageHeader
|
||||
{
|
||||
MessageSize = (ushort)UnsafeUtility.SizeOf<TestMessage>(),
|
||||
MessageType = m_MessagingSystem.GetMessageType(typeof(TestMessage)),
|
||||
};
|
||||
var message = GetMessage();
|
||||
|
||||
var writer = new FastBufferWriter(1300, Allocator.Temp);
|
||||
using (writer)
|
||||
{
|
||||
writer.TryBeginWrite(FastBufferWriter.GetWriteSize(message));
|
||||
writer.WriteValue(message);
|
||||
|
||||
var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||
using (reader)
|
||||
{
|
||||
m_MessagingSystem.HandleMessage(messageHeader, reader, 0, 0, 0);
|
||||
LogAssert.Expect(LogType.Exception, new Regex(".*HandlerNotRegisteredException.*"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,55 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.Netcode.EditorTests.NetworkVar
|
||||
{
|
||||
public class NetworkVarTests
|
||||
{
|
||||
public class NetworkVarComponent : NetworkBehaviour
|
||||
{
|
||||
public NetworkVariable<int> NetworkVariable = new NetworkVariable<int>();
|
||||
}
|
||||
[Test]
|
||||
public void TestAssignmentUnchanged()
|
||||
{
|
||||
var intVar = new NetworkVariable<int>();
|
||||
|
||||
intVar.Value = 314159265;
|
||||
|
||||
intVar.OnValueChanged += (value, newValue) =>
|
||||
var gameObjectMan = new GameObject();
|
||||
var networkManager = gameObjectMan.AddComponent<NetworkManager>();
|
||||
networkManager.BehaviourUpdater = new NetworkBehaviourUpdater();
|
||||
var gameObject = new GameObject();
|
||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
||||
networkObject.NetworkManagerOwner = networkManager;
|
||||
var networkVarComponent = gameObject.AddComponent<NetworkVarComponent>();
|
||||
networkVarComponent.NetworkVariable.Initialize(networkVarComponent);
|
||||
networkVarComponent.NetworkVariable.Value = 314159265;
|
||||
networkVarComponent.NetworkVariable.OnValueChanged += (value, newValue) =>
|
||||
{
|
||||
Assert.Fail("OnValueChanged was invoked when setting the same value");
|
||||
};
|
||||
|
||||
intVar.Value = 314159265;
|
||||
networkVarComponent.NetworkVariable.Value = 314159265;
|
||||
Object.DestroyImmediate(gameObject);
|
||||
Object.DestroyImmediate(gameObjectMan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAssignmentChanged()
|
||||
{
|
||||
var intVar = new NetworkVariable<int>();
|
||||
|
||||
intVar.Value = 314159265;
|
||||
|
||||
var gameObjectMan = new GameObject();
|
||||
var networkManager = gameObjectMan.AddComponent<NetworkManager>();
|
||||
networkManager.BehaviourUpdater = new NetworkBehaviourUpdater();
|
||||
var gameObject = new GameObject();
|
||||
var networkObject = gameObject.AddComponent<NetworkObject>();
|
||||
var networkVarComponent = gameObject.AddComponent<NetworkVarComponent>();
|
||||
networkObject.NetworkManagerOwner = networkManager;
|
||||
networkVarComponent.NetworkVariable.Initialize(networkVarComponent);
|
||||
networkVarComponent.NetworkVariable.Value = 314159265;
|
||||
var changed = false;
|
||||
|
||||
intVar.OnValueChanged += (value, newValue) =>
|
||||
networkVarComponent.NetworkVariable.OnValueChanged += (value, newValue) =>
|
||||
{
|
||||
changed = true;
|
||||
};
|
||||
|
||||
intVar.Value = 314159266;
|
||||
|
||||
networkVarComponent.NetworkVariable.Value = 314159266;
|
||||
Assert.True(changed);
|
||||
Object.DestroyImmediate(gameObject);
|
||||
Object.DestroyImmediate(gameObjectMan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using NUnit.Framework;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Unity.Netcode.EditorTests
|
||||
{
|
||||
public class UserBitReaderAndBitWriterTests_NCCBUG175
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void WhenBitwiseWritingMoreThan8Bits_ValuesAreCorrect()
|
||||
{
|
||||
using var writer = new FastBufferWriter(1024, Allocator.Temp);
|
||||
ulong inVal = 123456789;
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
writer.WriteValueSafe(i);
|
||||
}
|
||||
|
||||
using (var bitWriter = writer.EnterBitwiseContext())
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
Assert.IsTrue((bitWriter.TryBeginWriteBits(32)));
|
||||
bitWriter.WriteBits(inVal, 31);
|
||||
bitWriter.WriteBit(true);
|
||||
}
|
||||
}
|
||||
|
||||
using var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
reader.ReadValueSafe(out int outVal);
|
||||
Assert.AreEqual(i, outVal);
|
||||
}
|
||||
|
||||
using var bitReader = reader.EnterBitwiseContext();
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
Assert.IsTrue(bitReader.TryBeginReadBits(32));
|
||||
bitReader.ReadBits(out ulong outVal, 31);
|
||||
bitReader.ReadBit(out bool bit);
|
||||
Assert.AreEqual(inVal, outVal);
|
||||
Assert.AreEqual(true, bit);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhenBitwiseReadingMoreThan8Bits_ValuesAreCorrect()
|
||||
{
|
||||
using var writer = new FastBufferWriter(1024, Allocator.Temp);
|
||||
ulong inVal = 123456789;
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
writer.WriteValueSafe(i);
|
||||
}
|
||||
|
||||
uint combined = (uint)inVal | (1u << 31);
|
||||
writer.WriteValueSafe(combined);
|
||||
writer.WriteValueSafe(combined);
|
||||
writer.WriteValueSafe(combined);
|
||||
|
||||
using var reader = new FastBufferReader(writer, Allocator.Temp);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
reader.ReadValueSafe(out int outVal);
|
||||
Assert.AreEqual(i, outVal);
|
||||
}
|
||||
|
||||
using (var bitReader = reader.EnterBitwiseContext())
|
||||
{
|
||||
Assert.IsTrue(bitReader.TryBeginReadBits(32));
|
||||
bitReader.ReadBits(out ulong outVal, 31);
|
||||
bitReader.ReadBit(out bool bit);
|
||||
Assert.AreEqual(inVal, outVal);
|
||||
Assert.AreEqual(true, bit);
|
||||
|
||||
Assert.IsTrue(bitReader.TryBeginReadBits(32));
|
||||
bitReader.ReadBits(out outVal, 31);
|
||||
bitReader.ReadBit(out bit);
|
||||
Assert.AreEqual(inVal, outVal);
|
||||
Assert.AreEqual(true, bit);
|
||||
|
||||
Assert.IsTrue(bitReader.TryBeginReadBits(32));
|
||||
bitReader.ReadBits(out outVal, 31);
|
||||
bitReader.ReadBit(out bit);
|
||||
Assert.AreEqual(inVal, outVal);
|
||||
Assert.AreEqual(true, bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adfa622d42824b70a39a30b6aa22c9c5
|
||||
timeCreated: 1660758428
|
||||
@@ -15,6 +15,9 @@
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
|
||||
@@ -53,7 +53,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
|
||||
public bool EnableTesting;
|
||||
private bool m_Initialized;
|
||||
private bool m_FinishedTests;
|
||||
private bool m_ChangesAppliedToNetworkVariables;
|
||||
|
||||
@@ -148,6 +147,11 @@ namespace Unity.Netcode.RuntimeTests
|
||||
return m_FinishedTests;
|
||||
}
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
InitializeTest();
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
private void Update()
|
||||
{
|
||||
@@ -164,37 +168,29 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
if (NetworkManager != null && NetworkManager.IsListening)
|
||||
{
|
||||
if (!m_Initialized)
|
||||
{
|
||||
InitializeTest();
|
||||
m_Initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Now change all of the values to make sure we are at least testing the local callback
|
||||
m_NetworkVariableBool.Value = false;
|
||||
m_NetworkVariableByte.Value = 255;
|
||||
m_NetworkVariableColor.Value = new Color(100, 100, 100);
|
||||
m_NetworkVariableColor32.Value = new Color32(100, 100, 100, 100);
|
||||
m_NetworkVariableDouble.Value = 1000;
|
||||
m_NetworkVariableFloat.Value = 1000.0f;
|
||||
m_NetworkVariableInt.Value = 1000;
|
||||
m_NetworkVariableLong.Value = 100000;
|
||||
m_NetworkVariableSByte.Value = -127;
|
||||
m_NetworkVariableQuaternion.Value = new Quaternion(100, 100, 100, 100);
|
||||
m_NetworkVariableShort.Value = short.MaxValue;
|
||||
m_NetworkVariableVector4.Value = new Vector4(1000, 1000, 1000, 1000);
|
||||
m_NetworkVariableVector3.Value = new Vector3(1000, 1000, 1000);
|
||||
m_NetworkVariableVector2.Value = new Vector2(1000, 1000);
|
||||
m_NetworkVariableRay.Value = new Ray(Vector3.one, Vector3.right);
|
||||
m_NetworkVariableULong.Value = ulong.MaxValue;
|
||||
m_NetworkVariableUInt.Value = uint.MaxValue;
|
||||
m_NetworkVariableUShort.Value = ushort.MaxValue;
|
||||
//Now change all of the values to make sure we are at least testing the local callback
|
||||
m_NetworkVariableBool.Value = false;
|
||||
m_NetworkVariableByte.Value = 255;
|
||||
m_NetworkVariableColor.Value = new Color(100, 100, 100);
|
||||
m_NetworkVariableColor32.Value = new Color32(100, 100, 100, 100);
|
||||
m_NetworkVariableDouble.Value = 1000;
|
||||
m_NetworkVariableFloat.Value = 1000.0f;
|
||||
m_NetworkVariableInt.Value = 1000;
|
||||
m_NetworkVariableLong.Value = 100000;
|
||||
m_NetworkVariableSByte.Value = -127;
|
||||
m_NetworkVariableQuaternion.Value = new Quaternion(100, 100, 100, 100);
|
||||
m_NetworkVariableShort.Value = short.MaxValue;
|
||||
m_NetworkVariableVector4.Value = new Vector4(1000, 1000, 1000, 1000);
|
||||
m_NetworkVariableVector3.Value = new Vector3(1000, 1000, 1000);
|
||||
m_NetworkVariableVector2.Value = new Vector2(1000, 1000);
|
||||
m_NetworkVariableRay.Value = new Ray(Vector3.one, Vector3.right);
|
||||
m_NetworkVariableULong.Value = ulong.MaxValue;
|
||||
m_NetworkVariableUInt.Value = uint.MaxValue;
|
||||
m_NetworkVariableUShort.Value = ushort.MaxValue;
|
||||
|
||||
//Set the timeout (i.e. how long we will wait for all NetworkVariables to have registered their changes)
|
||||
m_WaitForChangesTimeout = Time.realtimeSinceStartup + 0.50f;
|
||||
m_ChangesAppliedToNetworkVariables = true;
|
||||
}
|
||||
//Set the timeout (i.e. how long we will wait for all NetworkVariables to have registered their changes)
|
||||
m_WaitForChangesTimeout = Time.realtimeSinceStartup + 0.50f;
|
||||
m_ChangesAppliedToNetworkVariables = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,6 +813,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
[Ignore("This test is unstable (MTT-4146)")]
|
||||
public IEnumerator WhenAMessageIsDeferredForMoreThanTheConfiguredTime_ItIsRemoved([Values(1, 2, 3)] int timeout)
|
||||
{
|
||||
RegisterClientPrefabs();
|
||||
|
||||
82
Tests/Runtime/ListChangedTest.cs
Normal file
82
Tests/Runtime/ListChangedTest.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
public class NetworkListChangedTestComponent : NetworkBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class ListChangedObject : NetworkBehaviour
|
||||
{
|
||||
public int ExpectedPreviousValue = 0;
|
||||
public int ExpectedValue = 0;
|
||||
public bool AddDone = false;
|
||||
|
||||
public NetworkList<int> MyNetworkList = new NetworkList<int>();
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
MyNetworkList.OnListChanged += Changed;
|
||||
base.OnNetworkSpawn();
|
||||
}
|
||||
|
||||
public void Changed(NetworkListEvent<int> listEvent)
|
||||
{
|
||||
if (listEvent.Type == NetworkListEvent<int>.EventType.Value)
|
||||
{
|
||||
if (listEvent.PreviousValue != ExpectedPreviousValue)
|
||||
{
|
||||
Debug.Log($"Expected previous value mismatch {listEvent.PreviousValue} versus {ExpectedPreviousValue}");
|
||||
Debug.Assert(listEvent.PreviousValue == ExpectedPreviousValue);
|
||||
}
|
||||
|
||||
if (listEvent.Value != ExpectedValue)
|
||||
{
|
||||
Debug.Log($"Expected value mismatch {listEvent.Value} versus {ExpectedValue}");
|
||||
Debug.Assert(listEvent.Value == ExpectedValue);
|
||||
}
|
||||
|
||||
AddDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NetworkListChangedTests : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 2;
|
||||
|
||||
private ulong m_ClientId0;
|
||||
private GameObject m_PrefabToSpawn;
|
||||
|
||||
private NetworkObject m_NetSpawnedObject1;
|
||||
|
||||
protected override void OnServerAndClientsCreated()
|
||||
{
|
||||
m_PrefabToSpawn = CreateNetworkObjectPrefab("ListChangedObject");
|
||||
m_PrefabToSpawn.AddComponent<ListChangedObject>();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator NetworkListChangedTest()
|
||||
{
|
||||
m_ClientId0 = m_ClientNetworkManagers[0].LocalClientId;
|
||||
|
||||
// create 3 objects
|
||||
var spawnedObject1 = SpawnObject(m_PrefabToSpawn, m_ServerNetworkManager);
|
||||
m_NetSpawnedObject1 = spawnedObject1.GetComponent<NetworkObject>();
|
||||
|
||||
m_NetSpawnedObject1.GetComponent<ListChangedObject>().MyNetworkList.Add(42);
|
||||
m_NetSpawnedObject1.GetComponent<ListChangedObject>().ExpectedPreviousValue = 42;
|
||||
m_NetSpawnedObject1.GetComponent<ListChangedObject>().ExpectedValue = 44;
|
||||
m_NetSpawnedObject1.GetComponent<ListChangedObject>().MyNetworkList[0] = 44;
|
||||
|
||||
Debug.Assert(m_NetSpawnedObject1.GetComponent<ListChangedObject>().AddDone);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2e5a740c1abd4315801e3f26ecf8adb
|
||||
guid: b269e2a059f814075a737691bc02afa4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -25,10 +25,17 @@ namespace Unity.Netcode.RuntimeTests.Metrics
|
||||
|
||||
var metricValues = waitForMetricValues.AssertMetricValuesHaveBeenFound();
|
||||
|
||||
var networkVariableDeltaSent = metricValues.First();
|
||||
Assert.AreEqual(nameof(NetworkVariableComponent.MyNetworkVariable), networkVariableDeltaSent.Name);
|
||||
Assert.AreEqual(Server.LocalClientId, networkVariableDeltaSent.Connection.Id);
|
||||
Assert.AreNotEqual(0, networkVariableDeltaSent.BytesCount);
|
||||
bool found = false;
|
||||
foreach (var networkVariableDeltaSent in metricValues)
|
||||
{
|
||||
if (nameof(NetworkVariableComponent.MyNetworkVariable) == networkVariableDeltaSent.Name &&
|
||||
Client.LocalClientId == networkVariableDeltaSent.Connection.Id &&
|
||||
0 != networkVariableDeltaSent.BytesCount)
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
Assert.IsTrue(found);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
#if COM_UNITY_MODULES_ANIMATION
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Unity.Netcode.Components;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
[TestFixture(HostOrServer.Host)]
|
||||
[TestFixture(HostOrServer.Server)]
|
||||
public class NetworkAnimatorTests : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 1;
|
||||
|
||||
private GameObject m_PlayerOnServer;
|
||||
private GameObject m_PlayerOnClient;
|
||||
|
||||
private Animator m_PlayerOnServerAnimator;
|
||||
private Animator m_PlayerOnClientAnimator;
|
||||
|
||||
public NetworkAnimatorTests(HostOrServer hostOrServer) : base(hostOrServer) { }
|
||||
|
||||
protected override void OnCreatePlayerPrefab()
|
||||
{
|
||||
// ideally, we would build up the AnimatorController entirely in code and not need an asset,
|
||||
// but after some attempts this doesn't seem readily doable. Instead, we load a controller
|
||||
var controller = Resources.Load("TestAnimatorController") as RuntimeAnimatorController;
|
||||
var animator = m_PlayerPrefab.AddComponent<Animator>();
|
||||
animator.runtimeAnimatorController = controller;
|
||||
|
||||
var networkAnimator = m_PlayerPrefab.AddComponent<NetworkAnimator>();
|
||||
networkAnimator.Animator = animator;
|
||||
}
|
||||
|
||||
protected override IEnumerator OnServerAndClientsConnected()
|
||||
{
|
||||
m_PlayerOnServer = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId].gameObject;
|
||||
m_PlayerOnServerAnimator = m_PlayerOnServerAnimator = m_PlayerOnServer.GetComponent<Animator>();
|
||||
|
||||
m_PlayerOnClient = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ClientNetworkManagers[0].LocalClientId].gameObject;
|
||||
m_PlayerOnClientAnimator = m_PlayerOnClient.GetComponent<Animator>();
|
||||
|
||||
return base.OnServerAndClientsConnected();
|
||||
}
|
||||
|
||||
// helper function to scan an animator and verify a given clip is present
|
||||
private bool HasClip(Animator animator, string clipName)
|
||||
{
|
||||
var clips = new List<AnimatorClipInfo>();
|
||||
animator.GetCurrentAnimatorClipInfo(0, clips);
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
if (clip.clip.name == clipName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationTriggerReset([Values(true, false)] bool asHash)
|
||||
{
|
||||
// We have "UnboundTrigger" purposely not bound to any animations so we can test resetting.
|
||||
// If we used a trigger that was bound to a transition, then the trigger would reset as soon as the
|
||||
// transition happens. This way it will stay stuck on
|
||||
string triggerString = "UnboundTrigger";
|
||||
int triggerHash = Animator.StringToHash(triggerString);
|
||||
|
||||
// Verify trigger is off
|
||||
Assert.True(m_PlayerOnServerAnimator.GetBool(triggerString) == false);
|
||||
Assert.True(m_PlayerOnClientAnimator.GetBool(triggerString) == false);
|
||||
|
||||
// trigger.
|
||||
if (asHash)
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().SetTrigger(triggerHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().SetTrigger(triggerString);
|
||||
}
|
||||
|
||||
// verify trigger is set for client and server
|
||||
yield return WaitForConditionOrTimeOut(() => asHash ? m_PlayerOnServerAnimator.GetBool(triggerHash) : m_PlayerOnServerAnimator.GetBool(triggerString));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out on server trigger set check");
|
||||
|
||||
yield return WaitForConditionOrTimeOut(() => asHash ? m_PlayerOnClientAnimator.GetBool(triggerHash) : m_PlayerOnClientAnimator.GetBool(triggerString));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out on client trigger set check");
|
||||
|
||||
// reset the trigger
|
||||
if (asHash)
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().ResetTrigger(triggerHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().ResetTrigger(triggerString);
|
||||
}
|
||||
|
||||
// verify trigger is reset for client and server
|
||||
yield return WaitForConditionOrTimeOut(() => asHash ? m_PlayerOnServerAnimator.GetBool(triggerHash) == false : m_PlayerOnServerAnimator.GetBool(triggerString) == false);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out on server reset check");
|
||||
|
||||
yield return WaitForConditionOrTimeOut(() => asHash ? m_PlayerOnClientAnimator.GetBool(triggerHash) == false : m_PlayerOnClientAnimator.GetBool(triggerString) == false);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out on client reset check");
|
||||
}
|
||||
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationStateSyncTest()
|
||||
{
|
||||
// check that we have started in the default state
|
||||
Assert.True(m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(0).IsName("DefaultState"));
|
||||
Assert.True(m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(0).IsName("DefaultState"));
|
||||
|
||||
// cause a change to the AlphaState state by setting AlphaParameter, which is
|
||||
// the variable bound to the transition from default to AlphaState (see the TestAnimatorController asset)
|
||||
m_PlayerOnServerAnimator.SetBool("AlphaParameter", true);
|
||||
|
||||
// ...and now we should be in the AlphaState having triggered the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(0).IsName("AlphaState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(0).IsName("AlphaState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Client failed to sync its animation state from the server");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationLayerStateSyncTest()
|
||||
{
|
||||
int layer = 1;
|
||||
// check that we have started in the default state
|
||||
Assert.True(m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(layer).IsName("DefaultStateLayer2"));
|
||||
Assert.True(m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(layer).IsName("DefaultStateLayer2"));
|
||||
|
||||
// cause a change to the AlphaState state by setting AlphaParameter, which is
|
||||
// the variable bound to the transition from default to AlphaState (see the TestAnimatorController asset)
|
||||
m_PlayerOnServerAnimator.SetBool("Layer2AlphaParameter", true);
|
||||
|
||||
// ...and now we should be in the AlphaState having triggered the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(layer).IsName("Layer2AlphaState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(layer).IsName("Layer2AlphaState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Client failed to sync its animation state from the server");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationLayerWeightTest()
|
||||
{
|
||||
int layer = 1;
|
||||
float targetWeight = 0.333f;
|
||||
|
||||
// check that we have started in the default state
|
||||
Assert.True(Mathf.Approximately(m_PlayerOnServerAnimator.GetLayerWeight(layer), 1f));
|
||||
Assert.True(Mathf.Approximately(m_PlayerOnClientAnimator.GetLayerWeight(layer), 1f));
|
||||
|
||||
m_PlayerOnServerAnimator.SetLayerWeight(layer, targetWeight);
|
||||
|
||||
// ...and now we should be in the AlphaState having triggered the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() =>
|
||||
Mathf.Approximately(m_PlayerOnServerAnimator.GetLayerWeight(layer), targetWeight)
|
||||
);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() =>
|
||||
Mathf.Approximately(m_PlayerOnClientAnimator.GetLayerWeight(layer), targetWeight)
|
||||
);
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state");
|
||||
}
|
||||
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationStateSyncTriggerTest([Values(true, false)] bool asHash)
|
||||
{
|
||||
string triggerString = "TestTrigger";
|
||||
int triggerHash = Animator.StringToHash(triggerString);
|
||||
|
||||
// check that we have started in the default state
|
||||
Assert.True(m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(0).IsName("DefaultState"));
|
||||
Assert.True(m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(0).IsName("DefaultState"));
|
||||
|
||||
// cause a change to the AlphaState state by setting TestTrigger
|
||||
// note, we have a special test for triggers because activating triggers via the
|
||||
// NetworkAnimator is special; for other parameters you set them on the Animator and NetworkAnimator
|
||||
// listens. But because triggers are super short and transitory, we require users to call
|
||||
// NetworkAnimator.SetTrigger so we don't miss it
|
||||
if (asHash)
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().SetTrigger(triggerHash);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PlayerOnServer.GetComponent<NetworkAnimator>().SetTrigger(triggerString);
|
||||
}
|
||||
|
||||
// ...and now we should be in the AlphaState having triggered the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnServerAnimator.GetCurrentAnimatorStateInfo(0).IsName("TriggeredState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its animation state via trigger");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() => m_PlayerOnClientAnimator.GetCurrentAnimatorStateInfo(0).IsName("TriggeredState"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Client failed to sync its animation state from the server via trigger");
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator AnimationStateSyncTestWithOverride()
|
||||
{
|
||||
// set up the animation override controller
|
||||
var overrideController = Resources.Load("TestAnimatorOverrideController") as AnimatorOverrideController;
|
||||
m_PlayerOnServer.GetComponent<Animator>().runtimeAnimatorController = overrideController;
|
||||
m_PlayerOnClient.GetComponent<Animator>().runtimeAnimatorController = overrideController;
|
||||
|
||||
// in our default state, we should see the OverrideDefaultAnimation clip
|
||||
Assert.True(HasClip(m_PlayerOnServerAnimator, "OverrideDefaultAnimation"));
|
||||
Assert.True(HasClip(m_PlayerOnClientAnimator, "OverrideDefaultAnimation"));
|
||||
|
||||
// cause a change to the AlphaState state by setting AlphaParameter, which is
|
||||
// the variable bound to the transition from default to AlphaState (see the TestAnimatorController asset)
|
||||
m_PlayerOnServerAnimator.SetBool("AlphaParameter", true);
|
||||
|
||||
// ...and now we should be in the AlphaState having set the AlphaParameter
|
||||
yield return WaitForConditionOrTimeOut(() => HasClip(m_PlayerOnServerAnimator, "OverrideAlphaAnimation"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Server failed to reach its overriden animation state");
|
||||
|
||||
// ...and now the client should also have sync'd and arrived at the correct state
|
||||
yield return WaitForConditionOrTimeOut(() => HasClip(m_PlayerOnServerAnimator, "OverrideAlphaAnimation"));
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Client failed to reach its overriden animation state");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // COM_UNITY_MODULES_ANIMATION
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3a8707ef624947a7ae8843ca6c70c0a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AlphaAnimation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db8faf64ca46248abb6624513ac1fb1b
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: DefaultAnimation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1f6191147839943ab93e2171cc15c5e9
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Layer2Animation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d31c84f6372c54d7eb8decb27010d005
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: OverrideAlphaAnimation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05a2afc2ff8884d32afc64ed6765880a
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,53 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!74 &7400000
|
||||
AnimationClip:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: OverrideDefaultAnimation
|
||||
serializedVersion: 6
|
||||
m_Legacy: 0
|
||||
m_Compressed: 0
|
||||
m_UseHighQualityCurve: 1
|
||||
m_RotationCurves: []
|
||||
m_CompressedRotationCurves: []
|
||||
m_EulerCurves: []
|
||||
m_PositionCurves: []
|
||||
m_ScaleCurves: []
|
||||
m_FloatCurves: []
|
||||
m_PPtrCurves: []
|
||||
m_SampleRate: 60
|
||||
m_WrapMode: 0
|
||||
m_Bounds:
|
||||
m_Center: {x: 0, y: 0, z: 0}
|
||||
m_Extent: {x: 0, y: 0, z: 0}
|
||||
m_ClipBindingConstant:
|
||||
genericBindings: []
|
||||
pptrCurveMapping: []
|
||||
m_AnimationClipSettings:
|
||||
serializedVersion: 2
|
||||
m_AdditiveReferencePoseClip: {fileID: 0}
|
||||
m_AdditiveReferencePoseTime: 0
|
||||
m_StartTime: 0
|
||||
m_StopTime: 1
|
||||
m_OrientationOffsetY: 0
|
||||
m_Level: 0
|
||||
m_CycleOffset: 0
|
||||
m_HasAdditiveReferencePose: 0
|
||||
m_LoopTime: 0
|
||||
m_LoopBlend: 0
|
||||
m_LoopBlendOrientation: 0
|
||||
m_LoopBlendPositionY: 0
|
||||
m_LoopBlendPositionXZ: 0
|
||||
m_KeepOriginalOrientation: 0
|
||||
m_KeepOriginalPositionY: 1
|
||||
m_KeepOriginalPositionXZ: 0
|
||||
m_HeightFromFeet: 0
|
||||
m_Mirror: 0
|
||||
m_EditorCurves: []
|
||||
m_EulerEditorCurves: []
|
||||
m_HasGenericRootTransform: 0
|
||||
m_HasMotionFloatCurves: 0
|
||||
m_Events: []
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf503a5569d0b4df4910a26d09ce4530
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 7400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,449 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1102 &-8144973961595650150
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: New State
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 0}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &-7257898091357968356
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: New State
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 0}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &-7235917949335567458
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 2
|
||||
m_ConditionEvent: Layer2AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 6016706997111698284}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 2
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1101 &-6097014330458455406
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 2
|
||||
m_ConditionEvent: AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -1198466922477486815}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1107 &-1914299053840757887
|
||||
AnimatorStateMachine:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Base Layer
|
||||
m_ChildStates:
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: -1198466922477486815}
|
||||
m_Position: {x: 70, y: 290, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 320527679719022362}
|
||||
m_Position: {x: 110, y: 490, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 3942933370568001311}
|
||||
m_Position: {x: 380, y: 280, z: 0}
|
||||
m_ChildStateMachines: []
|
||||
m_AnyStateTransitions: []
|
||||
m_EntryTransitions: []
|
||||
m_StateMachineTransitions: {}
|
||||
m_StateMachineBehaviours: []
|
||||
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||
m_EntryPosition: {x: 30, y: 180, z: 0}
|
||||
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||
m_DefaultState: {fileID: -1198466922477486815}
|
||||
--- !u!1102 &-1198466922477486815
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: DefaultState
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 232953446134799302}
|
||||
- {fileID: 8340347106517238820}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: 1f6191147839943ab93e2171cc15c5e9, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!91 &9100000
|
||||
AnimatorController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: TestAnimatorController
|
||||
serializedVersion: 5
|
||||
m_AnimatorParameters:
|
||||
- m_Name: AlphaParameter
|
||||
m_Type: 4
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: TestTrigger
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: UnboundTrigger
|
||||
m_Type: 9
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- m_Name: Layer2AlphaParameter
|
||||
m_Type: 4
|
||||
m_DefaultFloat: 0
|
||||
m_DefaultInt: 0
|
||||
m_DefaultBool: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
m_AnimatorLayers:
|
||||
- serializedVersion: 5
|
||||
m_Name: Base Layer
|
||||
m_StateMachine: {fileID: -1914299053840757887}
|
||||
m_Mask: {fileID: 0}
|
||||
m_Motions: []
|
||||
m_Behaviours: []
|
||||
m_BlendingMode: 0
|
||||
m_SyncedLayerIndex: -1
|
||||
m_DefaultWeight: 0
|
||||
m_IKPass: 0
|
||||
m_SyncedLayerAffectsTiming: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
- serializedVersion: 5
|
||||
m_Name: Layer2
|
||||
m_StateMachine: {fileID: 1433017894673297828}
|
||||
m_Mask: {fileID: 0}
|
||||
m_Motions: []
|
||||
m_Behaviours: []
|
||||
m_BlendingMode: 0
|
||||
m_SyncedLayerIndex: -1
|
||||
m_DefaultWeight: 1
|
||||
m_IKPass: 0
|
||||
m_SyncedLayerAffectsTiming: 0
|
||||
m_Controller: {fileID: 9100000}
|
||||
--- !u!1101 &232953446134799302
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 320527679719022362}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1102 &320527679719022362
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: AlphaState
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: db8faf64ca46248abb6624513ac1fb1b, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1102 &927597079590233140
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Layer2AlphaState
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: -7235917949335567458}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: d31c84f6372c54d7eb8decb27010d005, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1107 &1433017894673297828
|
||||
AnimatorStateMachine:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Layer2
|
||||
m_ChildStates:
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 6016706997111698284}
|
||||
m_Position: {x: 160, y: 250, z: 0}
|
||||
- serializedVersion: 1
|
||||
m_State: {fileID: 927597079590233140}
|
||||
m_Position: {x: 270, y: 370, z: 0}
|
||||
m_ChildStateMachines: []
|
||||
m_AnyStateTransitions: []
|
||||
m_EntryTransitions: []
|
||||
m_StateMachineTransitions: {}
|
||||
m_StateMachineBehaviours: []
|
||||
m_AnyStatePosition: {x: 50, y: 20, z: 0}
|
||||
m_EntryPosition: {x: 50, y: 120, z: 0}
|
||||
m_ExitPosition: {x: 800, y: 120, z: 0}
|
||||
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
|
||||
m_DefaultState: {fileID: 6016706997111698284}
|
||||
--- !u!1102 &3942933370568001311
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: TriggeredState
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions: []
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 7400000, guid: db8faf64ca46248abb6624513ac1fb1b, type: 2}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &5326371122012901575
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 2
|
||||
m_ConditionEvent: AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: -1198466922477486815}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1102 &6016706997111698284
|
||||
AnimatorState:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: DefaultStateLayer2
|
||||
m_Speed: 1
|
||||
m_CycleOffset: 0
|
||||
m_Transitions:
|
||||
- {fileID: 6324505406226331058}
|
||||
m_StateMachineBehaviours: []
|
||||
m_Position: {x: 50, y: 50, z: 0}
|
||||
m_IKOnFeet: 0
|
||||
m_WriteDefaultValues: 1
|
||||
m_Mirror: 0
|
||||
m_SpeedParameterActive: 0
|
||||
m_MirrorParameterActive: 0
|
||||
m_CycleOffsetParameterActive: 0
|
||||
m_TimeParameterActive: 0
|
||||
m_Motion: {fileID: 0}
|
||||
m_Tag:
|
||||
m_SpeedParameter:
|
||||
m_MirrorParameter:
|
||||
m_CycleOffsetParameter:
|
||||
m_TimeParameter:
|
||||
--- !u!1101 &6324505406226331058
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: Layer2AlphaParameter
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 927597079590233140}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 2
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
--- !u!1101 &8340347106517238820
|
||||
AnimatorStateTransition:
|
||||
m_ObjectHideFlags: 1
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name:
|
||||
m_Conditions:
|
||||
- m_ConditionMode: 1
|
||||
m_ConditionEvent: TestTrigger
|
||||
m_EventTreshold: 0
|
||||
m_DstStateMachine: {fileID: 0}
|
||||
m_DstState: {fileID: 3942933370568001311}
|
||||
m_Solo: 0
|
||||
m_Mute: 0
|
||||
m_IsExit: 0
|
||||
serializedVersion: 3
|
||||
m_TransitionDuration: 0.25
|
||||
m_TransitionOffset: 0
|
||||
m_ExitTime: 0.75
|
||||
m_HasExitTime: 1
|
||||
m_HasFixedDuration: 1
|
||||
m_InterruptionSource: 0
|
||||
m_OrderedInterruption: 1
|
||||
m_CanTransitionToSelf: 1
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0b8ebecb362240989d16159bdfa067c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 9100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,15 +0,0 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!221 &22100000
|
||||
AnimatorOverrideController:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: TestAnimatorOverrideController
|
||||
m_Controller: {fileID: 9100000, guid: a0b8ebecb362240989d16159bdfa067c, type: 2}
|
||||
m_Clips:
|
||||
- m_OriginalClip: {fileID: 7400000, guid: 1f6191147839943ab93e2171cc15c5e9, type: 2}
|
||||
m_OverrideClip: {fileID: 7400000, guid: cf503a5569d0b4df4910a26d09ce4530, type: 2}
|
||||
- m_OriginalClip: {fileID: 7400000, guid: db8faf64ca46248abb6624513ac1fb1b, type: 2}
|
||||
m_OverrideClip: {fileID: 7400000, guid: 05a2afc2ff8884d32afc64ed6765880a, type: 2}
|
||||
@@ -2,6 +2,7 @@ using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
using Unity.Netcode.Components;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
@@ -21,6 +22,53 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
public class SimpleNetworkBehaviour : NetworkBehaviour
|
||||
{
|
||||
public bool OnNetworkDespawnCalled;
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
OnNetworkDespawnCalled = true;
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerator OnSetup()
|
||||
{
|
||||
m_AllowServerToStart = false;
|
||||
return base.OnSetup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This validates the fix for when a child GameObject with a NetworkBehaviour
|
||||
/// is deleted while the parent GameObject with a NetworkObject is spawned and
|
||||
/// is not deleted until a later time would cause an exception due to the
|
||||
/// NetworkBehaviour not being removed from the NetworkObject.ChildNetworkBehaviours
|
||||
/// list.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator ValidatedDisableddNetworkBehaviourWarning()
|
||||
{
|
||||
m_AllowServerToStart = true;
|
||||
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Now just start the Host
|
||||
yield return StartServerAndClients();
|
||||
|
||||
var parentObject = new GameObject();
|
||||
var childObject = new GameObject();
|
||||
childObject.name = "ChildObject";
|
||||
childObject.transform.parent = parentObject.transform;
|
||||
var parentNetworkObject = parentObject.AddComponent<NetworkObject>();
|
||||
var childBehaviour = childObject.AddComponent<NetworkTransform>();
|
||||
|
||||
// Set the child object to be inactive in the hierarchy
|
||||
childObject.SetActive(false);
|
||||
|
||||
LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during ownership assignment!");
|
||||
LogAssert.Expect(LogType.Warning, $"{childObject.name} is disabled! Netcode for GameObjects does not support spawning disabled NetworkBehaviours! The {childBehaviour.GetType().Name} component was skipped during spawn!");
|
||||
|
||||
parentNetworkObject.Spawn();
|
||||
yield return s_DefaultWaitForTick;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,6 +90,9 @@ namespace Unity.Netcode.RuntimeTests
|
||||
// set the log level to developer
|
||||
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
|
||||
|
||||
// The only valid condition for this would be if the NetworkBehaviour is spawned.
|
||||
simpleNetworkBehaviour.IsSpawned = true;
|
||||
|
||||
// Verify the warning gets logged under normal conditions
|
||||
var isNull = simpleNetworkBehaviour.NetworkObject == null;
|
||||
LogAssert.Expect(LogType.Warning, $"[Netcode] Could not get {nameof(NetworkObject)} for the {nameof(NetworkBehaviour)}. Are you missing a {nameof(NetworkObject)} component?");
|
||||
@@ -57,5 +108,44 @@ namespace Unity.Netcode.RuntimeTests
|
||||
networkObjectToTest.Despawn();
|
||||
Object.Destroy(networkObjectToTest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This validates the fix for when a child GameObject with a NetworkBehaviour
|
||||
/// is deleted while the parent GameObject with a NetworkObject is spawned and
|
||||
/// is not deleted until a later time would cause an exception due to the
|
||||
/// NetworkBehaviour not being removed from the NetworkObject.ChildNetworkBehaviours
|
||||
/// list.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator ValidateDeleteChildNetworkBehaviour()
|
||||
{
|
||||
m_AllowServerToStart = true;
|
||||
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Now just start the Host
|
||||
yield return StartServerAndClients();
|
||||
|
||||
var parentObject = new GameObject();
|
||||
var childObject = new GameObject();
|
||||
childObject.transform.parent = parentObject.transform;
|
||||
var parentNetworkObject = parentObject.AddComponent<NetworkObject>();
|
||||
childObject.AddComponent<SimpleNetworkBehaviour>();
|
||||
|
||||
parentNetworkObject.Spawn();
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Destroy the child object with child NetworkBehaviour
|
||||
Object.Destroy(childObject);
|
||||
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Assure no log messages are logged when they should not be logged
|
||||
LogAssert.NoUnexpectedReceived();
|
||||
|
||||
// Destroy the parent object which should not cause any exceptions
|
||||
// (validating the fix)
|
||||
Object.Destroy(parentObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
public NetworkVariable<int> MyNetworkVariable;
|
||||
|
||||
private void Start()
|
||||
private void Awake()
|
||||
{
|
||||
MyNetworkVariable = new NetworkVariable<int>();
|
||||
MyNetworkVariable.OnValueChanged += Changed;
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
#if NGO_TRANSFORM_DEBUG
|
||||
using System.Text.RegularExpressions;
|
||||
#endif
|
||||
using Unity.Netcode.Components;
|
||||
using NUnit.Framework;
|
||||
// using Unity.Netcode.Samples;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
@@ -14,8 +9,15 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
public class NetworkTransformTestComponent : NetworkTransform
|
||||
{
|
||||
public bool ServerAuthority;
|
||||
public bool ReadyToReceivePositionUpdate = false;
|
||||
|
||||
|
||||
protected override bool OnIsServerAuthoritative()
|
||||
{
|
||||
return ServerAuthority;
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
@@ -23,243 +25,531 @@ namespace Unity.Netcode.RuntimeTests
|
||||
ReadyToReceivePositionUpdate = true;
|
||||
}
|
||||
|
||||
public void CommitToTransform()
|
||||
{
|
||||
TryCommitTransformToServer(transform, NetworkManager.LocalTime.Time);
|
||||
}
|
||||
|
||||
public (bool isDirty, bool isPositionDirty, bool isRotationDirty, bool isScaleDirty) ApplyState()
|
||||
{
|
||||
return ApplyLocalNetworkState(transform);
|
||||
var transformState = ApplyLocalNetworkState(transform);
|
||||
return (transformState.IsDirty, transformState.HasPositionChange, transformState.HasRotAngleChange, transformState.HasScaleChange);
|
||||
}
|
||||
}
|
||||
|
||||
// [TestFixture(true, true)]
|
||||
[TestFixture(true, false)]
|
||||
// [TestFixture(false, true)]
|
||||
[TestFixture(false, false)]
|
||||
[TestFixture(HostOrServer.Host, Authority.Server)]
|
||||
[TestFixture(HostOrServer.Host, Authority.Owner)]
|
||||
[TestFixture(HostOrServer.Server, Authority.Server)]
|
||||
[TestFixture(HostOrServer.Server, Authority.Owner)]
|
||||
|
||||
public class NetworkTransformTests : NetcodeIntegrationTest
|
||||
{
|
||||
private NetworkObject m_ClientSideClientPlayer;
|
||||
private NetworkObject m_ServerSideClientPlayer;
|
||||
private NetworkObject m_AuthoritativePlayer;
|
||||
private NetworkObject m_NonAuthoritativePlayer;
|
||||
|
||||
private readonly bool m_TestWithClientNetworkTransform;
|
||||
private NetworkTransformTestComponent m_AuthoritativeTransform;
|
||||
private NetworkTransformTestComponent m_NonAuthoritativeTransform;
|
||||
private NetworkTransformTestComponent m_OwnerTransform;
|
||||
|
||||
public NetworkTransformTests(bool testWithHost, bool testWithClientNetworkTransform)
|
||||
private readonly Authority m_Authority;
|
||||
|
||||
public enum Authority
|
||||
{
|
||||
m_UseHost = testWithHost; // from test fixture
|
||||
m_TestWithClientNetworkTransform = testWithClientNetworkTransform;
|
||||
Server,
|
||||
Owner
|
||||
}
|
||||
|
||||
public enum Interpolation
|
||||
{
|
||||
DisableInterpolate,
|
||||
EnableInterpolate
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="testWithHost">Value is set by TestFixture</param>
|
||||
/// <param name="testWithClientNetworkTransform">Value is set by TestFixture</param>
|
||||
public NetworkTransformTests(HostOrServer testWithHost, Authority authority)
|
||||
{
|
||||
m_UseHost = testWithHost == HostOrServer.Host ? true : false;
|
||||
m_Authority = authority;
|
||||
}
|
||||
|
||||
protected override int NumberOfClients => 1;
|
||||
|
||||
protected override void OnCreatePlayerPrefab()
|
||||
{
|
||||
if (m_TestWithClientNetworkTransform)
|
||||
{
|
||||
// m_PlayerPrefab.AddComponent<ClientNetworkTransform>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var networkTransform = m_PlayerPrefab.AddComponent<NetworkTransformTestComponent>();
|
||||
networkTransform.Interpolate = false;
|
||||
}
|
||||
var networkTransformTestComponent = m_PlayerPrefab.AddComponent<NetworkTransformTestComponent>();
|
||||
networkTransformTestComponent.ServerAuthority = m_Authority == Authority.Server;
|
||||
}
|
||||
|
||||
protected override void OnServerAndClientsCreated()
|
||||
{
|
||||
#if NGO_TRANSFORM_DEBUG
|
||||
// Log assert for writing without authority is a developer log...
|
||||
// TODO: This is why monolithic test base classes and test helpers are an anti-pattern - this is part of an individual test case setup but is separated from the code verifying it!
|
||||
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
|
||||
m_ClientNetworkManagers[0].LogLevel = LogLevel.Developer;
|
||||
#endif
|
||||
if (m_EnableVerboseDebug)
|
||||
{
|
||||
m_ServerNetworkManager.LogLevel = LogLevel.Developer;
|
||||
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||
{
|
||||
clientNetworkManager.LogLevel = LogLevel.Developer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerator OnServerAndClientsConnected()
|
||||
{
|
||||
// Get the client player representation on both the server and the client side
|
||||
m_ServerSideClientPlayer = m_PlayerNetworkObjects[m_ServerNetworkManager.LocalClientId][m_ClientNetworkManagers[0].LocalClientId];
|
||||
m_ClientSideClientPlayer = m_PlayerNetworkObjects[m_ClientNetworkManagers[0].LocalClientId][m_ClientNetworkManagers[0].LocalClientId];
|
||||
var serverSideClientPlayer = m_ServerNetworkManager.ConnectedClients[m_ClientNetworkManagers[0].LocalClientId].PlayerObject;
|
||||
var clientSideClientPlayer = m_ClientNetworkManagers[0].LocalClient.PlayerObject;
|
||||
|
||||
m_AuthoritativePlayer = m_Authority == Authority.Server ? serverSideClientPlayer : clientSideClientPlayer;
|
||||
m_NonAuthoritativePlayer = m_Authority == Authority.Server ? clientSideClientPlayer : serverSideClientPlayer;
|
||||
|
||||
// Get the NetworkTransformTestComponent to make sure the client side is ready before starting test
|
||||
var otherSideNetworkTransformComponent = m_ClientSideClientPlayer.GetComponent<NetworkTransformTestComponent>();
|
||||
m_AuthoritativeTransform = m_AuthoritativePlayer.GetComponent<NetworkTransformTestComponent>();
|
||||
m_NonAuthoritativeTransform = m_NonAuthoritativePlayer.GetComponent<NetworkTransformTestComponent>();
|
||||
|
||||
m_OwnerTransform = m_AuthoritativeTransform.IsOwner ? m_AuthoritativeTransform : m_NonAuthoritativeTransform;
|
||||
|
||||
// Wait for the client-side to notify it is finished initializing and spawning.
|
||||
yield return WaitForConditionOrTimeOut(() => otherSideNetworkTransformComponent.ReadyToReceivePositionUpdate == true);
|
||||
yield return WaitForConditionOrTimeOut(() => m_NonAuthoritativeTransform.ReadyToReceivePositionUpdate == true);
|
||||
AssertOnTimeout("Timed out waiting for client-side to notify it is ready!");
|
||||
|
||||
Assert.True(m_AuthoritativeTransform.CanCommitToTransform);
|
||||
Assert.False(m_NonAuthoritativeTransform.CanCommitToTransform);
|
||||
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client-side to notify it is ready!");
|
||||
|
||||
yield return base.OnServerAndClientsConnected();
|
||||
}
|
||||
|
||||
// TODO: rewrite after perms & authority changes
|
||||
[UnityTest]
|
||||
public IEnumerator TestAuthoritativeTransformChangeOneAtATime([Values] bool testLocalTransform)
|
||||
public enum TransformSpace
|
||||
{
|
||||
// Get the client player's NetworkTransform for both instances
|
||||
var authoritativeNetworkTransform = m_ServerSideClientPlayer.GetComponent<NetworkTransform>();
|
||||
var otherSideNetworkTransform = m_ClientSideClientPlayer.GetComponent<NetworkTransform>();
|
||||
|
||||
Assert.That(!otherSideNetworkTransform.CanCommitToTransform);
|
||||
Assert.That(authoritativeNetworkTransform.CanCommitToTransform);
|
||||
|
||||
if (authoritativeNetworkTransform.CanCommitToTransform)
|
||||
{
|
||||
authoritativeNetworkTransform.InLocalSpace = testLocalTransform;
|
||||
}
|
||||
|
||||
if (otherSideNetworkTransform.CanCommitToTransform)
|
||||
{
|
||||
otherSideNetworkTransform.InLocalSpace = testLocalTransform;
|
||||
}
|
||||
|
||||
float approximation = 0.05f;
|
||||
|
||||
// test position
|
||||
var authPlayerTransform = authoritativeNetworkTransform.transform;
|
||||
|
||||
Assert.AreEqual(Vector3.zero, otherSideNetworkTransform.transform.position, "server side pos should be zero at first"); // sanity check
|
||||
|
||||
authPlayerTransform.position = new Vector3(10, 20, 30);
|
||||
|
||||
yield return WaitForConditionOrTimeOut(() => otherSideNetworkTransform.transform.position.x > approximation);
|
||||
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"timeout while waiting for position change! Otherside value {otherSideNetworkTransform.transform.position.x} vs. Approximation {approximation}");
|
||||
|
||||
Assert.True(new Vector3(10, 20, 30) == otherSideNetworkTransform.transform.position, $"wrong position on ghost, {otherSideNetworkTransform.transform.position}"); // Vector3 already does float approximation with ==
|
||||
|
||||
// test rotation
|
||||
authPlayerTransform.rotation = Quaternion.Euler(45, 40, 35); // using euler angles instead of quaternions directly to really see issues users might encounter
|
||||
Assert.AreEqual(Quaternion.identity, otherSideNetworkTransform.transform.rotation, "wrong initial value for rotation"); // sanity check
|
||||
|
||||
yield return WaitForConditionOrTimeOut(() => otherSideNetworkTransform.transform.rotation.eulerAngles.x > approximation);
|
||||
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "timeout while waiting for rotation change");
|
||||
|
||||
// approximation needed here since eulerAngles isn't super precise.
|
||||
Assert.LessOrEqual(Math.Abs(45 - otherSideNetworkTransform.transform.rotation.eulerAngles.x), approximation, $"wrong rotation on ghost on x, got {otherSideNetworkTransform.transform.rotation.eulerAngles.x}");
|
||||
Assert.LessOrEqual(Math.Abs(40 - otherSideNetworkTransform.transform.rotation.eulerAngles.y), approximation, $"wrong rotation on ghost on y, got {otherSideNetworkTransform.transform.rotation.eulerAngles.y}");
|
||||
Assert.LessOrEqual(Math.Abs(35 - otherSideNetworkTransform.transform.rotation.eulerAngles.z), approximation, $"wrong rotation on ghost on z, got {otherSideNetworkTransform.transform.rotation.eulerAngles.z}");
|
||||
|
||||
// test scale
|
||||
UnityEngine.Assertions.Assert.AreApproximatelyEqual(1f, otherSideNetworkTransform.transform.lossyScale.x, "wrong initial value for scale"); // sanity check
|
||||
UnityEngine.Assertions.Assert.AreApproximatelyEqual(1f, otherSideNetworkTransform.transform.lossyScale.y, "wrong initial value for scale"); // sanity check
|
||||
UnityEngine.Assertions.Assert.AreApproximatelyEqual(1f, otherSideNetworkTransform.transform.lossyScale.z, "wrong initial value for scale"); // sanity check
|
||||
authPlayerTransform.localScale = new Vector3(2, 3, 4);
|
||||
|
||||
yield return WaitForConditionOrTimeOut(() => otherSideNetworkTransform.transform.lossyScale.x > 1f + approximation);
|
||||
|
||||
Assert.False(s_GlobalTimeoutHelper.TimedOut, "timeout while waiting for scale change");
|
||||
|
||||
UnityEngine.Assertions.Assert.AreApproximatelyEqual(2f, otherSideNetworkTransform.transform.lossyScale.x, "wrong scale on ghost");
|
||||
UnityEngine.Assertions.Assert.AreApproximatelyEqual(3f, otherSideNetworkTransform.transform.lossyScale.y, "wrong scale on ghost");
|
||||
UnityEngine.Assertions.Assert.AreApproximatelyEqual(4f, otherSideNetworkTransform.transform.lossyScale.z, "wrong scale on ghost");
|
||||
|
||||
// todo reparent and test
|
||||
// todo test all public API
|
||||
World,
|
||||
Local
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator TestCantChangeTransformFromOtherSideAuthority([Values] bool testClientAuthority)
|
||||
public enum OverrideState
|
||||
{
|
||||
// Get the client player's NetworkTransform for both instances
|
||||
var authoritativeNetworkTransform = m_ServerSideClientPlayer.GetComponent<NetworkTransform>();
|
||||
var otherSideNetworkTransform = m_ClientSideClientPlayer.GetComponent<NetworkTransform>();
|
||||
Update,
|
||||
CommitToTransform
|
||||
}
|
||||
|
||||
Assert.AreEqual(Vector3.zero, otherSideNetworkTransform.transform.position, "other side pos should be zero at first"); // sanity check
|
||||
/// <summary>
|
||||
/// Tests changing all axial values one at a time.
|
||||
/// These tests are performed:
|
||||
/// - While in local space and world space
|
||||
/// - While interpolation is enabled and disabled
|
||||
/// - Using the TryCommitTransformToServer "override" that can be used
|
||||
/// from a child derived or external class.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator TestAuthoritativeTransformChangeOneAtATime([Values] TransformSpace testLocalTransform, [Values] Interpolation interpolation, [Values] OverrideState overideState)
|
||||
{
|
||||
var overrideUpdate = overideState == OverrideState.CommitToTransform;
|
||||
m_AuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_NonAuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
|
||||
otherSideNetworkTransform.transform.position = new Vector3(4, 5, 6);
|
||||
m_AuthoritativeTransform.InLocalSpace = testLocalTransform == TransformSpace.Local;
|
||||
|
||||
// test position
|
||||
var authPlayerTransform = overrideUpdate ? m_OwnerTransform.transform : m_AuthoritativeTransform.transform;
|
||||
|
||||
Assert.AreEqual(Vector3.zero, m_NonAuthoritativeTransform.transform.position, "server side pos should be zero at first"); // sanity check
|
||||
|
||||
authPlayerTransform.position = new Vector3(10, 20, 30);
|
||||
if (overrideUpdate)
|
||||
{
|
||||
m_OwnerTransform.CommitToTransform();
|
||||
}
|
||||
|
||||
yield return WaitForConditionOrTimeOut(PositionsMatch);
|
||||
AssertOnTimeout($"Timed out waiting for positions to match");
|
||||
|
||||
// test rotation
|
||||
Assert.AreEqual(Quaternion.identity, m_NonAuthoritativeTransform.transform.rotation, "wrong initial value for rotation"); // sanity check
|
||||
|
||||
authPlayerTransform.rotation = Quaternion.Euler(45, 40, 35); // using euler angles instead of quaternions directly to really see issues users might encounter
|
||||
if (overrideUpdate)
|
||||
{
|
||||
m_OwnerTransform.CommitToTransform();
|
||||
}
|
||||
|
||||
yield return WaitForConditionOrTimeOut(RotationsMatch);
|
||||
AssertOnTimeout($"Timed out waiting for rotations to match");
|
||||
|
||||
authPlayerTransform.localScale = new Vector3(2, 3, 4);
|
||||
if (overrideUpdate)
|
||||
{
|
||||
m_OwnerTransform.CommitToTransform();
|
||||
}
|
||||
|
||||
yield return WaitForConditionOrTimeOut(ScaleValuesMatch);
|
||||
AssertOnTimeout($"Timed out waiting for scale values to match");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to verify nonAuthority cannot change the transform directly
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator VerifyNonAuthorityCantChangeTransform([Values] Interpolation interpolation)
|
||||
{
|
||||
m_AuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_NonAuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
Assert.AreEqual(Vector3.zero, m_NonAuthoritativeTransform.transform.position, "other side pos should be zero at first"); // sanity check
|
||||
|
||||
m_NonAuthoritativeTransform.transform.position = new Vector3(4, 5, 6);
|
||||
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
Assert.AreEqual(Vector3.zero, otherSideNetworkTransform.transform.position, "got authority error, but other side still moved!");
|
||||
#if NGO_TRANSFORM_DEBUG
|
||||
// We are no longer emitting this warning, and we are banishing tests that rely on console output, so
|
||||
// needs re-implementation
|
||||
// TODO: This should be a separate test - verify 1 behavior per test
|
||||
LogAssert.Expect(LogType.Warning, new Regex(".*without authority detected.*"));
|
||||
#endif
|
||||
}
|
||||
Assert.AreEqual(Vector3.zero, m_NonAuthoritativeTransform.transform.position, "[Position] NonAuthority was able to change the position!");
|
||||
|
||||
var nonAuthorityRotation = m_NonAuthoritativeTransform.transform.rotation;
|
||||
var originalNonAuthorityEulerRotation = nonAuthorityRotation.eulerAngles;
|
||||
var nonAuthorityEulerRotation = originalNonAuthorityEulerRotation;
|
||||
// Verify rotation is not marked dirty when rotated by half of the threshold
|
||||
nonAuthorityEulerRotation.y += 20.0f;
|
||||
nonAuthorityRotation.eulerAngles = nonAuthorityEulerRotation;
|
||||
m_NonAuthoritativeTransform.transform.rotation = nonAuthorityRotation;
|
||||
yield return s_DefaultWaitForTick;
|
||||
var nonAuthorityCurrentEuler = m_NonAuthoritativeTransform.transform.rotation.eulerAngles;
|
||||
Assert.True(originalNonAuthorityEulerRotation.Equals(nonAuthorityCurrentEuler), "[Rotation] NonAuthority was able to change the rotation!");
|
||||
|
||||
var nonAuthorityScale = m_NonAuthoritativeTransform.transform.localScale;
|
||||
m_NonAuthoritativeTransform.transform.localScale = nonAuthorityScale * 100;
|
||||
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
Assert.True(nonAuthorityScale.Equals(m_NonAuthoritativeTransform.transform.localScale), "[Scale] NonAuthority was able to change the scale!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that rotation checks don't produce false positive
|
||||
/// results when rolling over between 0 and 360 degrees
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator TestRotationThresholdDeltaCheck()
|
||||
public IEnumerator TestRotationThresholdDeltaCheck([Values] Interpolation interpolation)
|
||||
{
|
||||
// Get the client player's NetworkTransform for both instances
|
||||
var authoritativeNetworkTransform = m_ServerSideClientPlayer.GetComponent<NetworkTransformTestComponent>();
|
||||
var otherSideNetworkTransform = m_ClientSideClientPlayer.GetComponent<NetworkTransformTestComponent>();
|
||||
otherSideNetworkTransform.RotAngleThreshold = authoritativeNetworkTransform.RotAngleThreshold = 5.0f;
|
||||
m_AuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_NonAuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
|
||||
var halfThreshold = authoritativeNetworkTransform.RotAngleThreshold * 0.5001f;
|
||||
var serverRotation = authoritativeNetworkTransform.transform.rotation;
|
||||
var serverEulerRotation = serverRotation.eulerAngles;
|
||||
m_NonAuthoritativeTransform.RotAngleThreshold = m_AuthoritativeTransform.RotAngleThreshold = 5.0f;
|
||||
|
||||
var halfThreshold = m_AuthoritativeTransform.RotAngleThreshold * 0.5001f;
|
||||
var authorityRotation = m_AuthoritativeTransform.transform.rotation;
|
||||
var authorityEulerRotation = authorityRotation.eulerAngles;
|
||||
|
||||
// Verify rotation is not marked dirty when rotated by half of the threshold
|
||||
serverEulerRotation.y += halfThreshold;
|
||||
serverRotation.eulerAngles = serverEulerRotation;
|
||||
authoritativeNetworkTransform.transform.rotation = serverRotation;
|
||||
var results = authoritativeNetworkTransform.ApplyState();
|
||||
Assert.IsFalse(results.isRotationDirty, $"Rotation is dirty when rotation threshold is {authoritativeNetworkTransform.RotAngleThreshold} degrees and only adjusted by {halfThreshold} degrees!");
|
||||
authorityEulerRotation.y += halfThreshold;
|
||||
authorityRotation.eulerAngles = authorityEulerRotation;
|
||||
m_AuthoritativeTransform.transform.rotation = authorityRotation;
|
||||
var results = m_AuthoritativeTransform.ApplyState();
|
||||
Assert.IsFalse(results.isRotationDirty, $"Rotation is dirty when rotation threshold is {m_AuthoritativeTransform.RotAngleThreshold} degrees and only adjusted by {halfThreshold} degrees!");
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Verify rotation is marked dirty when rotated by another half threshold value
|
||||
serverEulerRotation.y += halfThreshold;
|
||||
serverRotation.eulerAngles = serverEulerRotation;
|
||||
authoritativeNetworkTransform.transform.rotation = serverRotation;
|
||||
results = authoritativeNetworkTransform.ApplyState();
|
||||
Assert.IsTrue(results.isRotationDirty, $"Rotation was not dirty when rotated by the threshold value: {authoritativeNetworkTransform.RotAngleThreshold} degrees!");
|
||||
authorityEulerRotation.y += halfThreshold;
|
||||
authorityRotation.eulerAngles = authorityEulerRotation;
|
||||
m_AuthoritativeTransform.transform.rotation = authorityRotation;
|
||||
results = m_AuthoritativeTransform.ApplyState();
|
||||
Assert.IsTrue(results.isRotationDirty, $"Rotation was not dirty when rotated by the threshold value: {m_AuthoritativeTransform.RotAngleThreshold} degrees!");
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
//Reset rotation back to zero on all axis
|
||||
serverRotation.eulerAngles = serverEulerRotation = Vector3.zero;
|
||||
authoritativeNetworkTransform.transform.rotation = serverRotation;
|
||||
authorityRotation.eulerAngles = authorityEulerRotation = Vector3.zero;
|
||||
m_AuthoritativeTransform.transform.rotation = authorityRotation;
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
// Rotate by 360 minus halfThreshold (which is really just negative halfThreshold) and verify rotation is not marked dirty
|
||||
serverEulerRotation.y = 360 - halfThreshold;
|
||||
serverRotation.eulerAngles = serverEulerRotation;
|
||||
authoritativeNetworkTransform.transform.rotation = serverRotation;
|
||||
results = authoritativeNetworkTransform.ApplyState();
|
||||
authorityEulerRotation.y = 360 - halfThreshold;
|
||||
authorityRotation.eulerAngles = authorityEulerRotation;
|
||||
m_AuthoritativeTransform.transform.rotation = authorityRotation;
|
||||
results = m_AuthoritativeTransform.ApplyState();
|
||||
|
||||
Assert.IsFalse(results.isRotationDirty, $"Rotation is dirty when rotation threshold is {authoritativeNetworkTransform.RotAngleThreshold} degrees and only adjusted by " +
|
||||
$"{Mathf.DeltaAngle(0, serverEulerRotation.y)} degrees!");
|
||||
Assert.IsFalse(results.isRotationDirty, $"Rotation is dirty when rotation threshold is {m_AuthoritativeTransform.RotAngleThreshold} degrees and only adjusted by " +
|
||||
$"{Mathf.DeltaAngle(0, authorityEulerRotation.y)} degrees!");
|
||||
|
||||
serverEulerRotation.y -= halfThreshold;
|
||||
serverRotation.eulerAngles = serverEulerRotation;
|
||||
authoritativeNetworkTransform.transform.rotation = serverRotation;
|
||||
results = authoritativeNetworkTransform.ApplyState();
|
||||
authorityEulerRotation.y -= halfThreshold;
|
||||
authorityRotation.eulerAngles = authorityEulerRotation;
|
||||
m_AuthoritativeTransform.transform.rotation = authorityRotation;
|
||||
results = m_AuthoritativeTransform.ApplyState();
|
||||
|
||||
Assert.IsTrue(results.isRotationDirty, $"Rotation was not dirty when rotated by {Mathf.DeltaAngle(0, serverEulerRotation.y)} degrees!");
|
||||
Assert.IsTrue(results.isRotationDirty, $"Rotation was not dirty when rotated by {Mathf.DeltaAngle(0, authorityEulerRotation.y)} degrees!");
|
||||
|
||||
//Reset rotation back to zero on all axis
|
||||
serverRotation.eulerAngles = serverEulerRotation = Vector3.zero;
|
||||
authoritativeNetworkTransform.transform.rotation = serverRotation;
|
||||
authorityRotation.eulerAngles = authorityEulerRotation = Vector3.zero;
|
||||
m_AuthoritativeTransform.transform.rotation = authorityRotation;
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
serverEulerRotation.y -= halfThreshold;
|
||||
serverRotation.eulerAngles = serverEulerRotation;
|
||||
authoritativeNetworkTransform.transform.rotation = serverRotation;
|
||||
results = authoritativeNetworkTransform.ApplyState();
|
||||
Assert.IsFalse(results.isRotationDirty, $"Rotation is dirty when rotation threshold is {authoritativeNetworkTransform.RotAngleThreshold} degrees and only adjusted by " +
|
||||
$"{Mathf.DeltaAngle(0, serverEulerRotation.y)} degrees!");
|
||||
authorityEulerRotation.y -= halfThreshold;
|
||||
authorityRotation.eulerAngles = authorityEulerRotation;
|
||||
m_AuthoritativeTransform.transform.rotation = authorityRotation;
|
||||
results = m_AuthoritativeTransform.ApplyState();
|
||||
Assert.IsFalse(results.isRotationDirty, $"Rotation is dirty when rotation threshold is {m_AuthoritativeTransform.RotAngleThreshold} degrees and only adjusted by " +
|
||||
$"{Mathf.DeltaAngle(0, authorityEulerRotation.y)} degrees!");
|
||||
|
||||
serverEulerRotation.y -= halfThreshold;
|
||||
serverRotation.eulerAngles = serverEulerRotation;
|
||||
authoritativeNetworkTransform.transform.rotation = serverRotation;
|
||||
results = authoritativeNetworkTransform.ApplyState();
|
||||
authorityEulerRotation.y -= halfThreshold;
|
||||
authorityRotation.eulerAngles = authorityEulerRotation;
|
||||
m_AuthoritativeTransform.transform.rotation = authorityRotation;
|
||||
results = m_AuthoritativeTransform.ApplyState();
|
||||
|
||||
Assert.IsTrue(results.isRotationDirty, $"Rotation was not dirty when rotated by {Mathf.DeltaAngle(0, serverEulerRotation.y)} degrees!");
|
||||
Assert.IsTrue(results.isRotationDirty, $"Rotation was not dirty when rotated by {Mathf.DeltaAngle(0, authorityEulerRotation.y)} degrees!");
|
||||
}
|
||||
|
||||
private bool ValidateBitSetValues(NetworkTransform.NetworkTransformState serverState, NetworkTransform.NetworkTransformState clientState)
|
||||
{
|
||||
if (serverState.HasPositionX == clientState.HasPositionX && serverState.HasPositionY == clientState.HasPositionY && serverState.HasPositionZ == clientState.HasPositionZ &&
|
||||
serverState.HasRotAngleX == clientState.HasRotAngleX && serverState.HasRotAngleY == clientState.HasRotAngleY && serverState.HasRotAngleZ == clientState.HasRotAngleZ &&
|
||||
serverState.HasScaleX == clientState.HasScaleX && serverState.HasScaleY == clientState.HasScaleY && serverState.HasScaleZ == clientState.HasScaleZ)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to make sure that the bitset value is updated properly
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator TestBitsetValue([Values] Interpolation interpolation)
|
||||
{
|
||||
m_AuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_NonAuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_NonAuthoritativeTransform.RotAngleThreshold = m_AuthoritativeTransform.RotAngleThreshold = 0.1f;
|
||||
yield return s_DefaultWaitForTick;
|
||||
|
||||
m_AuthoritativeTransform.transform.rotation = Quaternion.Euler(1, 2, 3);
|
||||
var serverLastSentState = m_AuthoritativeTransform.GetLastSentState();
|
||||
var clientReplicatedState = m_NonAuthoritativeTransform.ReplicatedNetworkState.Value;
|
||||
yield return WaitForConditionOrTimeOut(() => ValidateBitSetValues(serverLastSentState, clientReplicatedState));
|
||||
AssertOnTimeout($"Timed out waiting for Authoritative Bitset state to equal NonAuthoritative replicated Bitset state!");
|
||||
|
||||
yield return WaitForConditionOrTimeOut(RotationsMatch);
|
||||
AssertOnTimeout($"[Timed-Out] Authoritative rotation {m_AuthoritativeTransform.transform.rotation.eulerAngles} != Non-Authoritative rotation {m_NonAuthoritativeTransform.transform.rotation.eulerAngles}");
|
||||
}
|
||||
|
||||
private float m_DetectedPotentialInterpolatedTeleport;
|
||||
|
||||
/// <summary>
|
||||
/// The tests teleporting with and without interpolation
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator TeleportTest([Values] Interpolation interpolation)
|
||||
{
|
||||
m_AuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_NonAuthoritativeTransform.Interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
var authTransform = m_AuthoritativeTransform.transform;
|
||||
var nonAuthPosition = m_NonAuthoritativeTransform.transform.position;
|
||||
var currentTick = m_AuthoritativeTransform.NetworkManager.ServerTime.Tick;
|
||||
m_DetectedPotentialInterpolatedTeleport = 0.0f;
|
||||
var teleportDestination = new Vector3(100.00f, 100.00f, 100.00f);
|
||||
var targetDistance = Mathf.Abs(Vector3.Distance(nonAuthPosition, teleportDestination));
|
||||
m_AuthoritativeTransform.Teleport(new Vector3(100.00f, 100.00f, 100.00f), authTransform.rotation, authTransform.localScale);
|
||||
yield return WaitForConditionOrTimeOut(() => TeleportPositionMatches(nonAuthPosition));
|
||||
|
||||
AssertOnTimeout($"[Timed-Out][Teleport] Timed out waiting for NonAuthoritative position to !");
|
||||
Assert.IsTrue(m_DetectedPotentialInterpolatedTeleport == 0.0f, $"Detected possible interpolation on non-authority side! NonAuthority distance: {m_DetectedPotentialInterpolatedTeleport} | Target distance: {targetDistance}");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This test validates the <see cref="NetworkTransform.SetState(Vector3?, Quaternion?, Vector3?, bool)"/> method
|
||||
/// usage for the non-authoritative side. It will either be the owner or the server making/requesting state changes.
|
||||
/// This validates that:
|
||||
/// - The owner authoritative mode can still be controlled by the server (i.e. owner authoritative with server authority override capabilities)
|
||||
/// - The server authoritative mode can still be directed by the client owner.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This also tests that the original server authoritative model with client-owner driven NetworkTransforms is preserved.
|
||||
/// </remarks>
|
||||
[UnityTest]
|
||||
public IEnumerator NonAuthorityOwnerSettingStateTest([Values] Interpolation interpolation)
|
||||
{
|
||||
var interpolate = interpolation == Interpolation.EnableInterpolate;
|
||||
m_AuthoritativeTransform.Interpolate = interpolate;
|
||||
m_NonAuthoritativeTransform.Interpolate = interpolate;
|
||||
m_NonAuthoritativeTransform.RotAngleThreshold = m_AuthoritativeTransform.RotAngleThreshold = 0.1f;
|
||||
|
||||
// Test one parameter at a time first
|
||||
var newPosition = new Vector3(125f, 35f, 65f);
|
||||
var newRotation = Quaternion.Euler(1, 2, 3);
|
||||
var newScale = new Vector3(2.0f, 2.0f, 2.0f);
|
||||
m_NonAuthoritativeTransform.SetState(newPosition, null, null, interpolate);
|
||||
yield return WaitForConditionOrTimeOut(() => PositionsMatchesValue(newPosition));
|
||||
AssertOnTimeout($"Timed out waiting for non-authoritative position state request to be applied!");
|
||||
Assert.True(Aproximately(newPosition, m_AuthoritativeTransform.transform.position), "Authoritative position does not match!");
|
||||
Assert.True(Aproximately(newPosition, m_NonAuthoritativeTransform.transform.position), "Non-Authoritative position does not match!");
|
||||
|
||||
m_NonAuthoritativeTransform.SetState(null, newRotation, null, interpolate);
|
||||
yield return WaitForConditionOrTimeOut(() => RotationMatchesValue(newRotation.eulerAngles));
|
||||
AssertOnTimeout($"Timed out waiting for non-authoritative rotation state request to be applied!");
|
||||
Assert.True(Aproximately(newRotation.eulerAngles, m_AuthoritativeTransform.transform.rotation.eulerAngles), "Authoritative rotation does not match!");
|
||||
Assert.True(Aproximately(newRotation.eulerAngles, m_NonAuthoritativeTransform.transform.rotation.eulerAngles), "Non-Authoritative rotation does not match!");
|
||||
|
||||
m_NonAuthoritativeTransform.SetState(null, null, newScale, interpolate);
|
||||
yield return WaitForConditionOrTimeOut(() => ScaleMatchesValue(newScale));
|
||||
AssertOnTimeout($"Timed out waiting for non-authoritative scale state request to be applied!");
|
||||
Assert.True(Aproximately(newScale, m_AuthoritativeTransform.transform.localScale), "Authoritative scale does not match!");
|
||||
Assert.True(Aproximately(newScale, m_NonAuthoritativeTransform.transform.localScale), "Non-Authoritative scale does not match!");
|
||||
|
||||
// Test all parameters at once
|
||||
newPosition = new Vector3(55f, 95f, -25f);
|
||||
newRotation = Quaternion.Euler(20, 5, 322);
|
||||
newScale = new Vector3(0.5f, 0.5f, 0.5f);
|
||||
|
||||
m_NonAuthoritativeTransform.SetState(newPosition, newRotation, newScale, interpolate);
|
||||
yield return WaitForConditionOrTimeOut(() => PositionRotationScaleMatches(newPosition, newRotation.eulerAngles, newScale));
|
||||
AssertOnTimeout($"Timed out waiting for non-authoritative position, rotation, and scale state request to be applied!");
|
||||
Assert.True(Aproximately(newPosition, m_AuthoritativeTransform.transform.position), "Authoritative position does not match!");
|
||||
Assert.True(Aproximately(newPosition, m_NonAuthoritativeTransform.transform.position), "Non-Authoritative position does not match!");
|
||||
Assert.True(Aproximately(newRotation.eulerAngles, m_AuthoritativeTransform.transform.rotation.eulerAngles), "Authoritative rotation does not match!");
|
||||
Assert.True(Aproximately(newRotation.eulerAngles, m_NonAuthoritativeTransform.transform.rotation.eulerAngles), "Non-Authoritative rotation does not match!");
|
||||
Assert.True(Aproximately(newScale, m_AuthoritativeTransform.transform.localScale), "Authoritative scale does not match!");
|
||||
Assert.True(Aproximately(newScale, m_NonAuthoritativeTransform.transform.localScale), "Non-Authoritative scale does not match!");
|
||||
}
|
||||
|
||||
private bool Aproximately(float x, float y)
|
||||
{
|
||||
return Mathf.Abs(x - y) <= k_AproximateDeltaVariance;
|
||||
}
|
||||
|
||||
private bool Aproximately(Vector3 a, Vector3 b)
|
||||
{
|
||||
return Mathf.Abs(a.x - b.x) <= k_AproximateDeltaVariance &&
|
||||
Mathf.Abs(a.y - b.y) <= k_AproximateDeltaVariance &&
|
||||
Mathf.Abs(a.z - b.z) <= k_AproximateDeltaVariance;
|
||||
}
|
||||
|
||||
private const float k_AproximateDeltaVariance = 0.01f;
|
||||
private bool PositionsMatchesValue(Vector3 positionToMatch)
|
||||
{
|
||||
var authorityPosition = m_AuthoritativeTransform.transform.position;
|
||||
var nonAuthorityPosition = m_NonAuthoritativeTransform.transform.position;
|
||||
var auhtorityIsEqual = Aproximately(authorityPosition, positionToMatch);
|
||||
var nonauthorityIsEqual = Aproximately(nonAuthorityPosition, positionToMatch);
|
||||
|
||||
if (!auhtorityIsEqual)
|
||||
{
|
||||
VerboseDebug($"Authority position {authorityPosition} != position to match: {positionToMatch}!");
|
||||
}
|
||||
if (!nonauthorityIsEqual)
|
||||
{
|
||||
VerboseDebug($"NonAuthority position {nonAuthorityPosition} != position to match: {positionToMatch}!");
|
||||
}
|
||||
return auhtorityIsEqual && nonauthorityIsEqual;
|
||||
}
|
||||
|
||||
private bool RotationMatchesValue(Vector3 rotationEulerToMatch)
|
||||
{
|
||||
var authorityRotationEuler = m_AuthoritativeTransform.transform.rotation.eulerAngles;
|
||||
var nonAuthorityRotationEuler = m_NonAuthoritativeTransform.transform.rotation.eulerAngles;
|
||||
var auhtorityIsEqual = Aproximately(authorityRotationEuler, rotationEulerToMatch);
|
||||
var nonauthorityIsEqual = Aproximately(nonAuthorityRotationEuler, rotationEulerToMatch);
|
||||
|
||||
if (!auhtorityIsEqual)
|
||||
{
|
||||
VerboseDebug($"Authority rotation {authorityRotationEuler} != rotation to match: {rotationEulerToMatch}!");
|
||||
}
|
||||
if (!nonauthorityIsEqual)
|
||||
{
|
||||
VerboseDebug($"NonAuthority position {nonAuthorityRotationEuler} != rotation to match: {rotationEulerToMatch}!");
|
||||
}
|
||||
return auhtorityIsEqual && nonauthorityIsEqual;
|
||||
}
|
||||
|
||||
private bool ScaleMatchesValue(Vector3 scaleToMatch)
|
||||
{
|
||||
var authorityScale = m_AuthoritativeTransform.transform.localScale;
|
||||
var nonAuthorityScale = m_NonAuthoritativeTransform.transform.localScale;
|
||||
var auhtorityIsEqual = Aproximately(authorityScale, scaleToMatch);
|
||||
var nonauthorityIsEqual = Aproximately(nonAuthorityScale, scaleToMatch);
|
||||
|
||||
if (!auhtorityIsEqual)
|
||||
{
|
||||
VerboseDebug($"Authority scale {authorityScale} != scale to match: {scaleToMatch}!");
|
||||
}
|
||||
if (!nonauthorityIsEqual)
|
||||
{
|
||||
VerboseDebug($"NonAuthority scale {nonAuthorityScale} != scale to match: {scaleToMatch}!");
|
||||
}
|
||||
return auhtorityIsEqual && nonauthorityIsEqual;
|
||||
}
|
||||
|
||||
|
||||
private bool TeleportPositionMatches(Vector3 nonAuthorityOriginalPosition)
|
||||
{
|
||||
var nonAuthorityPosition = m_NonAuthoritativeTransform.transform.position;
|
||||
var authorityPosition = m_AuthoritativeTransform.transform.position;
|
||||
var targetDistance = Mathf.Abs(Vector3.Distance(nonAuthorityOriginalPosition, authorityPosition));
|
||||
var nonAuthorityCurrentDistance = Mathf.Abs(Vector3.Distance(nonAuthorityPosition, nonAuthorityOriginalPosition));
|
||||
if (!Aproximately(targetDistance, nonAuthorityCurrentDistance))
|
||||
{
|
||||
if (nonAuthorityCurrentDistance >= 0.15f * targetDistance && nonAuthorityCurrentDistance <= 0.75f * targetDistance)
|
||||
{
|
||||
m_DetectedPotentialInterpolatedTeleport = nonAuthorityCurrentDistance;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
var xIsEqual = Aproximately(authorityPosition.x, nonAuthorityPosition.x);
|
||||
var yIsEqual = Aproximately(authorityPosition.y, nonAuthorityPosition.y);
|
||||
var zIsEqual = Aproximately(authorityPosition.z, nonAuthorityPosition.z);
|
||||
if (!xIsEqual || !yIsEqual || !zIsEqual)
|
||||
{
|
||||
VerboseDebug($"Authority position {authorityPosition} != NonAuthority position {nonAuthorityPosition}");
|
||||
}
|
||||
return xIsEqual && yIsEqual && zIsEqual; ;
|
||||
}
|
||||
|
||||
private bool PositionRotationScaleMatches(Vector3 position, Vector3 eulerRotation, Vector3 scale)
|
||||
{
|
||||
return PositionsMatchesValue(position) && RotationMatchesValue(eulerRotation) && ScaleMatchesValue(scale);
|
||||
}
|
||||
|
||||
private bool RotationsMatch()
|
||||
{
|
||||
var authorityEulerRotation = m_AuthoritativeTransform.transform.rotation.eulerAngles;
|
||||
var nonAuthorityEulerRotation = m_NonAuthoritativeTransform.transform.rotation.eulerAngles;
|
||||
var xIsEqual = Aproximately(authorityEulerRotation.x, nonAuthorityEulerRotation.x);
|
||||
var yIsEqual = Aproximately(authorityEulerRotation.y, nonAuthorityEulerRotation.y);
|
||||
var zIsEqual = Aproximately(authorityEulerRotation.z, nonAuthorityEulerRotation.z);
|
||||
if (!xIsEqual || !yIsEqual || !zIsEqual)
|
||||
{
|
||||
VerboseDebug($"Authority rotation {authorityEulerRotation} != NonAuthority rotation {nonAuthorityEulerRotation}");
|
||||
}
|
||||
return xIsEqual && yIsEqual && zIsEqual;
|
||||
}
|
||||
|
||||
private bool PositionsMatch()
|
||||
{
|
||||
var authorityPosition = m_AuthoritativeTransform.transform.position;
|
||||
var nonAuthorityPosition = m_NonAuthoritativeTransform.transform.position;
|
||||
var xIsEqual = Aproximately(authorityPosition.x, nonAuthorityPosition.x);
|
||||
var yIsEqual = Aproximately(authorityPosition.y, nonAuthorityPosition.y);
|
||||
var zIsEqual = Aproximately(authorityPosition.z, nonAuthorityPosition.z);
|
||||
if (!xIsEqual || !yIsEqual || !zIsEqual)
|
||||
{
|
||||
VerboseDebug($"Authority position {authorityPosition} != NonAuthority position {nonAuthorityPosition}");
|
||||
}
|
||||
return xIsEqual && yIsEqual && zIsEqual;
|
||||
}
|
||||
|
||||
private bool ScaleValuesMatch()
|
||||
{
|
||||
var authorityScale = m_AuthoritativeTransform.transform.localScale;
|
||||
var nonAuthorityScale = m_NonAuthoritativeTransform.transform.localScale;
|
||||
var xIsEqual = Aproximately(authorityScale.x, nonAuthorityScale.x);
|
||||
var yIsEqual = Aproximately(authorityScale.y, nonAuthorityScale.y);
|
||||
var zIsEqual = Aproximately(authorityScale.z, nonAuthorityScale.z);
|
||||
if (!xIsEqual || !yIsEqual || !zIsEqual)
|
||||
{
|
||||
VerboseDebug($"Authority scale {authorityScale} != NonAuthority scale {nonAuthorityScale}");
|
||||
}
|
||||
return xIsEqual && yIsEqual && zIsEqual;
|
||||
}
|
||||
|
||||
/*
|
||||
* ownership change
|
||||
* test teleport with interpolation
|
||||
* test teleport without interpolation
|
||||
* test dynamic spawning
|
||||
*/
|
||||
protected override IEnumerator OnTearDown()
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(m_PlayerPrefab);
|
||||
m_EnableVerboseDebug = false;
|
||||
Object.DestroyImmediate(m_PlayerPrefab);
|
||||
yield return base.OnTearDown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
public bool FieldWritten;
|
||||
public bool DeltaRead;
|
||||
public bool FieldRead;
|
||||
public bool Dirty = false;
|
||||
|
||||
public override void ResetDirty()
|
||||
{
|
||||
Dirty = false;
|
||||
}
|
||||
|
||||
public override bool IsDirty()
|
||||
{
|
||||
return Dirty;
|
||||
}
|
||||
|
||||
public override void WriteDelta(FastBufferWriter writer)
|
||||
{
|
||||
@@ -138,12 +127,12 @@ namespace Unity.Netcode.RuntimeTests
|
||||
Assert.IsFalse(s_GlobalTimeoutHelper.TimedOut, "Timed out waiting for client side DummyNetBehaviour to register it was spawned!");
|
||||
|
||||
// Check that FieldWritten is written when dirty
|
||||
serverComponent.NetVar.Dirty = true;
|
||||
serverComponent.NetVar.SetDirty(true);
|
||||
yield return s_DefaultWaitForTick;
|
||||
Assert.True(serverComponent.NetVar.FieldWritten);
|
||||
|
||||
// Check that DeltaWritten is written when dirty
|
||||
serverComponent.NetVar.Dirty = true;
|
||||
serverComponent.NetVar.SetDirty(true);
|
||||
yield return s_DefaultWaitForTick;
|
||||
Assert.True(serverComponent.NetVar.DeltaWritten);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
public NetworkVariable<Vector3> OwnerWritable_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableBase.DefaultReadPerm, NetworkVariableWritePermission.Owner);
|
||||
public NetworkVariable<Vector3> ServerWritable_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableBase.DefaultReadPerm, NetworkVariableWritePermission.Server);
|
||||
public NetworkVariable<Vector3> OwnerReadWrite_Position = new NetworkVariable<Vector3>(Vector3.one, NetworkVariableReadPermission.Owner, NetworkVariableWritePermission.Owner);
|
||||
}
|
||||
|
||||
[TestFixtureSource(nameof(TestDataSource))]
|
||||
@@ -104,6 +105,42 @@ namespace Unity.Netcode.RuntimeTests
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CheckOwnerReadWriteAreEqualOnOwnerAndServer()
|
||||
{
|
||||
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
var testCompServer = testObjServer.GetComponent<NetVarPermTestComp>();
|
||||
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||
{
|
||||
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
|
||||
if (testObjServer.OwnerClientId == testObjClient.OwnerClientId &&
|
||||
testCompServer.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value &&
|
||||
testCompServer.OwnerReadWrite_Position.ReadPerm == testCompClient.ServerWritable_Position.ReadPerm &&
|
||||
testCompServer.OwnerReadWrite_Position.WritePerm == testCompClient.ServerWritable_Position.WritePerm)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(NetVarPermTestComp ownerReadWriteObject)
|
||||
{
|
||||
foreach (var clientNetworkManager in m_ClientNetworkManagers)
|
||||
{
|
||||
var testObjClient = clientNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
|
||||
if (testObjClient.OwnerClientId != ownerReadWriteObject.OwnerClientId ||
|
||||
ownerReadWriteObject.OwnerReadWrite_Position.Value == testCompClient.ServerWritable_Position.Value ||
|
||||
ownerReadWriteObject.OwnerReadWrite_Position.ReadPerm != testCompClient.ServerWritable_Position.ReadPerm ||
|
||||
ownerReadWriteObject.OwnerReadWrite_Position.WritePerm != testCompClient.ServerWritable_Position.WritePerm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ServerChangesOwnerWritableNetVar()
|
||||
{
|
||||
@@ -164,6 +201,44 @@ namespace Unity.Netcode.RuntimeTests
|
||||
yield return WaitForOwnerWritableAreEqualOnAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This tests the scenario where a client owner has both read and write
|
||||
/// permissions set. The server should be the only instance that can read
|
||||
/// the NetworkVariable. ServerCannotChangeOwnerWritableNetVar performs
|
||||
/// the same check to make sure the server cannot write to a client owner
|
||||
/// NetworkVariable with owner write permissions.
|
||||
/// </summary>
|
||||
[UnityTest]
|
||||
public IEnumerator ClientOwnerWithReadWriteChangesNetVar()
|
||||
{
|
||||
yield return WaitForOwnerWritableAreEqualOnAll();
|
||||
|
||||
var testObjServer = m_ServerNetworkManager.SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
|
||||
int clientManagerIndex = m_ClientNetworkManagers.Length - 1;
|
||||
var newOwnerClientId = m_ClientNetworkManagers[clientManagerIndex].LocalClientId;
|
||||
testObjServer.ChangeOwnership(newOwnerClientId);
|
||||
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 2);
|
||||
|
||||
yield return WaitForOwnerWritableAreEqualOnAll();
|
||||
|
||||
var testObjClient = m_ClientNetworkManagers[clientManagerIndex].SpawnManager.SpawnedObjects[m_TestObjId];
|
||||
var testCompClient = testObjClient.GetComponent<NetVarPermTestComp>();
|
||||
|
||||
var oldValue = testCompClient.OwnerReadWrite_Position.Value;
|
||||
var newValue = oldValue + new Vector3(Random.Range(0, 100.0f), Random.Range(0, 100.0f), Random.Range(0, 100.0f));
|
||||
|
||||
testCompClient.OwnerWritable_Position.Value = newValue;
|
||||
yield return WaitForPositionsAreEqual(testCompClient.OwnerWritable_Position, newValue);
|
||||
|
||||
// Verify the client owner and server match
|
||||
yield return CheckOwnerReadWriteAreEqualOnOwnerAndServer();
|
||||
|
||||
// Verify the non-owner clients do not have the same Value but do have the same permissions
|
||||
yield return CheckOwnerReadWriteAreNotEqualOnNonOwnerClients(testCompClient);
|
||||
}
|
||||
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ClientCannotChangeServerWritableNetVar()
|
||||
{
|
||||
|
||||
103
Tests/Runtime/OwnerModifiedTests.cs
Normal file
103
Tests/Runtime/OwnerModifiedTests.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using Unity.Netcode.TestHelpers.Runtime;
|
||||
|
||||
namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
// This is a bit of a quirky test.
|
||||
// Addresses MTT-4386 #2109
|
||||
// Where the NetworkVariable updates would be repeated on some clients.
|
||||
// The twist comes fom the updates needing to happens very specifically for the issue to repro in tests
|
||||
|
||||
public class OwnerModifiedObject : NetworkBehaviour, INetworkUpdateSystem
|
||||
{
|
||||
public NetworkList<int> MyNetworkList;
|
||||
|
||||
static internal int Updates = 0;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
MyNetworkList = new NetworkList<int>(new List<int>(), NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
|
||||
MyNetworkList.OnListChanged += Changed;
|
||||
}
|
||||
|
||||
public void Changed(NetworkListEvent<int> listEvent)
|
||||
{
|
||||
var expected = 0;
|
||||
var listString = "";
|
||||
foreach (var i in MyNetworkList)
|
||||
{
|
||||
Assert.AreEqual(i, expected);
|
||||
expected++;
|
||||
listString += i.ToString();
|
||||
}
|
||||
Debug.Log($"[{NetworkManager.LocalClientId}] Value changed to {listString}");
|
||||
Updates++;
|
||||
}
|
||||
|
||||
public bool AddValues;
|
||||
|
||||
public NetworkUpdateStage NetworkUpdateStageToCheck;
|
||||
|
||||
private int m_ValueToUpdate;
|
||||
|
||||
public void NetworkUpdate(NetworkUpdateStage updateStage)
|
||||
{
|
||||
if (updateStage == NetworkUpdateStageToCheck)
|
||||
{
|
||||
if (AddValues)
|
||||
{
|
||||
MyNetworkList.Add(m_ValueToUpdate++);
|
||||
AddValues = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDestroy()
|
||||
{
|
||||
NetworkUpdateLoop.UnregisterAllNetworkUpdates(this);
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
public void InitializeLastCient()
|
||||
{
|
||||
NetworkUpdateLoop.RegisterAllNetworkUpdates(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class OwnerModifiedTests : NetcodeIntegrationTest
|
||||
{
|
||||
protected override int NumberOfClients => 2;
|
||||
|
||||
protected override void OnCreatePlayerPrefab()
|
||||
{
|
||||
m_PlayerPrefab.AddComponent<OwnerModifiedObject>();
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator OwnerModifiedTest()
|
||||
{
|
||||
// We use this to assure we are the "last client" connected.
|
||||
yield return CreateAndStartNewClient();
|
||||
var ownerModLastClient = m_ClientNetworkManagers[2].LocalClient.PlayerObject.GetComponent<OwnerModifiedObject>();
|
||||
ownerModLastClient.InitializeLastCient();
|
||||
|
||||
// Run through all update loops setting the value once every 5 frames
|
||||
foreach (var updateLoopType in System.Enum.GetValues(typeof(NetworkUpdateStage)))
|
||||
{
|
||||
ownerModLastClient.NetworkUpdateStageToCheck = (NetworkUpdateStage)updateLoopType;
|
||||
Debug.Log($"Testing Update Stage: {ownerModLastClient.NetworkUpdateStageToCheck}");
|
||||
ownerModLastClient.AddValues = true;
|
||||
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 5);
|
||||
}
|
||||
|
||||
yield return NetcodeIntegrationTestHelpers.WaitForTicks(m_ServerNetworkManager, 5);
|
||||
|
||||
// We'll have at least one update per stage per client, if all goes well.
|
||||
Assert.True(OwnerModifiedObject.Updates > 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Tests/Runtime/OwnerModifiedTests.cs.meta
Normal file
11
Tests/Runtime/OwnerModifiedTests.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 419d83ebac7544ea9b0a9d5c3eab2c71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -10,21 +10,19 @@ namespace Unity.Netcode.RuntimeTests
|
||||
{
|
||||
public class TransformInterpolationObject : NetworkBehaviour
|
||||
{
|
||||
// Set the minimum threshold which we will use as our margin of error
|
||||
public const float MinThreshold = 0.001f;
|
||||
|
||||
public bool CheckPosition;
|
||||
public bool IsMoving;
|
||||
public bool IsFixed;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Since the local position is transformed from local to global and vice-versa on the server and client
|
||||
// it may accumulate some error. We allow an error of 0.01 over the range of 1000 used in this test.
|
||||
// This requires precision to 5 digits, so it doesn't weaken the test, while preventing spurious failures
|
||||
const float maxRoundingError = 0.01f;
|
||||
|
||||
// Check the position of the nested object on the client
|
||||
if (CheckPosition)
|
||||
{
|
||||
if (transform.position.y < -maxRoundingError || transform.position.y > 100.0f + maxRoundingError)
|
||||
if (transform.position.y < -MinThreshold || transform.position.y > 100.0f + MinThreshold)
|
||||
{
|
||||
Debug.LogError($"Interpolation failure. transform.position.y is {transform.position.y}. Should be between 0.0 and 100.0");
|
||||
}
|
||||
@@ -65,7 +63,8 @@ namespace Unity.Netcode.RuntimeTests
|
||||
protected override void OnServerAndClientsCreated()
|
||||
{
|
||||
m_PrefabToSpawn = CreateNetworkObjectPrefab("InterpTestObject");
|
||||
m_PrefabToSpawn.AddComponent<NetworkTransform>();
|
||||
var networkTransform = m_PrefabToSpawn.AddComponent<NetworkTransform>();
|
||||
networkTransform.PositionThreshold = TransformInterpolationObject.MinThreshold;
|
||||
m_PrefabToSpawn.AddComponent<TransformInterpolationObject>();
|
||||
}
|
||||
|
||||
@@ -85,8 +84,6 @@ namespace Unity.Netcode.RuntimeTests
|
||||
m_SpawnedObjectOnClient = s_GlobalNetworkObjects[clientId][m_SpawnedAsNetworkObject.NetworkObjectId];
|
||||
// make sure the objects are set with the right network manager
|
||||
m_SpawnedObjectOnClient.NetworkManagerOwner = m_ClientNetworkManagers[0];
|
||||
|
||||
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
|
||||
@@ -457,5 +457,26 @@ namespace Unity.Netcode.RuntimeTests
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReliablePayloadsCanBeLargerThanMaximum()
|
||||
{
|
||||
InitializeTransport(out m_Server, out m_ServerEvents);
|
||||
InitializeTransport(out m_Client1, out m_Client1Events);
|
||||
|
||||
m_Server.StartServer();
|
||||
m_Client1.StartClient();
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Connect, m_Client1Events);
|
||||
|
||||
var payloadSize = UnityTransport.InitialMaxPayloadSize + 1;
|
||||
var data = new ArraySegment<byte>(new byte[payloadSize]);
|
||||
|
||||
m_Server.Send(m_Client1.ServerClientId, data, NetworkDelivery.Reliable);
|
||||
|
||||
yield return WaitForNetworkEvent(NetworkEvent.Data, m_Client1Events);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
"optionalUnityReferences": [
|
||||
"TestAssemblies"
|
||||
],
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [
|
||||
{
|
||||
"name": "com.unity.multiplayer.tools",
|
||||
|
||||
Reference in New Issue
Block a user