Tools for testing

This commit is contained in:
2026-06-11 19:24:09 +02:00
parent 878486c92f
commit 2ccf77c0eb
6 changed files with 1590 additions and 376 deletions

View File

@@ -1,4 +1,6 @@
using System;
using Newtonsoft.Json.Linq;
using System;
using System.Numerics;
using System.Text;
using TMPro;
using UdonSharp;
@@ -52,9 +54,9 @@ namespace Marro.PacManUdon
/// </summary>
[SerializeField] private GameObject root;
/// <summary>
/// The delay at which the receiving side replays events.
/// The delay in ticks at which the receiving side replays events.
/// </summary>
[SerializeField] private float delay = 1f;
[SerializeField] private int delay = 50;
/// <summary>
/// The maximum amount of times a message is sent.
/// </summary>
@@ -62,11 +64,11 @@ namespace Marro.PacManUdon
/// <summary>
/// How long to wait since last message to send next ping.
/// </summary>
[SerializeField] private float pingDelay = 0.3f;
[SerializeField] private int pingDelay = 15;
/// <summary>
/// The time delta at which updates occur.
/// </summary>
[SerializeField] private float updateDelta = 0.0166666667f;
[SerializeField] private float tickDelta = 0.0166666667f;
#endregion
#region Constants
@@ -123,13 +125,14 @@ namespace Marro.PacManUdon
private SyncedObject[] syncedUpdateSubscribers;
/// <summary>
/// Offset from system time to network time, including delay.
/// The <see cref="Time.fixedTime"/> that corresponds to tick 0.
/// </summary>
private float offsetTime;
private float startTime;
/// <summary>
/// Time since last full sync, captured when this FixedUpdate started, with network delay applied.
/// Time in ticks since start of game.
/// </summary>
private float internalTime;
private int targetTicks;
/// <summary>
/// True if time is paused
@@ -142,13 +145,9 @@ namespace Marro.PacManUdon
private bool stepNext;
/// <summary>
/// Time at which the next update should occur.
/// Time at which next received event occured, in ticks.
/// </summary>
private float nextUpdateTime;
/// <summary>
/// Time at which next received event occured.
/// </summary>
private float nextEventTime;
private int nextEventTime;
/// <summary>
/// Amounot of retries in a row without a successful sync.
@@ -196,9 +195,9 @@ namespace Marro.PacManUdon
/// </summary>
private int eventTransmissionHistoryIndex;
/// <summary>
/// Time of last event transmission.
/// Time of last event transmission, in ticks.
/// </summary>
private float lastEventTransmissionTime;
private int lastEventTransmissionTime;
/// <summary>
@@ -251,14 +250,18 @@ namespace Marro.PacManUdon
private bool synced = false;
/// <summary>
/// The time since last full sync which is currently being simulated.
/// The time since start of game, in ticks.
/// </summary>
public int SyncedTimeTicks { get; private set; }
/// <summary>
/// The time since start of game which is currently being simulated.
/// </summary>
public float SyncedTime { get; private set; }
/// <summary>
/// Time since the last simulation, in seconds.
/// </summary>
public float SyncedDeltaTime { get; private set; }
public float SyncedDeltaTime => tickDelta;
/// <summary>
/// Is the current simulation to prepare for applying a network event?
@@ -355,11 +358,10 @@ namespace Marro.PacManUdon
retriesWithoutSuccess = 0;
hasFullSyncReady = false;
offsetTime = Time.fixedTime;
internalTime = 0;
targetTicks = 0;
startTime = Time.fixedTime;
SyncedTime = 0;
SyncedDeltaTime = Time.fixedDeltaTime;
nextUpdateTime = SyncedTime;
SyncedTimeTicks = 0;
Ready = true;
@@ -373,11 +375,16 @@ namespace Marro.PacManUdon
RequestEvent(NetworkEventType.FullSync);
}
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Initialized, time offset: {offsetTime}");
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Initialized");
}
public void Update()
{
if (debugOutput != null)
{
WriteDebugOutput(debugOutput);
}
if (!initialized)
{
return;
@@ -387,35 +394,12 @@ namespace Marro.PacManUdon
UpdateInternalTime();
// Forwards simulated time by updateDelta until we're caught up
while (nextUpdateTime <= internalTime)
while (SyncedTimeTicks <= targetTicks)
{
PerformFixedSyncedUpdate();
}
}
private void UpdateInternalTime()
{
var delta = Time.fixedTime - offsetTime - internalTime;
if (!paused)
{
// Continue time like normal
internalTime += delta;
}
else if (paused && !stepNext)
{
// Since we're paused, increase our offset from Unity's time
offsetTime += delta;
}
else
{
// Step forward by exactly updateDelta, apply the remainder to our offset
offsetTime += delta - updateDelta;
internalTime += updateDelta;
stepNext = false;
}
}
private void PerformFixedSyncedUpdate()
{
if (Ready)
@@ -431,9 +415,8 @@ namespace Marro.PacManUdon
}
}
ProgressSyncedTime(nextUpdateTime);
ProgressSyncedTime();
CallSyncedUpdate();
nextUpdateTime = SyncedTime + updateDelta;
}
private void CallSyncedUpdate()
@@ -555,7 +538,7 @@ namespace Marro.PacManUdon
return;
}
var timestamp = SyncedTime;
var timestamp = SyncedTimeTicks;
var eventId = GetNextEventId(lastEventId);
@@ -593,7 +576,7 @@ namespace Marro.PacManUdon
retriesWithoutSuccess = 0; // We had success!
}
private static void InitializeEvent(NetworkEventType eventType, float timestamp, byte eventId, out byte[] data, out int index)
private static void InitializeEvent(NetworkEventType eventType, int timestamp, byte eventId, out byte[] data, out int index)
{
data = new byte[MaxEventSize];
@@ -640,7 +623,7 @@ namespace Marro.PacManUdon
private void ProgressPingTime()
{
if (eventsQueueIndex > 0 && !serializationRequested
&& internalTime - lastEventTransmissionTime >= pingDelay)
&& targetTicks - lastEventTransmissionTime >= pingDelay)
{
RequestSerializationForEvents();
}
@@ -695,7 +678,7 @@ namespace Marro.PacManUdon
// If there was a full sync in the queue, it has now been transmitted at least once
hasFullSyncReady = false;
lastEventTransmissionTime = internalTime;
lastEventTransmissionTime = targetTicks;
}
#endregion
@@ -811,7 +794,7 @@ namespace Marro.PacManUdon
QueueEventInBuffer(@event);
// Set this event to play after the default delay
nextEventTime = internalTime + delay;
nextEventTime = targetTicks + delay;
hasFullSyncReady = true;
@@ -822,7 +805,7 @@ namespace Marro.PacManUdon
{
IsEventUpdate = true;
while (eventsQueueIndex > 0 && nextEventTime <= SyncedTime)
while (eventsQueueIndex > 0 && nextEventTime <= SyncedTimeTicks)
{
var success = ApplyEvent(eventsQueue[0]);
@@ -847,11 +830,16 @@ namespace Marro.PacManUdon
{
SyncToTimestamp(timestamp);
}
else if (timestamp < SyncedTimeTicks)
{
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Next received event is in the past! " +
$"{nameof(nextEventTime)}: {nextEventTime} < {nameof(SyncedTimeTicks)}: {SyncedTimeTicks}.");
HandleError(true);
return false;
}
var index = (int)HeaderLength; // Skip header
ProgressSyncedTime(timestamp);
var subscribers = GetEventSubscribers(eventType);
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) ApplyEvent with dt {SyncedDeltaTime}");
@@ -890,7 +878,7 @@ namespace Marro.PacManUdon
Synced = true;
}
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Performed incoming eventof type {eventType}! Total {index} bytes.");
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Performed incoming event of type {eventType}! Total {index} bytes.");
retriesWithoutSuccess = 0; // We had success!
@@ -951,30 +939,38 @@ namespace Marro.PacManUdon
#endregion
#region Time
private void ProgressSyncedTime(float newTime)
private void UpdateInternalTime()
{
//Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) updating SyncedTime from {SyncedTime} to {newTime}");
SyncedDeltaTime = newTime - SyncedTime;
if (SyncedDeltaTime < 0)
if (paused && !stepNext)
{
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Negative Dt: {SyncedDeltaTime}! Going from {SyncedTime} to {newTime}, IsEventUpdate: {IsEventUpdate}");
return;
}
SyncedTime = newTime;
if (paused && stepNext)
{
targetTicks++;
stepNext = false;
return;
}
targetTicks = (int)((Time.fixedTime - startTime) / tickDelta);
}
private void SyncToTimestamp(float timestamp)
private void ProgressSyncedTime()
{
var oldOffset = offsetTime;
offsetTime = Time.fixedTime - timestamp;
SyncedTimeTicks++;
SyncedTime = SyncedTimeTicks * tickDelta;
}
var delta = offsetTime - oldOffset;
internalTime -= delta;
SyncedTime -= delta;
nextEventTime -= delta;
private void SyncToTimestamp(int timestamp)
{
startTime = Time.fixedTime - timestamp * tickDelta;
targetTicks = timestamp;
SyncedTimeTicks = timestamp;
SyncedTime = SyncedTimeTicks * tickDelta;
nextEventTime = timestamp;
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Synced to timestamp {timestamp}, current time is {Time.fixedTime}, offsetTime is now {offsetTime}, internalTime is now {internalTime}, SyncedTime is now {SyncedTime}, nextEventTime is now {nextEventTime}");
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Synced to timestamp {timestamp}, internalTime is now {targetTicks}, SyncedTime is now {SyncedTime}, nextEventTime is now {nextEventTime}");
}
private void UpdateNextEventTime()
@@ -993,9 +989,9 @@ namespace Marro.PacManUdon
return;
}
if (nextEventTime < SyncedTime)
if (nextEventTime < SyncedTimeTicks)
{
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) New event timestamp is earlier than our current synced time by {SyncedTime - nextEventTime} seconds! nextEventTime: {nextEventTime} SyncedTime: {SyncedTime}, internalTime: {internalTime}");
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) New event timestamp is earlier than our current synced time by {SyncedTime - nextEventTime} seconds! nextEventTime: {nextEventTime} SyncedTime: {SyncedTime}, internalTime: {targetTicks}");
HandleError(true);
return;
}
@@ -1026,8 +1022,8 @@ namespace Marro.PacManUdon
private static NetworkEventType GetEventTypeFromHeader(byte[] @event, int eventIndex = 0) =>
(NetworkEventType)@event[eventIndex + HeaderEventTypeIndex];
private static float GetTimestampFromHeader(byte[] @event, int eventIndex = 0) =>
BitConverter.ToSingle(@event, eventIndex + HeaderTimestampIndex);
private static int GetTimestampFromHeader(byte[] @event, int eventIndex = 0) =>
BitConverter.ToInt32(@event, eventIndex + HeaderTimestampIndex);
private static byte GetEventIdFromHeader(byte[] @event, int eventIndex = 0) =>
@event[eventIndex + HeaderEventIdIndex];
@@ -1107,6 +1103,11 @@ namespace Marro.PacManUdon
Array.Copy(data, start, result, 0, length);
return result;
}
private static float RoundDown(float value, float precision)
{
return (float)(Math.Floor(value / precision) * precision);
}
#endregion
#region SyncedData
@@ -1137,20 +1138,20 @@ namespace Marro.PacManUdon
#region Debug
public void SimulateSyncToTimestamp(float timestamp)
{
SyncToTimestamp(timestamp);
//SyncToTimestamp(timestamp);
}
public void WriteDebugOutput(TMP_InputField debugOutput)
{
debugOutput.text += $"{nameof(NetworkManager)}:\n" +
debugOutput.text = $"{nameof(NetworkManager)}:\n" +
$"IsOwner: {IsOwner}\n" +
$"Ready: {Ready}\n" +
$"Synced: {Synced}\n" +
$"hasFullSyncReady: {hasFullSyncReady}\n" +
$"lastEventId: {lastEventId}" +
$"lastEventId: {lastEventId}\n" +
$"Time.fixedTime: {Time.fixedTime}\n" +
$"offsetTime: {offsetTime}\n" +
$"internalTime: {internalTime}\n" +
$"startTime: {startTime}\n" +
$"targetTicks: {targetTicks}\n" +
$"SyncedTime: {SyncedTime}\n" +
$"Dt: {SyncedDeltaTime}\n" +
$"BufferIndex: {eventsQueueIndex}\n" +
@@ -1158,6 +1159,11 @@ namespace Marro.PacManUdon
$"\n";
}
/// <summary>
/// Text field to display debug info in.
/// </summary>
[SerializeField] private TMP_InputField debugOutput;
/// <summary>
/// An animator which visualizes whether the current perspective is the owner.
/// </summary>