Compare commits

...

6 Commits

Author SHA1 Message Date
878486c92f Small changes 2026-06-11 13:38:01 +02:00
bce6b329c2 Improved network accuracy 2026-06-11 12:25:12 +02:00
f0859d92ac Implemented event subscribers 2026-06-11 12:05:38 +02:00
e75452b145 Fixes 2026-06-11 12:05:12 +02:00
e7968a5753 NetworkManagerTester working 2026-06-10 20:09:41 +02:00
cf39fd5dd9 A lot of older work 2026-06-09 17:45:51 +02:00
22 changed files with 69162 additions and 60671 deletions

61241
Assets/PacManGame.prefab Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 15ac0ed4c56c7784ea3ae9000fc2af1f
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -67,6 +67,9 @@ namespace Marro.PacManUdon
soundManager.Initialize(); soundManager.Initialize();
intermission2Pole.Initialize(this, ghostManager.Ghosts[0]); intermission2Pole.Initialize(this, ghostManager.Ghosts[0]);
SubscribeToEvent(NetworkEventType.StartGameButtonPressed);
SubscribeToEvent(NetworkEventType.TimeSequenceSync);
HideEverything(); HideEverything();
SetScore(0); SetScore(0);
@@ -123,7 +126,7 @@ namespace Marro.PacManUdon
private void InitializeNewGame() private void InitializeNewGame()
{ {
Debug.Log($"{gameObject} Started new game!"); //Debug.Log($"{gameObject} Started new game!");
SetScore(0); SetScore(0);
SetExtraLives(startingExtraLives); SetExtraLives(startingExtraLives);
SetLevel(1); SetLevel(1);
@@ -131,7 +134,7 @@ namespace Marro.PacManUdon
private void InitializeLevel() private void InitializeLevel()
{ {
Debug.Log($"{gameObject} New level started!"); //Debug.Log($"{gameObject} New level started!");
pelletManager.RestoreAllPellets(); pelletManager.RestoreAllPellets();
@@ -142,7 +145,7 @@ namespace Marro.PacManUdon
private void RestartLevel(bool afterLifeLost = false) private void RestartLevel(bool afterLifeLost = false)
{ {
Debug.Log($"{gameObject} (Re)started level!"); //Debug.Log($"{gameObject} (Re)started level!");
// SetInGameComponentVisibility(true); // SetInGameComponentVisibility(true);
@@ -186,7 +189,7 @@ namespace Marro.PacManUdon
public void GotPowerPellet(Pellet pellet) public void GotPowerPellet(Pellet pellet)
{ {
Debug.Log($"{gameObject} GotPowerPellet"); //Debug.Log($"{gameObject} GotPowerPellet");
if (gameState == PacManGameState.AttractMode) if (gameState == PacManGameState.AttractMode)
{ {
@@ -215,7 +218,7 @@ namespace Marro.PacManUdon
public void GhostCaught(int scoreBonus) public void GhostCaught(int scoreBonus)
{ {
Debug.Log($"{gameObject} GhostCaught"); //Debug.Log($"{gameObject} GhostCaught");
if (gameState == PacManGameState.AttractMode) if (gameState == PacManGameState.AttractMode)
{ {
@@ -246,7 +249,7 @@ namespace Marro.PacManUdon
public void Intermission2PoleUpdate() public void Intermission2PoleUpdate()
{ {
Debug.Log($"{gameObject} Intermission2PoleUpdate"); //Debug.Log($"{gameObject} Intermission2PoleUpdate");
TimeSequenceSkipToNextStep(); TimeSequenceSkipToNextStep();
} }
@@ -394,21 +397,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++] = new byte[] { NetworkManager.Int32ToByte((int)gameState) };
//data[offset++] = BitConverter.GetBytes(currentlyInTimeSequence); //data[offset++] = BitConverter.GetBytes(currentlyInTimeSequence);
//data[offset++] = new byte[] { NetworkManager.Int32ToByte((int)currentTimeSequence) }; //data[offset++] = new byte[] { NetworkManager.Int32ToByte((int)currentTimeSequence) };
//data[offset++] = BitConverter.GetBytes(timeSequenceSecondsPassed); //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) if (eventType == NetworkEventType.StartGameButtonPressed)
{ {
StartGameButtonPressed(); StartGameButtonPressed();
} }
if (eventType == NetworkEventType.TimeSequenceSync)
{
var currentlyInTimeSequence = data.ReadBool(ref index);
if (currentlyInTimeSequence)
{
var currentTimeSequence = (PacManTimeSequence)data.ReadByte(ref index);
var timeSequenceSecondsPassed = data.ReadFloat(ref index);
TimeSequenceSyncWithRemote(currentTimeSequence, timeSequenceSecondsPassed);
}
else
{
TimeSequenceTryEndCurrent();
}
}
//SetGameState((PacManGameState)data[offset++]); //SetGameState((PacManGameState)data[offset++]);
//var currentlyInTimeSequence = BitConverter.ToBoolean(data, offset++); //var currentlyInTimeSequence = BitConverter.ToBoolean(data, offset++);

View File

@@ -109,6 +109,8 @@ namespace Marro.PacManUdon
frozenState = PacManGhostFrozenState.Frozen; frozenState = PacManGhostFrozenState.Frozen;
Index = index; Index = index;
SubscribeToEvent(NetworkEventType.GhostUpdate);
} }
public void Reset() public void Reset()
@@ -163,13 +165,13 @@ namespace Marro.PacManUdon
SetPosition(nextPosition); SetPosition(nextPosition);
} }
private Vector2 ProcessNextPosition(Vector2 position, Vector2 nextPosition) private Vector2 ProcessNextPosition(Vector2 position, Vector2 nextPosition)
{ {
if (turnAroundSoon && ghostState == PacManGhostState.Normal if (turnAroundSoon && ghostState == PacManGhostState.Normal
&& GridMoverTools.CrossesTileBorder(position, nextPosition, direction.x != 0, direction.y != 0)) && GridMoverTools.CrossesTileBorder(position, nextPosition, direction.x != 0, direction.y != 0))
{ {
SetDirection(direction * -1); SetDirection(direction * -1);
Debug.Log($"{gameObject} turned around to direction {GetDirection()}"); //Debug.Log($"{gameObject} turned around to direction {GetDirection()}");
turnAroundSoon = false; turnAroundSoon = false;
return nextPosition; return nextPosition;
} }
@@ -816,7 +818,6 @@ namespace Marro.PacManUdon
inTunnel = data.ReadBool(ref index); inTunnel = data.ReadBool(ref index);
rngState = data.ReadInt(ref index); rngState = data.ReadInt(ref index);
turnAroundSoon = data.ReadBool(ref index); turnAroundSoon = data.ReadBool(ref index);
Debug.Log($"{gameObject} turnAroundSoon = {turnAroundSoon}");
speed = data.ReadFloat(ref index); speed = data.ReadFloat(ref index);
ghostState = (PacManGhostState)data.ReadByte(ref index); ghostState = (PacManGhostState)data.ReadByte(ref index);

View File

@@ -74,6 +74,8 @@ namespace Marro.PacManUdon
ghosts[ghostIndex].Initialize(pacMan, blinky, startTransform, homePosition, idlePosition1, idlePosition2, cornerPosition, ghostIndex); ghosts[ghostIndex].Initialize(pacMan, blinky, startTransform, homePosition, idlePosition1, idlePosition2, cornerPosition, ghostIndex);
} }
SubscribeToEvent(NetworkEventType.GhostUpdate);
} }
public void RestartLevel(bool afterLifeLost = false) public void RestartLevel(bool afterLifeLost = false)
@@ -193,6 +195,7 @@ namespace Marro.PacManUdon
} }
// Debug.Log($"{gameObject} GhostCaughtQueue with ghost {ghost}"); // Debug.Log($"{gameObject} GhostCaughtQueue with ghost {ghost}");
//networkManager.SendEventSoon(NetworkEventType.TimeSequenceSync);
//networkManager.SendEventSoon(NetworkEventType.GhostUpdate); //networkManager.SendEventSoon(NetworkEventType.GhostUpdate);
ghostScaredQueue.Add(ghost); ghostScaredQueue.Add(ghost);
@@ -315,7 +318,7 @@ namespace Marro.PacManUdon
public void SetLevel(int level) public void SetLevel(int level)
{ {
Debug.Log($"GhostManager: SetLevel {level}"); //Debug.Log($"GhostManager: SetLevel {level}");
SetLevelConstants(level); SetLevelConstants(level);
int[] privatePelletCounterReleaseValues = PacManConstants.GetGhostHousePrivatePelletCounterLimitForLevel(level); int[] privatePelletCounterReleaseValues = PacManConstants.GetGhostHousePrivatePelletCounterLimitForLevel(level);
@@ -376,7 +379,7 @@ namespace Marro.PacManUdon
void SetScattering(bool scattering, bool reverseDirection = true) void SetScattering(bool scattering, bool reverseDirection = true)
{ {
Debug.Log($"{gameObject} SetScattering: {scattering}"); //Debug.Log($"{gameObject} SetScattering: {scattering}");
foreach (Ghost ghost in ghosts) foreach (Ghost ghost in ghosts)
{ {
if (ghost == blinky && elroyLevel > 0) // Once blinky is elroy he no longer scatters if (ghost == blinky && elroyLevel > 0) // Once blinky is elroy he no longer scatters
@@ -393,7 +396,7 @@ namespace Marro.PacManUdon
/// </summary> /// </summary>
void SetSharedPelletCounterActive(bool active) void SetSharedPelletCounterActive(bool active)
{ {
Debug.Log($"{gameObject} SetSharedPelletCounterActive {active}"); //Debug.Log($"{gameObject} SetSharedPelletCounterActive {active}");
sharedPelletCounterActive = active; sharedPelletCounterActive = active;
foreach (Ghost ghost in ghosts) foreach (Ghost ghost in ghosts)
{ {
@@ -528,7 +531,7 @@ namespace Marro.PacManUdon
} }
ghostScaredQueueArray[i] = (byte)((Ghost)ghost.Reference).Index; ghostScaredQueueArray[i] = (byte)((Ghost)ghost.Reference).Index;
} }
Debug.Log($"{gameObject} Sent a ghostScareQueue of length {ghostScaredQueue.Count}"); //Debug.Log($"{gameObject} Sent a ghostScareQueue of length {ghostScaredQueue.Count}");
data.Append(ghostScaredQueueArray, ref index); data.Append(ghostScaredQueueArray, ref index);
} }
@@ -578,7 +581,7 @@ namespace Marro.PacManUdon
ghostScaredQueue.Add(ghosts[ghostIndex]); ghostScaredQueue.Add(ghosts[ghostIndex]);
} }
index += ghosts.Length; index += ghosts.Length;
Debug.Log($"{gameObject} Read back a ghostScareQueue of length {ghostScaredQueue.Count}"); //Debug.Log($"{gameObject} Read back a ghostScareQueue of length {ghostScaredQueue.Count}");
return true; return true;
} }

View File

@@ -36,6 +36,11 @@ namespace Marro.PacManUdon
data.Append(GetDirection(), ref index); data.Append(GetDirection(), ref index);
} }
public void PadSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{
index += 16;
}
public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType) public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{ {
SetPosition(data.ReadVector2(ref index)); SetPosition(data.ReadVector2(ref index));
@@ -43,5 +48,12 @@ namespace Marro.PacManUdon
return true; return true;
} }
public bool ConsumeSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{
index += 16;
return true;
}
} }
} }

View File

@@ -47,7 +47,7 @@ namespace Marro.PacManUdon
public void SetActive(bool isActive) public void SetActive(bool isActive)
{ {
Debug.Log($"({nameof(PacManUdon)} {nameof(Intermission2Pole)}) SetActive {isActive}."); //Debug.Log($"({nameof(PacManUdon)} {nameof(Intermission2Pole)}) SetActive {isActive}.");
gameObject.SetActive(isActive); gameObject.SetActive(isActive);
} }
@@ -115,7 +115,7 @@ namespace Marro.PacManUdon
public void SetStrechLevel(PoleStrechLevels level) public void SetStrechLevel(PoleStrechLevels level)
{ {
Debug.Log($"({nameof(PacManUdon)} {nameof(Intermission2Pole)}) Set strech level to {level}."); //Debug.Log($"({nameof(PacManUdon)} {nameof(Intermission2Pole)}) Set strech level to {level}.");
_animator.SetFloat("Strech", GetAnimatorValueForStrechLevel(level)); _animator.SetFloat("Strech", GetAnimatorValueForStrechLevel(level));
} }

File diff suppressed because it is too large Load Diff

View File

@@ -17,9 +17,13 @@ namespace Marro.PacManUdon
StartGameButtonPressed = 3, StartGameButtonPressed = 3,
SyncPellets = 4, SyncPellets = 4,
GhostUpdate = 5, 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. // 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. // If user is owner, this data is created and stored in a buffer which is cleared upon transmission.
@@ -60,9 +64,9 @@ namespace Marro.PacManUdon
/// </summary> /// </summary>
[SerializeField] private float pingDelay = 0.3f; [SerializeField] private float pingDelay = 0.3f;
/// <summary> /// <summary>
/// The rate at which updates occur. /// The time delta at which updates occur.
/// </summary> /// </summary>
[SerializeField] private float updateRate = 0.0166666667f; [SerializeField] private float updateDelta = 0.0166666667f;
#endregion #endregion
#region Constants #region Constants
@@ -99,9 +103,24 @@ namespace Marro.PacManUdon
#region Private attributes #region Private attributes
/// <summary> /// <summary>
/// Objects which are controlled by this <see cref="NetworkManager"/>. /// Whether <see cref="Initialize"/> has been called successfully.
/// </summary> /// </summary>
private SyncedObject[] syncedObjects; private bool initialized = false;
/// <summary>
/// Subscribers to each <see cref="NetworkEventType"/>.
/// </summary>
private SyncedObject[][] networkEventSubscribers;
/// <summary>
/// Indices for <see cref="networkEventSubscribers"/>.
/// </summary>
private int[] networkEventSubscribersIndices;
/// <summary>
/// Subscribers for <see cref="SyncedObject.SyncedUpdate"/>.
/// </summary>
private SyncedObject[] syncedUpdateSubscribers;
/// <summary> /// <summary>
/// Offset from system time to network time, including delay. /// Offset from system time to network time, including delay.
@@ -113,7 +132,17 @@ namespace Marro.PacManUdon
private float internalTime; private float internalTime;
/// <summary> /// <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> /// </summary>
private float nextUpdateTime; private float nextUpdateTime;
/// <summary> /// <summary>
@@ -185,16 +214,41 @@ namespace Marro.PacManUdon
#endregion #endregion
#region Public fields #region Public fields
private bool ready = false;
/// <summary> /// <summary>
/// Whether this <see cref="NetworkManager"/> is ready to transmit or receive data. /// Whether this <see cref="NetworkManager"/> is ready to transmit or receive data.
/// If false, networking is disabled and this <see cref="NetworkManager"/> acts as a pass-through. /// If false, networking is disabled and this <see cref="NetworkManager"/> acts as a pass-through.
/// </summary> /// </summary>
public bool Ready { get; private set; } = false; public bool Ready
{
get => ready;
private set
{
ready = value;
if (DebugImageToIndicateReady != null)
{
DebugImageToIndicateReady.SetFloat("Color", value ? 1 : 0);
}
}
}
/// <summary> /// <summary>
/// Whether the current perspective is synced with the owner. (Always true if current perspective is owner.) /// Whether the current perspective is synced with the owner. (Always true if current perspective is owner.)
/// </summary> /// </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> /// <summary>
/// The time since last full sync which is currently being simulated. /// The time since last full sync which is currently being simulated.
@@ -216,16 +270,36 @@ namespace Marro.PacManUdon
/// <summary> /// <summary>
/// Is the local user owner? /// Is the local user owner?
/// </summary> /// </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 #endregion
#region General #region General
/// <summary>
/// Initializes the <see cref="NetworkManager"/>. Call <see cref="Reset"/> afterwards to activate networking.
/// </summary>
public void Initialize() public void Initialize()
{ {
if (initialized)
{
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Tried to call {nameof(Initialize)} when already initialized!");
return;
}
if (!BitConverter.IsLittleEndian) if (!BitConverter.IsLittleEndian)
{ {
Debug.LogError($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Fatal: NetworkManager only supports little endian! Network sync will not be possible."); Debug.LogError($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Fatal: NetworkManager only supports little endian! Network sync will not be possible.");
Ready = false;
return; return;
} }
@@ -234,15 +308,46 @@ namespace Marro.PacManUdon
root = transform.parent.gameObject; root = transform.parent.gameObject;
} }
syncedObjects = root.GetComponentsInChildren<SyncedObject>(includeInactive: true); InitializeSubscribers();
foreach (var obj in syncedObjects)
initialized = true;
Reset();
}
private void InitializeSubscribers()
{
syncedUpdateSubscribers = root.GetComponentsInChildren<SyncedObject>(includeInactive: true);
foreach (var obj in syncedUpdateSubscribers)
{ {
obj.networkManager = this; obj.networkManager = this;
} }
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Found {syncedObjects.Length} {nameof(SyncedObject)} in children of {root.name}."); Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Found {syncedUpdateSubscribers.Length} {nameof(SyncedObject)} in children of {root.name}.");
SetOwner(Networking.IsOwner(gameObject)); const int eventTypeCount = byte.MaxValue + 1;
networkEventSubscribers = new SyncedObject[eventTypeCount][];
networkEventSubscribersIndices = new int[eventTypeCount];
}
public void Reset()
{
if (!initialized)
{
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Tried to call {nameof(Reset)} while not initialized. Call {nameof(Initialize)} first.");
return;
}
if (tester == null)
{
IsOwner = Networking.IsOwner(gameObject);
}
else
{
IsOwner = tester.ShouldBeOwner(this);
}
ClearBuffer(); ClearBuffer();
@@ -256,26 +361,63 @@ namespace Marro.PacManUdon
SyncedDeltaTime = Time.fixedDeltaTime; SyncedDeltaTime = Time.fixedDeltaTime;
nextUpdateTime = SyncedTime; nextUpdateTime = SyncedTime;
if (!Synced)
{
RequestEvent(NetworkEventType.FullSync); // See if we can sync up
}
Ready = true; Ready = true;
// Sync up
if (IsOwner)
{
SendEventSoon(NetworkEventType.FullSyncForced);
}
else
{
RequestEvent(NetworkEventType.FullSync);
}
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Initialized, time offset: {offsetTime}"); Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Initialized, time offset: {offsetTime}");
} }
public void Update() public void Update()
{ {
if (!Ready) if (!initialized)
{ {
return; return;
} }
// Fetch the current time // Get our target time
UpdateInternalTime(); UpdateInternalTime();
// Forwards simulated time by updateDelta until we're caught up
while (nextUpdateTime <= internalTime)
{
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) if (Ready)
{ {
if (IsOwner) if (IsOwner)
@@ -285,31 +427,22 @@ namespace Marro.PacManUdon
} }
else else
{ {
ApplyReceivedEvents(); // See if there's events that need to be replayed ApplyReceivedEvents(); // See if there's events after last update that need to be replayed
} }
} }
// Forwards simulated time at the updateRate pace ProgressSyncedTime(nextUpdateTime);
while (nextUpdateTime <= internalTime) CallSyncedUpdate();
{ nextUpdateTime = SyncedTime + updateDelta;
ProgressSyncedTime(nextUpdateTime);
PerformFixedSyncedUpdate();
nextUpdateTime = SyncedTime + updateRate;
}
} }
private void UpdateInternalTime() private void CallSyncedUpdate()
{
internalTime = Time.fixedTime - offsetTime;
}
private void PerformFixedSyncedUpdate()
{ {
IsEventUpdate = false; 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) if (obj.gameObject.activeInHierarchy)
{ {
@@ -348,14 +481,30 @@ namespace Marro.PacManUdon
} }
} }
private void SetOwner(bool isOwner) private SyncedObject[] GetEventSubscribers(NetworkEventType eventType) => networkEventSubscribers[(int)eventType];
{
IsOwner = isOwner;
if (DebugImageToIndicateOwner != null) public void SubscribeToEvent(SyncedObject syncedObject, NetworkEventType eventType)
{
// This is inefficient, but I'd rather initialize slowly than perform bounds checks in often called code
var eventTypeIndex = (int)eventType;
var subscribers = networkEventSubscribers[eventTypeIndex];
int subscribersIndex = networkEventSubscribersIndices[eventTypeIndex];
if (subscribers == null)
{ {
DebugImageToIndicateOwner.SetFloat("Color", isOwner ? 1 : 0); subscribers = new SyncedObject[1];
} }
else
{
subscribers = new SyncedObject[subscribersIndex+1];
Array.Copy(networkEventSubscribers[eventTypeIndex], subscribers, subscribersIndex);
}
subscribers[subscribersIndex] = syncedObject;
networkEventSubscribers[eventTypeIndex] = subscribers;
networkEventSubscribersIndices[eventTypeIndex] = subscribersIndex;
} }
#endregion #endregion
@@ -412,9 +561,14 @@ namespace Marro.PacManUdon
InitializeEvent(eventType, timestamp, eventId, out byte[] data, out var index); InitializeEvent(eventType, timestamp, eventId, out byte[] data, out var index);
foreach (var obj in syncedObjects) var subscibers = GetEventSubscribers(eventType);
if (subscibers != null)
{ {
obj.CollectSyncedData(data, ref index, eventType); foreach (var obj in subscibers)
{
obj.CollectSyncedData(data, ref index, eventType);
}
} }
// Validate and fill in event size // Validate and fill in event size
@@ -455,6 +609,10 @@ namespace Marro.PacManUdon
{ {
RequestSerialization(); RequestSerialization();
serializationRequested = true; serializationRequested = true;
if (tester != null)
{
tester.RequestSerializationTest();
}
} }
[NetworkCallable] [NetworkCallable]
@@ -481,7 +639,7 @@ namespace Marro.PacManUdon
private void ProgressPingTime() private void ProgressPingTime()
{ {
if (eventsQueueIndex > 0 if (eventsQueueIndex > 0 && !serializationRequested
&& internalTime - lastEventTransmissionTime >= pingDelay) && internalTime - lastEventTransmissionTime >= pingDelay)
{ {
RequestSerializationForEvents(); RequestSerializationForEvents();
@@ -509,16 +667,19 @@ namespace Marro.PacManUdon
Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Serializing {eventsQueueIndex} event(s), eventTransmissionHistory is now {ArrayToString(eventTransmissionHistory)} with index {eventTransmissionHistoryIndex}"); Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Serializing {eventsQueueIndex} event(s), eventTransmissionHistory is now {ArrayToString(eventTransmissionHistory)} with index {eventTransmissionHistoryIndex}");
} }
public override void OnPostSerialization(SerializationResult result) public override void OnPostSerialization(SerializationResult result) => OnPostSerializationInternal(result.success, result.byteCount);
// Version of OnPostSerialization which does not require instantiating SerializationResult, so that it can be called for debugging purposes.
public void OnPostSerializationInternal(bool success, int byteCount)
{ {
if (!Ready || !IsOwner || networkedData.Length == 0) if (!Ready || !IsOwner || networkedData.Length == 0)
{ {
return; return;
} }
if (!result.success) if (!success)
{ {
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Serialization failed! Tried to send {result.byteCount} bytes."); Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Serialization failed! Tried to send {byteCount} bytes.");
return; return;
} }
@@ -553,6 +714,11 @@ namespace Marro.PacManUdon
} }
SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.Owner, "RequestEventReceived", eventType); SendCustomNetworkEvent(VRC.Udon.Common.Interfaces.NetworkEventTarget.Owner, "RequestEventReceived", eventType);
if (tester != null)
{
tester.RequestEvent(eventType);
}
} }
private void StoreIncomingData() private void StoreIncomingData()
@@ -618,7 +784,7 @@ namespace Marro.PacManUdon
QueueEventInBuffer(@event); QueueEventInBuffer(@event);
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)} Queued event with id {eventId}"); //Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)} Queued event with id {eventId}");
} }
else else
{ {
@@ -656,7 +822,7 @@ namespace Marro.PacManUdon
{ {
IsEventUpdate = true; IsEventUpdate = true;
while (eventsQueueIndex > 0 && nextEventTime <= internalTime) while (eventsQueueIndex > 0 && nextEventTime <= SyncedTime)
{ {
var success = ApplyEvent(eventsQueue[0]); var success = ApplyEvent(eventsQueue[0]);
@@ -686,20 +852,27 @@ namespace Marro.PacManUdon
ProgressSyncedTime(timestamp); ProgressSyncedTime(timestamp);
foreach (var obj in syncedObjects) var subscribers = GetEventSubscribers(eventType);
{
obj.SyncedUpdate();
}
foreach (var obj in syncedObjects) Debug.Log($"({nameof(PacManUdon)} {nameof(NetworkManager)}) ApplyEvent with dt {SyncedDeltaTime}");
{
var success = obj.WriteSyncedData(@event, ref index, eventType);
if (!success) if (subscribers != null)
{
foreach (var obj in subscribers)
{ {
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Malformed data reported by {obj.name} during event type {eventType}!"); obj.SyncedUpdate();
HandleError(true); }
return false;
foreach (var obj in subscribers)
{
var success = obj.WriteSyncedData(@event, ref index, eventType);
if (!success)
{
Debug.LogWarning($"({nameof(PacManUdon)} {nameof(NetworkManager)}) Malformed data reported by {obj.name} during event type {eventType}!");
HandleError(true);
return false;
}
} }
} }
@@ -819,7 +992,7 @@ namespace Marro.PacManUdon
HandleError(true); HandleError(true);
return; return;
} }
if (nextEventTime < SyncedTime) if (nextEventTime < SyncedTime)
{ {
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: {internalTime}");
@@ -869,7 +1042,7 @@ namespace Marro.PacManUdon
} }
bool newOwnerIsLocalPlayer = newOwner == Networking.LocalPlayer; bool newOwnerIsLocalPlayer = newOwner == Networking.LocalPlayer;
SetOwner(newOwnerIsLocalPlayer); IsOwner = newOwnerIsLocalPlayer;
if (newOwnerIsLocalPlayer) if (newOwnerIsLocalPlayer)
{ {
@@ -936,6 +1109,31 @@ namespace Marro.PacManUdon
} }
#endregion #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 #region Debug
public void SimulateSyncToTimestamp(float timestamp) public void SimulateSyncToTimestamp(float timestamp)
{ {
@@ -965,6 +1163,32 @@ namespace Marro.PacManUdon
/// </summary> /// </summary>
[SerializeField] private Animator DebugImageToIndicateOwner; [SerializeField] private Animator DebugImageToIndicateOwner;
/// <summary>
/// An animator which visualizes whether the NetworkManager is synced.
/// </summary>
[SerializeField] private Animator DebugImageToIndicateSynced;
/// <summary>
/// An animator which visualizes whether the NetworkManager is synced.
/// </summary>
[SerializeField] private Animator DebugImageToIndicateReady;
private NetworkManagerTester tester;
public void SetNetworkManagerTester(NetworkManagerTester tester)
{
this.tester = tester;
}
public byte[] NetworkedData
{
get => networkedData;
set => networkedData = value;
}
public bool SerializationRequested => serializationRequested;
public void SetIsOwner(bool isOwner) => IsOwner = isOwner;
public void DoFullSync() public void DoFullSync()
{ {
SendEventSoon(NetworkEventType.FullSync); SendEventSoon(NetworkEventType.FullSync);
@@ -979,6 +1203,47 @@ namespace Marro.PacManUdon
{ {
SendEventSoon(NetworkEventType.GhostUpdate); SendEventSoon(NetworkEventType.GhostUpdate);
} }
public void DoTimeSequenceSync()
{
SendEventSoon(NetworkEventType.TimeSequenceSync);
}
public 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 #endregion
} }
} }

View File

@@ -50,6 +50,8 @@ namespace Marro.PacManUdon
hideUntilUnfrozen = false; hideUntilUnfrozen = false;
startPosition = startTransform.localPosition; startPosition = startTransform.localPosition;
startRotation = startTransform.localRotation; startRotation = startTransform.localRotation;
SubscribeToEvent(NetworkEventType.PacManTurn);
} }
public void Reset() public void Reset()
@@ -64,7 +66,7 @@ namespace Marro.PacManUdon
SetDead(false); SetDead(false);
animator.SetTrigger("Reset"); animator.SetTrigger("Reset");
Debug.Log($"{gameObject} Reset! Position is now {GetPosition()}."); //Debug.Log($"{gameObject} Reset! Position is now {GetPosition()}.");
} }
public override void SyncedUpdate() public override void SyncedUpdate()
@@ -151,7 +153,11 @@ namespace Marro.PacManUdon
SetDirection(inputDirection + new Vector2(GridMoverTools.PositionToGrid(nextPosition).x - nextPosition.x, 0).normalized); 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 SetTargetDirection(inputDirection); // This is the direction most logic should assume pacman is moving, the actual direction may be different due to cornering
networkManager.SendEventSoon(NetworkEventType.PacManTurn);
if (!followingPredefinedPath)
{
networkManager.SendEventSoon(NetworkEventType.PacManTurn);
}
} }
return nextPosition; return nextPosition;
@@ -336,11 +342,18 @@ namespace Marro.PacManUdon
public override void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType) public override void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{ {
if (eventType != NetworkEventType.PacManTurn || kinematic || frozen || !enabled) if (eventType != NetworkEventType.PacManTurn)
{ {
return; return;
} }
if (kinematic || frozen || !enabled)
{
index += 8;
base.PadSyncedData(data, ref index, eventType);
return;
}
data.Append(targetDirection, ref index); data.Append(targetDirection, ref index);
base.CollectSyncedData(data, ref index, eventType); base.CollectSyncedData(data, ref index, eventType);
@@ -348,11 +361,18 @@ namespace Marro.PacManUdon
public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType) public override bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType)
{ {
if (eventType != NetworkEventType.PacManTurn || kinematic || frozen || !enabled) if (eventType != NetworkEventType.PacManTurn)
{ {
return true; return true;
} }
if (kinematic || frozen || !enabled)
{
index += 8;
base.ConsumeSyncedData(data, ref index, eventType);
return true;
}
SetTargetDirection(data.ReadVector2(ref index)); SetTargetDirection(data.ReadVector2(ref index));
return base.WriteSyncedData(data, ref index, eventType); return base.WriteSyncedData(data, ref index, eventType);

View File

@@ -28,6 +28,8 @@ namespace Marro.PacManUdon
SetPowerPelletsBlink(false); SetPowerPelletsBlink(false);
RestoreAllPellets(); RestoreAllPellets();
SubscribeToEvent(NetworkEventType.SyncPellets);
} }
public override void SyncedUpdate() public override void SyncedUpdate()

View File

@@ -18,12 +18,9 @@ namespace Marro.PacManUdon
private void StartTimeSequence(PacManTimeSequence timeSequence) private void StartTimeSequence(PacManTimeSequence timeSequence)
{ {
Debug.Log($"StartTimeSequence: {timeSequence}"); //Debug.Log($"StartTimeSequence: {timeSequence}");
if (currentlyInTimeSequence) TimeSequenceTryEndCurrent();
{
TimeSequenceEndCurrent();
}
TimeSequencePrepareForStart(timeSequence); TimeSequencePrepareForStart(timeSequence);
@@ -35,12 +32,24 @@ namespace Marro.PacManUdon
TimeSequenceProgressToTime(timeSequenceSecondsPassed); TimeSequenceProgressToTime(timeSequenceSecondsPassed);
} }
private void TimeSequenceEndCurrent() private void TimeSequenceTryEndCurrent()
{ {
if (!currentlyInTimeSequence)
{
return;
}
//Debug.Log($"{gameObject} TimeSequenceEndCurrent");
jumpingToTimeSequence = true; jumpingToTimeSequence = true;
TimeSequenceProgressToTime(100000f); TimeSequenceProgressToTime(100000f);
Debug.LogWarning($"{gameObject} TimeSequenceEndCurrent");
TryFinalizeTimeSequence();
if (waitingForTimeSequenceFinalize)
{
TimeSequenceExecuteFinalize(currentTimeSequence);
}
jumpingToTimeSequence = false; jumpingToTimeSequence = false;
} }
@@ -56,7 +65,7 @@ namespace Marro.PacManUdon
private void TimeSequenceSkipToNextStep() private void TimeSequenceSkipToNextStep()
{ {
Debug.Log($"{gameObject} TimeSequenceSkipToNextStep"); //Debug.Log($"{gameObject} TimeSequenceSkipToNextStep");
if (timeSequenceProgress < timeSequenceKeyframeTimes.Length) if (timeSequenceProgress < timeSequenceKeyframeTimes.Length)
{ {
TimeSequenceProgressToTime(timeSequenceKeyframeTimes[timeSequenceProgress]); TimeSequenceProgressToTime(timeSequenceKeyframeTimes[timeSequenceProgress]);
@@ -79,61 +88,40 @@ namespace Marro.PacManUdon
if (timeSequenceProgress >= timeSequenceKeyframeTimes.Length) if (timeSequenceProgress >= timeSequenceKeyframeTimes.Length)
{ {
currentlyInTimeSequence = false; currentlyInTimeSequence = false;
TimeSequencePrepareForFinish(currentTimeSequence); TimeSequenceFinish(currentTimeSequence);
break; break;
} }
} }
} }
private void TimeSequencePrepareForFinish(PacManTimeSequence timeSequence) private void TimeSequenceFinish(PacManTimeSequence timeSequence)
{ {
//if (networkManager.IsOwner)
//{
Debug.LogWarning($"{gameObject} TimeSequencePrepareForFinish");
TimeSequenceExecuteFinalize(timeSequence); TimeSequenceExecuteFinalize(timeSequence);
networkManager.SendEventSoon(NetworkEventType.TimeSequenceSync);
if (!jumpingToTimeSequence) if (!jumpingToTimeSequence)
{ {
TimeSequenceExecuteFinished(timeSequence); 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); // If we're (now) in a time sequence, jump our progress to match the one on the remote
waitingForTimeSequenceFinalize = false; 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 #region Events
public void JumpToTimeSequenceAttractScreenIntroduction() public void JumpToTimeSequenceAttractScreenIntroduction()
@@ -244,7 +232,7 @@ namespace Marro.PacManUdon
private void TimeSequenceExecuteStep(PacManTimeSequence timeSequence, int sequenceProgress) private void TimeSequenceExecuteStep(PacManTimeSequence timeSequence, int sequenceProgress)
{ {
Debug.Log($"{gameObject} Triggered time sequence step for sequence {currentTimeSequence} with progress {sequenceProgress}"); //Debug.Log($"{gameObject} Triggered time sequence step for sequence {currentTimeSequence} with progress {sequenceProgress}");
switch (timeSequence) switch (timeSequence)
{ {
default: default:
@@ -298,7 +286,7 @@ namespace Marro.PacManUdon
private void TimeSequenceExecuteFinalize(PacManTimeSequence timeSequence) private void TimeSequenceExecuteFinalize(PacManTimeSequence timeSequence)
{ {
Debug.Log($"{gameObject} Triggered time sequence finalize for sequence {currentTimeSequence}"); //Debug.Log($"{gameObject} Triggered time sequence finalize for sequence {currentTimeSequence}");
switch (timeSequence) switch (timeSequence)
{ {
default: default:
@@ -342,7 +330,7 @@ namespace Marro.PacManUdon
private void TimeSequenceExecuteFinished(PacManTimeSequence timeSequence) private void TimeSequenceExecuteFinished(PacManTimeSequence timeSequence)
{ {
Debug.Log($"{gameObject} Triggered time sequence finished for sequence {currentTimeSequence}"); //Debug.Log($"{gameObject} Triggered time sequence finished for sequence {currentTimeSequence}");
switch (timeSequence) switch (timeSequence)
{ {
default: default:

View File

@@ -9,7 +9,9 @@ namespace Marro.PacManUdon
public NetworkManager networkManager; public NetworkManager networkManager;
public virtual void SyncedUpdate() { } public virtual void SyncedUpdate() { }
public abstract void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType); public virtual void CollectSyncedData(byte[] data, ref int index, NetworkEventType eventType) { }
public abstract bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType); public virtual bool WriteSyncedData(byte[] data, ref int index, NetworkEventType eventType) { return false; }
protected void SubscribeToEvent(NetworkEventType eventType) => networkManager.SubscribeToEvent(this, eventType);
} }
} }

View File

@@ -0,0 +1,460 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c333ccfdd0cbdbc4ca30cef2dd6e6b9b, type: 3}
m_Name: NetworkManagerSyncTester
m_EditorClassIdentifier:
serializedUdonProgramAsset: {fileID: 11400000, guid: b33acf2ffaef28f49ba36d29b13cf487, type: 2}
udonAssembly:
assemblyError:
sourceCsScript: {fileID: 11500000, guid: cbbf384cde136444d9f640ff9f3445cf, type: 3}
scriptVersion: 2
compiledVersion: 2
behaviourSyncMode: 0
hasInteractEvent: 0
scriptID: -7942820763917989394
serializationData:
SerializedFormat: 2
SerializedBytes:
ReferencedUnityObjects: []
SerializedBytesString:
Prefab: {fileID: 0}
PrefabModificationsReferencedUnityObjects: []
PrefabModifications: []
SerializationNodes:
- Name: fieldDefinitions
Entry: 7
Data: 0|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UdonSharp.Compiler.FieldDefinition,
UdonSharp.Editor]], mscorlib
- Name: comparer
Entry: 7
Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.String,
mscorlib]], mscorlib
- Name:
Entry: 8
Data:
- Name:
Entry: 12
Data: 7
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: networkManager1
- Name: $v
Entry: 7
Data: 2|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: networkManager1
- Name: <UserType>k__BackingField
Entry: 7
Data: 3|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: Marro.PacManUdon.NetworkManager, Assembly-CSharp
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 7
Data: 4|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: VRC.Udon.UdonBehaviour, VRC.Udon
- Name:
Entry: 8
Data:
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 5|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 6|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: gridMovers1
- Name: $v
Entry: 7
Data: 7|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: gridMovers1
- Name: <UserType>k__BackingField
Entry: 7
Data: 8|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: Marro.PacManUdon.GridMover[], Assembly-CSharp
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 7
Data: 9|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: UnityEngine.Component[], UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 10|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 11|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: networkManager2
- Name: $v
Entry: 7
Data: 12|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: networkManager2
- Name: <UserType>k__BackingField
Entry: 9
Data: 3
- Name: <SystemType>k__BackingField
Entry: 9
Data: 4
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 13|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 14|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: gridMovers2
- Name: $v
Entry: 7
Data: 15|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: gridMovers2
- Name: <UserType>k__BackingField
Entry: 9
Data: 8
- Name: <SystemType>k__BackingField
Entry: 9
Data: 9
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 16|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 17|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: debugImageToIndicateSynced
- Name: $v
Entry: 7
Data: 18|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: debugImageToIndicateSynced
- Name: <UserType>k__BackingField
Entry: 7
Data: 19|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: UnityEngine.Animator, UnityEngine.AnimationModule
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 19
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 20|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 21|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: captureTime
- Name: $v
Entry: 7
Data: 22|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: captureTime
- Name: <UserType>k__BackingField
Entry: 7
Data: 23|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: System.Single, mscorlib
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 23
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 24|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: positions
- Name: $v
Entry: 7
Data: 25|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: positions
- Name: <UserType>k__BackingField
Entry: 7
Data: 26|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: UnityEngine.Vector2[], UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 26
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 27|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bcf028de1e758ca45bcd8834df0b821c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,99 @@
using Marro.PacManUdon;
using Newtonsoft.Json.Linq;
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class NetworkManagerSyncTester : UdonSharpBehaviour
{
[SerializeField] NetworkManager networkManager1;
[SerializeField] GridMover[] gridMovers1;
[SerializeField] NetworkManager networkManager2;
[SerializeField] GridMover[] gridMovers2;
[SerializeField] Animator debugImageToIndicateSynced;
private float captureTime;
private Vector2[] positions;
public void Update()
{
if (!networkManager1.Synced || !networkManager2.Synced)
{
positions = null;
return;
}
if (positions == null)
{
positions = GetPositions(gridMovers1);
captureTime = networkManager1.SyncedTime;
}
var remoteTime = networkManager2.SyncedTime;
if (captureTime > remoteTime)
{
return;
}
if (captureTime < remoteTime)
{
Debug.Log($"{nameof(NetworkManagerSyncTester)} Skipping check");
positions = null;
return;
}
bool equal = IsEqual(remoteTime);
SetIndicator(equal);
positions = null;
}
private bool IsEqual(float remoteTime)
{
var positions2 = GetPositions(gridMovers2);
for (int i = 0; i < positions.Length; i++)
{
var gridMover1 = gridMovers1[i];
var gridMover1Position = positions[i];
var gridMover2 = gridMovers2[i];
var gridMover2Position = positions2[i];
var equal = gridMover1Position == gridMover2Position;
if (!equal)
{
Debug.LogWarning($"{nameof(NetworkManagerSyncTester)} Desync found:\n{gridMover1.name} {gridMover1Position} (at {captureTime}) != {gridMover2.name} {gridMover2Position} (at {remoteTime})");
networkManager1.Pause();
networkManager2.Pause();
return false;
}
}
return true;
}
private void SetIndicator(bool value)
{
if (debugImageToIndicateSynced != null)
{
debugImageToIndicateSynced.SetFloat("Color", value ? 1 : 0);
}
}
private Vector2[] GetPositions(GridMover[] gridMovers)
{
var length = gridMovers.Length;
var positions = new Vector2[length];
for (int i = 0; i < length; i++)
{
positions[i] = gridMovers[i].GetPosition();
}
return positions;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cbbf384cde136444d9f640ff9f3445cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,340 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c333ccfdd0cbdbc4ca30cef2dd6e6b9b, type: 3}
m_Name: NetworkManagerTester
m_EditorClassIdentifier:
serializedUdonProgramAsset: {fileID: 11400000, guid: 2a95858b6f853584c8e55a5140383df5, type: 2}
udonAssembly:
assemblyError:
sourceCsScript: {fileID: 11500000, guid: 8261cb80d1b7e5f45b0b9afd3f6bb801, type: 3}
scriptVersion: 2
compiledVersion: 2
behaviourSyncMode: 0
hasInteractEvent: 0
scriptID: 3371975218947168961
serializationData:
SerializedFormat: 2
SerializedBytes:
ReferencedUnityObjects: []
SerializedBytesString:
Prefab: {fileID: 0}
PrefabModificationsReferencedUnityObjects: []
PrefabModifications: []
SerializationNodes:
- Name: fieldDefinitions
Entry: 7
Data: 0|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UdonSharp.Compiler.FieldDefinition,
UdonSharp.Editor]], mscorlib
- Name: comparer
Entry: 7
Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.String,
mscorlib]], mscorlib
- Name:
Entry: 8
Data:
- Name:
Entry: 12
Data: 5
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: networkManagers
- Name: $v
Entry: 7
Data: 2|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: networkManagers
- Name: <UserType>k__BackingField
Entry: 7
Data: 3|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: Marro.PacManUdon.NetworkManager[], Assembly-CSharp
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 7
Data: 4|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: UnityEngine.Component[], UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 5|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 6|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: interval
- Name: $v
Entry: 7
Data: 7|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: interval
- Name: <UserType>k__BackingField
Entry: 7
Data: 8|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: System.Single, mscorlib
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 8
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 9|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 10|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: ownerIndex
- Name: $v
Entry: 7
Data: 11|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: ownerIndex
- Name: <UserType>k__BackingField
Entry: 7
Data: 12|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: System.Int32, mscorlib
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 12
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 13|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 14|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: countdown
- Name: $v
Entry: 7
Data: 15|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: countdown
- Name: <UserType>k__BackingField
Entry: 9
Data: 8
- Name: <SystemType>k__BackingField
Entry: 9
Data: 8
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 16|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: serializationRequested
- Name: $v
Entry: 7
Data: 17|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: serializationRequested
- Name: <UserType>k__BackingField
Entry: 7
Data: 18|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: System.Boolean, mscorlib
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 18
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 19|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 46452140f2a4ee7458e4f4e13fea6578
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,99 @@
using Marro.PacManUdon;
using System;
using UdonSharp;
using UnityEngine;
public class NetworkManagerTester : UdonSharpBehaviour
{
[SerializeField] private NetworkManager[] networkManagers;
[SerializeField] private float interval = 0.1f;
[SerializeField] private int ownerIndex = 0;
private float countdown = 0;
private bool serializationRequested;
private void Start()
{
foreach (var networkManager in networkManagers)
{
networkManager.SetNetworkManagerTester(this);
if (networkManager.Ready)
{
networkManager.Reset();
}
}
}
private void FixedUpdate()
{
countdown -= Time.deltaTime;
if (countdown > 0)
{
return;
}
countdown = interval;
if (!serializationRequested)
{
return;
}
var source = networkManagers[ownerIndex];
var data = PerformSerialization(source);
Debug.Log($"{nameof(NetworkManagerTester)} Transferring {data.Length} bytes.");
foreach (var target in networkManagers)
{
if (target == source)
{
continue;
}
PerformDeserialization(target, data);
}
serializationRequested = false;
}
private byte[] PerformSerialization(NetworkManager networkManager)
{
networkManager.OnPreSerialization();
var data = networkManager.NetworkedData;
networkManager.OnPostSerializationInternal(true, data.Length) ;
return data;
}
private byte[] PerformDeserialization(NetworkManager networkManager, byte[] data)
{
networkManager.NetworkedData = data;
networkManager.OnDeserialization();
return data;
}
public bool ShouldBeOwner(NetworkManager manager) => Array.IndexOf(networkManagers, manager) == ownerIndex;
public void RequestSerializationTest()
{
serializationRequested = true;
}
public void RequestEvent(NetworkEventType eventType)
{
foreach (var target in networkManagers)
{
if (!target.IsOwner || !target.SerializationRequested)
{
continue;
}
Debug.Log($"{nameof(NetworkManagerTester)} Requested event with type {eventType}.");
target.RequestEventReceived(eventType);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8261cb80d1b7e5f45b0b9afd3f6bb801
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: