Skip to content

Commit 5fc93b9

Browse files
becksebenius-unitymattwalsh-unityLukeStampfli
authored
fix: reset tick system on connection approval (MTT-1346) (#1231)
* fix: reset tick system on connection approval (MTT-1346) * fix: don't run time system on clients before they are connected * feat: expose tick system reset as public and add xml docs * test: implement test which checks intialization of time and tick system * fix: run ticks only when connected and reset tick system when time resets. * make initialization test non framerate depended to fix issues when running on mac CI Co-authored-by: Matt Walsh <[email protected]> Co-authored-by: Luke Stampfli <[email protected]>
1 parent dbe4584 commit 5fc93b9

File tree

6 files changed

+138
-3
lines changed

6 files changed

+138
-3
lines changed

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1152,8 +1152,18 @@ private void OnNetworkEarlyUpdate()
11521152
// TODO Once we have a way to subscribe to NetworkUpdateLoop with order we can move this out of NetworkManager but for now this needs to be here because we need strict ordering.
11531153
private void OnNetworkPreUpdate()
11541154
{
1155+
if (IsServer == false && IsConnectedClient == false)
1156+
{
1157+
// As a client wait to run the time system until we are connected.
1158+
return;
1159+
}
1160+
11551161
// Only update RTT here, server time is updated by time sync messages
1156-
NetworkTimeSystem.Advance(Time.deltaTime);
1162+
var reset = NetworkTimeSystem.Advance(Time.deltaTime);
1163+
if (reset)
1164+
{
1165+
NetworkTickSystem.Reset(NetworkTimeSystem.LocalTime, NetworkTimeSystem.ServerTime);
1166+
}
11571167
NetworkTickSystem.UpdateTick(NetworkTimeSystem.LocalTime, NetworkTimeSystem.ServerTime);
11581168

11591169
if (IsServer == false)

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ConnectionApprovedMessage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public void Handle(FastBufferReader reader, ulong clientId, NetworkManager netwo
6565

6666
var time = new NetworkTime(networkManager.NetworkTickSystem.TickRate, NetworkTick);
6767
networkManager.NetworkTimeSystem.Reset(time.Time, 0.15f); // Start with a constant RTT of 150 until we receive values from the transport.
68+
networkManager.NetworkTickSystem.Reset(networkManager.NetworkTimeSystem.LocalTime, networkManager.NetworkTimeSystem.ServerTime);
6869

6970
networkManager.LocalClient = new NetworkClient() { ClientId = networkManager.LocalClientId };
7071

com.unity.netcode.gameobjects/Runtime/Timing/NetworkTickSystem.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ public NetworkTickSystem(uint tickRate, double localTimeSec, double serverTimeSe
5555
ServerTime = new NetworkTime(tickRate, serverTimeSec);
5656
}
5757

58+
/// <summary>
59+
/// Resets the tick system to the given network time.
60+
/// </summary>
61+
/// <param name="localTimeSec">The local time in seconds.</param>
62+
/// <param name="serverTimeSec">The server time in seconds.</param>
63+
public void Reset(double localTimeSec, double serverTimeSec)
64+
{
65+
LocalTime = new NetworkTime(TickRate, localTimeSec);
66+
ServerTime = new NetworkTime(TickRate, serverTimeSec);
67+
}
68+
5869
/// <summary>
5970
/// Called after advancing the time system to run ticks based on the difference in time.
6071
/// </summary>

com.unity.netcode.gameobjects/Tests/Runtime/BaseMultiInstanceTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public virtual IEnumerator Teardown()
6060
/// We want to exclude the TestRunner scene on the host-server side so it won't try to tell clients to
6161
/// synchronize to this scene when they connect
6262
/// </summary>
63-
private bool VerifySceneIsValidForClientsToLoad(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
63+
private static bool VerifySceneIsValidForClientsToLoad(int sceneIndex, string sceneName, LoadSceneMode loadSceneMode)
6464
{
6565
// exclude test runner scene
6666
if (sceneName.StartsWith(k_FirstPartOfTestRunnerSceneName))
@@ -75,7 +75,7 @@ private bool VerifySceneIsValidForClientsToLoad(int sceneIndex, string sceneName
7575
/// clients to synchronize (i.e. load) the test runner scene. This will also register the test runner
7676
/// scene and its handle for both client(s) and server-host.
7777
/// </summary>
78-
private void SceneManagerValidationAndTestRunnerInitialization(NetworkManager networkManager)
78+
public static void SceneManagerValidationAndTestRunnerInitialization(NetworkManager networkManager)
7979
{
8080
// If VerifySceneBeforeLoading is not already set, then go ahead and set it so the host/server
8181
// will not try to synchronize clients to the TestRunner scene. We only need to do this for the server.
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using System.Collections;
2+
using NUnit.Framework;
3+
using UnityEngine;
4+
using UnityEngine.TestTools;
5+
6+
namespace Unity.Netcode.RuntimeTests
7+
{
8+
/// <summary>
9+
/// Tests that the time and tick system are initialized properly
10+
/// </summary>
11+
public class TimeInitializationTest
12+
{
13+
private int m_ClientTickCounter;
14+
private int m_ConnectedTick;
15+
private NetworkManager m_Client;
16+
17+
[UnityTest]
18+
public IEnumerator TestClientTimeInitializationOnConnect([Values(0, 1f)] float serverStartDelay, [Values(0, 1f)] float clientStartDelay, [Values(true, false)] bool isHost)
19+
{
20+
// Create multiple NetworkManager instances
21+
if (!MultiInstanceHelpers.Create(1, out NetworkManager server, out NetworkManager[] clients, 30))
22+
{
23+
Debug.LogError("Failed to create instances");
24+
Assert.Fail("Failed to create instances");
25+
}
26+
27+
yield return new WaitForSeconds(serverStartDelay);
28+
MultiInstanceHelpers.Start(false, server, new NetworkManager[] { }, BaseMultiInstanceTest.SceneManagerValidationAndTestRunnerInitialization); // passing no clients on purpose to start them manually later
29+
30+
// 0 ticks should have passed
31+
var serverTick = server.NetworkTickSystem.ServerTime.Tick;
32+
Assert.AreEqual(0, serverTick);
33+
34+
// server time should be 0
35+
Assert.AreEqual(0, server.NetworkTickSystem.ServerTime.Time);
36+
37+
// wait 2 frames to ensure network tick is run
38+
yield return null;
39+
yield return null;
40+
41+
var serverTimePassed = server.NetworkTickSystem.ServerTime.Time;
42+
var expectedServerTickCount = Mathf.FloorToInt((float)(serverTimePassed * 30));
43+
44+
var ticksPassed = server.NetworkTickSystem.ServerTime.Tick - serverTick;
45+
Assert.AreEqual(expectedServerTickCount, ticksPassed);
46+
47+
yield return new WaitForSeconds(clientStartDelay);
48+
49+
Assert.AreEqual(1, clients.Length);
50+
m_Client = clients[0];
51+
52+
Assert.Null(m_Client.NetworkTickSystem);
53+
54+
m_Client.OnClientConnectedCallback += ClientOnOnClientConnectedCallback;
55+
56+
var clientStartRealTime = Time.time;
57+
58+
m_Client.StartClient();
59+
BaseMultiInstanceTest.SceneManagerValidationAndTestRunnerInitialization(clients[0]);
60+
61+
m_Client.NetworkTickSystem.Tick += NetworkTickSystemOnTick;
62+
m_ClientTickCounter = 0;
63+
64+
65+
// don't check for anything here and assume non-async connection.
66+
67+
// Wait for connection on client side
68+
yield return MultiInstanceHelpers.Run(MultiInstanceHelpers.WaitForClientsConnected(clients));
69+
70+
var clientStartRealTimeDuration = Time.time - clientStartRealTime;
71+
var clientStartRealTickDuration = Mathf.FloorToInt(clientStartRealTimeDuration * 30);
72+
73+
// check tick is initialized with server value
74+
Assert.AreNotEqual(0, m_ConnectedTick);
75+
76+
Assert.True(m_ClientTickCounter <= clientStartRealTickDuration);
77+
78+
MultiInstanceHelpers.Destroy();
79+
yield return null;
80+
}
81+
82+
private void NetworkTickSystemOnTick()
83+
{
84+
Debug.Log(m_Client.NetworkTickSystem.ServerTime.Tick);
85+
m_ClientTickCounter++;
86+
}
87+
88+
private void ClientOnOnClientConnectedCallback(ulong id)
89+
{
90+
// client connected to server
91+
m_ConnectedTick = m_Client.NetworkTickSystem.ServerTime.Tick;
92+
Debug.Log($"Connected tick: {m_ConnectedTick}");
93+
}
94+
95+
[UnityTearDown]
96+
public virtual IEnumerator Teardown()
97+
{
98+
MultiInstanceHelpers.Destroy();
99+
yield return null;
100+
}
101+
}
102+
}

com.unity.netcode.gameobjects/Tests/Runtime/Timing/TimeInitializationTest.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)