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.0-pre.8] - 2022-04-27 ### Changed - `unmanaged` structs are no longer universally accepted as RPC parameters because some structs (i.e., structs with pointers in them, such as `NativeList<T>`) can't be supported by the default memcpy struct serializer. Structs that are intended to be serialized across the network must add `INetworkSerializeByMemcpy` to the interface list (i.e., `struct Foo : INetworkSerializeByMemcpy`). This interface is empty and just serves to mark the struct as compatible with memcpy serialization. For external structs you can't edit, you can pass them to RPCs by wrapping them in `ForceNetworkSerializeByMemcpy<T>`. (#1901) ### Removed - Removed `SIPTransport` (#1870) - Removed `ClientNetworkTransform` from the package samples and moved to Boss Room's Utilities package which can be found [here](https://github.com/Unity-Technologies/com.unity.multiplayer.samples.coop/blob/main/Packages/com.unity.multiplayer.samples.coop/Utilities/Net/ClientAuthority/ClientNetworkTransform.cs). ### Fixed - Fixed `NetworkTransform` generating false positive rotation delta checks when rolling over between 0 and 360 degrees. (#1890) - Fixed client throwing an exception if it has messages in the outbound queue when processing the `NetworkEvent.Disconnect` event and is using UTP. (#1884) - Fixed issue during client synchronization if 'ValidateSceneBeforeLoading' returned false it would halt the client synchronization process resulting in a client that was approved but not synchronized or fully connected with the server. (#1883) - Fixed an issue where UNetTransport.StartServer would return success even if the underlying transport failed to start (#854) - Passing generic types to RPCs no longer causes a native crash (#1901) - Fixed an issue where calling `Shutdown` on a `NetworkManager` that was already shut down would cause an immediate shutdown the next time it was started (basically the fix makes `Shutdown` idempotent). (#1877)
143 lines
5.7 KiB
C#
143 lines
5.7 KiB
C#
using System.Collections;
|
|
using Unity.Netcode.Components;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
using UnityEngine.TestTools;
|
|
using Unity.Netcode.TestHelpers.Runtime;
|
|
|
|
|
|
namespace Unity.Netcode.RuntimeTests
|
|
{
|
|
public class TransformInterpolationObject : NetworkBehaviour
|
|
{
|
|
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)
|
|
{
|
|
Debug.LogError($"Interpolation failure. transform.position.y is {transform.position.y}. Should be between 0.0 and 100.0");
|
|
}
|
|
}
|
|
|
|
// Move the nested object on the server
|
|
if (IsMoving)
|
|
{
|
|
var y = Time.realtimeSinceStartup;
|
|
while (y > 10.0f)
|
|
{
|
|
y -= 10.0f;
|
|
}
|
|
|
|
// change the space between local and global every second
|
|
GetComponent<NetworkTransform>().InLocalSpace = ((int)y % 2 == 0);
|
|
|
|
transform.position = new Vector3(0.0f, y * 10, 0.0f);
|
|
}
|
|
|
|
// On the server, make sure to keep the parent object at a fixed position
|
|
if (IsFixed)
|
|
{
|
|
transform.position = new Vector3(1000.0f, 1000.0f, 1000.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class TransformInterpolationTests : NetcodeIntegrationTest
|
|
{
|
|
protected override int NumberOfClients => 1;
|
|
|
|
private GameObject m_PrefabToSpawn;
|
|
|
|
private NetworkObject m_SpawnedAsNetworkObject;
|
|
private NetworkObject m_SpawnedObjectOnClient;
|
|
|
|
private NetworkObject m_BaseAsNetworkObject;
|
|
private NetworkObject m_BaseOnClient;
|
|
|
|
|
|
protected override void OnServerAndClientsCreated()
|
|
{
|
|
m_PrefabToSpawn = CreateNetworkObjectPrefab("InterpTestObject");
|
|
m_PrefabToSpawn.AddComponent<NetworkTransform>();
|
|
m_PrefabToSpawn.AddComponent<TransformInterpolationObject>();
|
|
}
|
|
|
|
private IEnumerator RefreshNetworkObjects()
|
|
{
|
|
var clientId = m_ClientNetworkManagers[0].LocalClientId;
|
|
yield return WaitForConditionOrTimeOut(() => s_GlobalNetworkObjects.ContainsKey(clientId) &&
|
|
s_GlobalNetworkObjects[clientId].ContainsKey(m_BaseAsNetworkObject.NetworkObjectId) &&
|
|
s_GlobalNetworkObjects[clientId].ContainsKey(m_SpawnedAsNetworkObject.NetworkObjectId));
|
|
|
|
Assert.False(s_GlobalTimeoutHelper.TimedOut, $"Timed out waiting for client side {nameof(NetworkObject)} ID of {m_SpawnedAsNetworkObject.NetworkObjectId}");
|
|
|
|
m_BaseOnClient = s_GlobalNetworkObjects[clientId][m_BaseAsNetworkObject.NetworkObjectId];
|
|
// make sure the objects are set with the right network manager
|
|
m_BaseOnClient.NetworkManagerOwner = m_ClientNetworkManagers[0];
|
|
|
|
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]
|
|
public IEnumerator TransformInterpolationTest()
|
|
{
|
|
// create an object
|
|
var spawnedObject = Object.Instantiate(m_PrefabToSpawn);
|
|
var baseObject = Object.Instantiate(m_PrefabToSpawn);
|
|
baseObject.GetComponent<NetworkObject>().NetworkManagerOwner = m_ServerNetworkManager;
|
|
baseObject.GetComponent<NetworkObject>().Spawn();
|
|
|
|
m_SpawnedAsNetworkObject = spawnedObject.GetComponent<NetworkObject>();
|
|
m_SpawnedAsNetworkObject.NetworkManagerOwner = m_ServerNetworkManager;
|
|
|
|
m_BaseAsNetworkObject = baseObject.GetComponent<NetworkObject>();
|
|
m_BaseAsNetworkObject.NetworkManagerOwner = m_ServerNetworkManager;
|
|
|
|
|
|
m_SpawnedAsNetworkObject.TrySetParent(baseObject);
|
|
|
|
m_SpawnedAsNetworkObject.Spawn();
|
|
|
|
yield return RefreshNetworkObjects();
|
|
|
|
m_SpawnedAsNetworkObject.TrySetParent(baseObject);
|
|
|
|
baseObject.GetComponent<TransformInterpolationObject>().IsFixed = true;
|
|
spawnedObject.GetComponent<TransformInterpolationObject>().IsMoving = true;
|
|
|
|
const float maxPlacementError = 0.01f;
|
|
|
|
// Wait for the base object to place itself on both instances
|
|
while (m_BaseOnClient.transform.position.y < 1000 - maxPlacementError ||
|
|
m_BaseOnClient.transform.position.y > 1000 + maxPlacementError ||
|
|
baseObject.transform.position.y < 1000 - maxPlacementError ||
|
|
baseObject.transform.position.y > 1000 + maxPlacementError)
|
|
{
|
|
yield return new WaitForSeconds(0.01f);
|
|
}
|
|
|
|
m_SpawnedObjectOnClient.GetComponent<TransformInterpolationObject>().CheckPosition = true;
|
|
|
|
// Test that interpolation works correctly for 10 seconds
|
|
// Increasing this duration gives you the opportunity to go check in the Editor how the objects are setup
|
|
// and how they move
|
|
yield return new WaitForSeconds(10.0f);
|
|
}
|
|
}
|
|
}
|