using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using Unity.Netcode.TestHelpers.Runtime;
namespace Unity.Netcode.RuntimeTests
{
///
/// Runtime tests to test the network time system with the Unity player loop.
///
public class NetworkTimeSystemTests
{
private MonoBehaviourTest m_MonoBehaviourTest; // cache for teardown
[SetUp]
public void Setup()
{
// Create, instantiate, and host
Assert.IsTrue(NetworkManagerHelper.StartNetworkManager(out _));
}
///
/// Tests whether time is accessible and has correct values inside Update/FixedUpdate.
///
///
[UnityTest]
public IEnumerator PlayerLoopTimeTest()
{
m_MonoBehaviourTest = new MonoBehaviourTest();
yield return m_MonoBehaviourTest;
}
///
/// Tests whether the time system invokes the correct amount of ticks over a period of time.
/// Note we cannot test against Time.Time directly because of floating point precision. Our time is more precise leading to different results.
///
///
[UnityTest]
public IEnumerator CorrectAmountTicksTest()
{
var tickSystem = NetworkManager.Singleton.NetworkTickSystem;
var delta = tickSystem.LocalTime.FixedDeltaTime;
var previous_localTickCalculated = 0;
var previous_serverTickCalculated = 0;
while (tickSystem.LocalTime.Time < 3f)
{
yield return null;
var tickCalculated = tickSystem.LocalTime.Time / delta;
previous_localTickCalculated = (int)tickCalculated;
// This check is needed due to double division imprecision of large numbers
if ((tickCalculated - previous_localTickCalculated) >= 0.999999999999)
{
previous_localTickCalculated++;
}
tickCalculated = NetworkManager.Singleton.ServerTime.Time / delta;
previous_serverTickCalculated = (int)tickCalculated;
// This check is needed due to double division imprecision of large numbers
if ((tickCalculated - previous_serverTickCalculated) >= 0.999999999999)
{
previous_serverTickCalculated++;
}
Assert.AreEqual(previous_localTickCalculated, NetworkManager.Singleton.LocalTime.Tick, $"Calculated local tick {previous_localTickCalculated} does not match local tick {NetworkManager.Singleton.LocalTime.Tick}!");
Assert.AreEqual(previous_serverTickCalculated, NetworkManager.Singleton.ServerTime.Tick, $"Calculated server tick {previous_serverTickCalculated} does not match server tick {NetworkManager.Singleton.ServerTime.Tick}!");
Assert.True(Mathf.Approximately((float)NetworkManager.Singleton.LocalTime.Time, (float)NetworkManager.Singleton.ServerTime.Time), $"Local time {(float)NetworkManager.Singleton.LocalTime.Time} is not approximately server time {(float)NetworkManager.Singleton.ServerTime.Time}!");
}
}
[TearDown]
public void TearDown()
{
// Stop, shutdown, and destroy
NetworkManagerHelper.ShutdownNetworkManager();
if (m_MonoBehaviourTest != null)
{
Object.DestroyImmediate(m_MonoBehaviourTest.gameObject);
}
}
}
public class PlayerLoopTimeTestComponent : MonoBehaviour, IMonoBehaviourTest
{
public const int Passes = 100;
private int m_UpdatePasses = 0;
private int m_LastFixedUpdateTick = 0;
private int m_TickOffset = -1;
private NetworkTime m_LocalTimePreviousUpdate;
private NetworkTime m_ServerTimePreviousUpdate;
private NetworkTime m_LocalTimePreviousFixedUpdate;
public void Start()
{
// Run fixed update at same rate as network tick
Time.fixedDeltaTime = NetworkManager.Singleton.LocalTime.FixedDeltaTime;
// Uncap fixed time else we might skip fixed updates
Time.maximumDeltaTime = float.MaxValue;
}
public void Update()
{
// This must run first else it wont run if there is an exception
m_UpdatePasses++;
var localTime = NetworkManager.Singleton.LocalTime;
var serverTime = NetworkManager.Singleton.ServerTime;
// time should have advanced on the host/server
Assert.True(m_LocalTimePreviousUpdate.Time < localTime.Time);
Assert.True(m_ServerTimePreviousUpdate.Time < serverTime.Time);
// time should be further then last fixed step in update
Assert.True(m_LocalTimePreviousFixedUpdate.FixedTime < localTime.Time);
// we should be in same or further tick then fixed update
Assert.True(m_LocalTimePreviousFixedUpdate.Tick <= localTime.Tick);
// fixed update should result in same amounts of tick as network time
if (m_TickOffset == -1)
{
m_TickOffset = serverTime.Tick - m_LastFixedUpdateTick;
}
else
{
// offset of 1 is ok, this happens due to different tick duration offsets
Assert.True(Mathf.Abs(serverTime.Tick - m_TickOffset - m_LastFixedUpdateTick) <= 1);
}
m_LocalTimePreviousUpdate = localTime;
}
public void FixedUpdate()
{
var time = NetworkManager.Singleton.LocalTime;
m_LocalTimePreviousFixedUpdate = time;
Assert.AreEqual(Time.fixedDeltaTime, time.FixedDeltaTime);
Assert.True(Mathf.Approximately((float)NetworkManager.Singleton.LocalTime.Time, (float)NetworkManager.Singleton.ServerTime.Time));
m_LastFixedUpdateTick++;
}
public bool IsTestFinished => m_UpdatePasses >= Passes;
}
}