A lot of older work

This commit is contained in:
2026-06-09 17:45:51 +02:00
parent a8b395b1d3
commit cf39fd5dd9
8 changed files with 3760 additions and 1710 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -394,21 +394,47 @@ namespace Marro.PacManUdon
}
}
public override void CollectSyncedData(byte[] data, ref int offset, NetworkEventType eventType)
public override void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{
if (eventType == NetworkEventType.TimeSequenceSync)
{
data.Append(currentlyInTimeSequence, ref index);
if (currentlyInTimeSequence)
{
data.AppendAsByte((int)currentTimeSequence, ref index);
data.Append(timeSequenceSecondsPassed, ref index);
}
}
//data[offset++] = new byte[] { NetworkManager.Int32ToByte((int)gameState) };
//data[offset++] = BitConverter.GetBytes(currentlyInTimeSequence);
//data[offset++] = new byte[] { NetworkManager.Int32ToByte((int)currentTimeSequence) };
//data[offset++] = BitConverter.GetBytes(timeSequenceSecondsPassed);
}
public override bool WriteSyncedData(byte[] data, ref int offset, NetworkEventType eventType)
public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{
if (eventType == NetworkEventType.StartGameButtonPressed)
{
StartGameButtonPressed();
}
if (eventType == NetworkEventType.TimeSequenceSync)
{
var currentlyInTimeSequence = data.ReadBool(ref index);
if (!currentlyInTimeSequence)
{
TimeSequenceTryEndCurrent();
}
else
{
var currentTimeSequence = (PacManTimeSequence)data.ReadByte(ref index);
var timeSequenceSecondsPassed = data.ReadFloat(ref index);
TimeSequenceSyncWithRemote(currentTimeSequence, timeSequenceSecondsPassed);
}
}
//SetGameState((PacManGameState)data[offset++]);
//var currentlyInTimeSequence = BitConverter.ToBoolean(data, offset++);

View File

@@ -193,6 +193,7 @@ namespace Marro.PacManUdon
}
// Debug.Log($"{gameObject} GhostCaughtQueue with ghost {ghost}");
//networkManager.SendEventSoon(NetworkEventType.TimeSequenceSync);
//networkManager.SendEventSoon(NetworkEventType.GhostUpdate);
ghostScaredQueue.Add(ghost);

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ using System.Text;
using TMPro;
using UdonSharp;
using UnityEngine;
using UnityEngine.Android;
using VRC.SDK3.UdonNetworkCalling;
using VRC.SDKBase;
using VRC.Udon.Common;
@@ -17,9 +18,13 @@ namespace Marro.PacManUdon
StartGameButtonPressed = 3,
SyncPellets = 4,
GhostUpdate = 5,
TimeSequenceSync = 6,
Pause = 7,
Resume = 8,
Step = 9,
}
public class NetworkManager : UdonSharpBehaviour
public class NetworkManager : SyncedObject
{
// The network manager works by serializing event and state data into a byte array, including a timestamp for each event.
// If user is owner, this data is created and stored in a buffer which is cleared upon transmission.
@@ -42,6 +47,11 @@ namespace Marro.PacManUdon
// [7]: (byte) Type of event. 0 = Full Sync, which is used to sync up from an undefinted state.
// [+]: Event-specific data.
#region External settings
[SerializeField] private SyncedObject[] networkEventSubscribersFlat;
[SerializeField] private int[] networkEventSubscribersFlatSegmentLengths;
#endregion
#region Settings
/// <summary>
/// The root from which this <see cref="NetworkManager"/> will look for <see cref="SyncedObject"/> to control.
@@ -60,9 +70,9 @@ namespace Marro.PacManUdon
/// </summary>
[SerializeField] private float pingDelay = 0.3f;
/// <summary>
/// The rate at which updates occur.
/// The time delta at which updates occur.
/// </summary>
[SerializeField] private float updateRate = 0.0166666667f;
[SerializeField] private float updateDelta = 0.0166666667f;
#endregion
#region Constants
@@ -99,9 +109,19 @@ namespace Marro.PacManUdon
#region Private attributes
/// <summary>
/// Objects which are controlled by this <see cref="NetworkManager"/>.
/// Whether <see cref="Initialize"/> has been called successfully.
/// </summary>
private SyncedObject[] syncedObjects;
private bool initialized = false;
/// <summary>
/// Subscribers to each <see cref="NetworkEventType"/>.
/// </summary>
private SyncedObject[][] networkEventSubscribers;
/// <summary>
/// Subscribers for <see cref="SyncedObject.SyncedUpdate"/>.
/// </summary>
private SyncedObject[] syncedUpdateSubscribers;
/// <summary>
/// Offset from system time to network time, including delay.
@@ -113,7 +133,17 @@ namespace Marro.PacManUdon
private float internalTime;
/// <summary>
/// Time at which the latest update occured
/// True if time is paused
/// </summary>
private bool paused;
/// <summary>
/// True if a step should happen next update
/// </summary>
private bool stepNext;
/// <summary>
/// Time at which the next update should occur.
/// </summary>
private float nextUpdateTime;
/// <summary>
@@ -191,10 +221,23 @@ namespace Marro.PacManUdon
/// </summary>
public bool Ready { get; private set; } = false;
/// <summary>
/// Whether the current perspective is synced with the owner. (Always true if current perspective is owner.)
/// </summary>
public bool Synced { get; private set; } = false;
public bool Synced
{
get => synced;
private set
{
synced = value;
if (DebugImageToIndicateSynced != null)
{
DebugImageToIndicateSynced.SetFloat("Color", value ? 1 : 0);
}
}
}
private bool synced = false;
/// <summary>
/// The time since last full sync which is currently being simulated.
@@ -216,16 +259,36 @@ namespace Marro.PacManUdon
/// <summary>
/// Is the local user owner?
/// </summary>
public bool IsOwner { get; private set; }
public bool IsOwner
{
get => isOwner;
private set
{
isOwner = value;
if (DebugImageToIndicateOwner != null)
{
DebugImageToIndicateOwner.SetFloat("Color", value ? 1 : 0);
}
}
}
private bool isOwner = false;
#endregion
#region General
/// <summary>
/// Initializes the <see cref="NetworkManager"/>. Call <see cref="Reset"/> afterwards to activate networking.
/// </summary>
public void Initialize()
{
if (initialized)
{
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Tried to call {nameof(Initialize)} when already initialized!");
return;
}
if (!BitConverter.IsLittleEndian)
{
Debug.LogError($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Fatal: NetworkManager only supports little endian! Network sync will not be possible.");
Ready = false;
return;
}
@@ -234,15 +297,75 @@ namespace Marro.PacManUdon
root = transform.parent.gameObject;
}
syncedObjects = root.GetComponentsInChildren<SyncedObject>(includeInactive: true);
foreach (var obj in syncedObjects)
if (!TryGetNetworkEventSubscribers(out networkEventSubscribers))
{
Debug.LogError($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Fatal: Invalid NetworkEventSubscribers configuration! Network sync will not be possible.");
return;
}
syncedUpdateSubscribers = root.GetComponentsInChildren<SyncedObject>(includeInactive: true);
AssignNetworkManagerReferencesToSubscribers();
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Found {syncedUpdateSubscribers.Length} {nameof(SyncedObject)} in children of {root.name}.");
initialized = true;
}
private bool TryGetNetworkEventSubscribers(out SyncedObject[][] result)
{
result = new SyncedObject[0][];
return true;
var values = networkEventSubscribersFlat;
var lengths = networkEventSubscribersFlatSegmentLengths;
result = new SyncedObject[lengths.Length][];
var index = 0;
foreach (var length in lengths)
{
if (index + length >= values.Length)
{
Debug.LogError($"({nameof(PacManUdon)} {nameof(NetworkManager)}) {nameof(TryGetNetworkEventSubscribers)}: Lengths sum is larger than values.");
return false;
}
result[index] = new SyncedObject[length];
Array.Copy(values, index, result, 0, length);
index += length;
}
if (index != values.Length)
{
Debug.LogError($"({nameof(PacManUdon)} {nameof(NetworkManager)}) {nameof(TryGetNetworkEventSubscribers)}: Lengths sum is smaller than values.");
}
return true;
}
private void AssignNetworkManagerReferencesToSubscribers()
{
// This results in a lot of duplicated assignments, but the alternative is deduplicating and unfortunately Udon does not have a good method for this.
foreach (var obj in syncedUpdateSubscribers)
{
obj.networkManager = this;
}
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Found {syncedObjects.Length} {nameof(SyncedObject)} in children of {root.name}.");
foreach (var obj in networkEventSubscribersFlat)
{
obj.networkManager = this;
}
}
SetOwner(Networking.IsOwner(gameObject));
public void Reset()
{
if (!initialized)
{
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Tried to call {nameof(Reset)} while not initialized. Call {nameof(Initialize)} first.");
return;
}
IsOwner = Networking.IsOwner(gameObject);
ClearBuffer();
@@ -256,9 +379,14 @@ namespace Marro.PacManUdon
SyncedDeltaTime = Time.fixedDeltaTime;
nextUpdateTime = SyncedTime;
if (!Synced)
// Sync up
if (IsOwner)
{
RequestEvent(NetworkEventType.FullSync); // See if we can sync up
SendEventSoon(NetworkEventType.FullSyncForced);
}
else
{
RequestEvent(NetworkEventType.FullSync);
}
Ready = true;
@@ -268,12 +396,12 @@ namespace Marro.PacManUdon
public void Update()
{
if (!Ready)
if (!initialized)
{
return;
}
// Fetch the current time
// Get our target time
UpdateInternalTime();
if (Ready)
@@ -289,27 +417,45 @@ namespace Marro.PacManUdon
}
}
// Forwards simulated time at the updateRate pace
// Forwards simulated time by updateDelta until we're caught up
while (nextUpdateTime <= internalTime)
{
ProgressSyncedTime(nextUpdateTime);
PerformFixedSyncedUpdate();
nextUpdateTime = SyncedTime + updateRate;
nextUpdateTime = SyncedTime + updateDelta;
}
}
private void UpdateInternalTime()
{
internalTime = Time.fixedTime - offsetTime;
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()
{
IsEventUpdate = false;
for (int i = 0; i < syncedObjects.Length; i++)
for (int i = 0; i < syncedUpdateSubscribers.Length; i++)
{
var obj = syncedObjects[i];
var obj = syncedUpdateSubscribers[i];
if (obj.gameObject.activeInHierarchy)
{
@@ -348,15 +494,9 @@ namespace Marro.PacManUdon
}
}
private void SetOwner(bool isOwner)
{
IsOwner = isOwner;
if (DebugImageToIndicateOwner != null)
{
DebugImageToIndicateOwner.SetFloat("Color", isOwner ? 1 : 0);
}
}
private SyncedObject[] GetEventSubscribers(NetworkEventType eventType) =>
//networkEventSubscribers[(int)eventType];
syncedUpdateSubscribers;
#endregion
#region Sender
@@ -412,7 +552,9 @@ namespace Marro.PacManUdon
InitializeEvent(eventType, timestamp, eventId, out byte[] data, out var index);
foreach (var obj in syncedObjects)
var subscibers = GetEventSubscribers(eventType);
foreach (var obj in subscibers)
{
obj.CollectSyncedData(data, ref index, eventType);
}
@@ -686,12 +828,14 @@ namespace Marro.PacManUdon
ProgressSyncedTime(timestamp);
foreach (var obj in syncedObjects)
var subscibers = GetEventSubscribers(eventType);
foreach (var obj in subscibers)
{
obj.SyncedUpdate();
}
foreach (var obj in syncedObjects)
foreach (var obj in subscibers)
{
var success = obj.WriteSyncedData(@event, ref index, eventType);
@@ -869,7 +1013,7 @@ namespace Marro.PacManUdon
}
bool newOwnerIsLocalPlayer = newOwner == Networking.LocalPlayer;
SetOwner(newOwnerIsLocalPlayer);
IsOwner = newOwnerIsLocalPlayer;
if (newOwnerIsLocalPlayer)
{
@@ -936,6 +1080,31 @@ namespace Marro.PacManUdon
}
#endregion
#region SyncedData
public override void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{
// Nothing
}
public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{
switch (eventType)
{
case NetworkEventType.Pause:
Pause();
break;
case NetworkEventType.Resume:
Resume();
break;
case NetworkEventType.Step:
Step();
break;
}
return true;
}
#endregion
#region Debug
public void SimulateSyncToTimestamp(float timestamp)
{
@@ -965,6 +1134,11 @@ namespace Marro.PacManUdon
/// </summary>
[SerializeField] private Animator DebugImageToIndicateOwner;
/// <summary>
/// An animator which visualizes whether the NetworkManager is synced.
/// </summary>
[SerializeField] private Animator DebugImageToIndicateSynced;
public void DoFullSync()
{
SendEventSoon(NetworkEventType.FullSync);
@@ -979,6 +1153,47 @@ namespace Marro.PacManUdon
{
SendEventSoon(NetworkEventType.GhostUpdate);
}
public void DoTimeSequenceSync()
{
SendEventSoon(NetworkEventType.TimeSequenceSync);
}
private void Pause()
{
paused = true;
stepNext = false;
}
private void Resume()
{
paused = false;
stepNext = false;
}
private void Step()
{
paused = true;
stepNext = true;
}
public void PauseButtonPressed()
{
Pause();
SendEventSoon(NetworkEventType.Pause);
}
public void ResumeButtonPressed()
{
Resume();
SendEventSoon(NetworkEventType.Resume);
}
public void StepButtonPressed()
{
Step();
SendEventSoon(NetworkEventType.Step);
}
#endregion
}
}

View File

@@ -151,8 +151,12 @@ namespace Marro.PacManUdon
SetDirection(inputDirection + new Vector2(GridMoverTools.PositionToGrid(nextPosition).x - nextPosition.x, 0).normalized);
}
SetTargetDirection(inputDirection); // This is the direction most logic should assume pacman is moving, the actual direction may be different due to cornering
if (!followingPredefinedPath)
{
networkManager.SendEventSoon(NetworkEventType.PacManTurn);
}
}
return nextPosition;
}

View File

@@ -20,10 +20,7 @@ namespace Marro.PacManUdon
{
Debug.Log($"StartTimeSequence: {timeSequence}");
if (currentlyInTimeSequence)
{
TimeSequenceEndCurrent();
}
TimeSequenceTryEndCurrent();
TimeSequencePrepareForStart(timeSequence);
@@ -35,12 +32,24 @@ namespace Marro.PacManUdon
TimeSequenceProgressToTime(timeSequenceSecondsPassed);
}
private void TimeSequenceEndCurrent()
private void TimeSequenceTryEndCurrent()
{
if (!currentlyInTimeSequence)
{
return;
}
Debug.Log($"{gameObject} TimeSequenceEndCurrent");
jumpingToTimeSequence = true;
TimeSequenceProgressToTime(100000f);
Debug.LogWarning($"{gameObject} TimeSequenceEndCurrent");
TryFinalizeTimeSequence();
if (waitingForTimeSequenceFinalize)
{
TimeSequenceExecuteFinalize(currentTimeSequence);
}
jumpingToTimeSequence = false;
}
@@ -79,60 +88,39 @@ namespace Marro.PacManUdon
if (timeSequenceProgress >= timeSequenceKeyframeTimes.Length)
{
currentlyInTimeSequence = false;
TimeSequencePrepareForFinish(currentTimeSequence);
TimeSequenceFinish(currentTimeSequence);
break;
}
}
}
private void TimeSequencePrepareForFinish(PacManTimeSequence timeSequence)
private void TimeSequenceFinish(PacManTimeSequence timeSequence)
{
//if (networkManager.IsOwner)
//{
Debug.LogWarning($"{gameObject} TimeSequencePrepareForFinish");
TimeSequenceExecuteFinalize(timeSequence);
networkManager.SendEventSoon(NetworkEventType.TimeSequenceSync);
if (!jumpingToTimeSequence)
{
TimeSequenceExecuteFinished(timeSequence);
}
//}
//else
//{
// waitingForTimeSequenceFinalize = true;
//}
}
private void TryFinalizeTimeSequence()
private void TimeSequenceSyncWithRemote(PacManTimeSequence currentTimeSequence, float timeSequenceSecondsPassed)
{
if (!waitingForTimeSequenceFinalize)
// If the remote is in a time sequence but we're not, or we're in a different time sequence, or if we're behind,
// jump to the remote's time sequence.
if (!currentlyInTimeSequence
|| currentTimeSequence != this.currentTimeSequence
|| timeSequenceSecondsPassed < this.timeSequenceSecondsPassed)
{
return;
StartTimeSequence(currentTimeSequence);
}
TimeSequenceExecuteFinalize(currentTimeSequence);
waitingForTimeSequenceFinalize = false;
// If we're (now) in a time sequence, jump our progress to match the one on the remote
if (currentlyInTimeSequence)
{
TimeSequenceProgressToTime(timeSequenceSecondsPassed);
}
}
//private void TimeSequenceSyncWithRemote(bool currentlyInTimeSequence, PacManTimeSequence currentTimeSequence, float timeSequenceSecondsPassed)
//{
// // If the remote is in a time sequence but we're not, or we're in a different time sequence, jump to the remote's time sequence.
// if (currentlyInTimeSequence && (!this.currentlyInTimeSequence || currentTimeSequence != this.currentTimeSequence))
// {
// StartTimeSequence(currentTimeSequence);
// }
// // If we're (now) in a time sequence, jump our progress to match the one on the remote
// if (this.currentlyInTimeSequence)
// {
// TimeSequenceProgressToTime(timeSequenceSecondsPassed);
// }
// // If the remote has finished it's time sequence and we have one waiting to be finalized, we can do so now
// if (!currentlyInTimeSequence)
// {
// TryFinalizeTimeSequence();
// }
//}
#region Events