Skip to content

Commit

Permalink
Implement display of progress for stroke synchronization
Browse files Browse the repository at this point in the history
1 - implement strokes guid
2 - Implement  display of progress for stroke synchronization
  • Loading branch information
sbanca committed Dec 10, 2024
1 parent 7d2f22f commit 330e69e
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 36 deletions.
1 change: 1 addition & 0 deletions Assets/Scripts/Multiplayer/MultiplayerInterfaces.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public interface IDataConnectionHandler : IConnectionHandler
Task<bool> PerformCommand(BaseCommand command);
Task<bool> SendCommandToPlayer(BaseCommand command, int playerId);
Task<bool> CheckCommandReception(BaseCommand command, int playerId);
Task<bool> CheckStrokeReception(Stroke stroke, int playerId);
Task<bool> UndoCommand(BaseCommand command);
Task<bool> RedoCommand(BaseCommand command);
Task<bool> RpcSyncToSharedAnchor(string uuid);
Expand Down
10 changes: 10 additions & 0 deletions Assets/Scripts/Multiplayer/MultiplayerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,16 @@ public async Task<bool> CheckCommandReception(BaseCommand command, int id)
return false;
}

public async Task<bool> CheckStrokeReception(Stroke stroke, int id)
{
if (State == ConnectionState.IN_ROOM)
{
return await m_Manager.CheckStrokeReception(stroke, id);
}

return false;
}

public void OnCommandUndo(BaseCommand command)
{
if (State == ConnectionState.IN_ROOM)
Expand Down
105 changes: 77 additions & 28 deletions Assets/Scripts/Multiplayer/MultiplayerSceneSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Threading.Tasks;
using TiltBrush;
using TMPro;
using UnityEngine;


namespace OpenBrush.Multiplayer
{
public class MultiplayerSceneSync : MonoBehaviour
Expand All @@ -35,7 +36,7 @@ public class MultiplayerSceneSync : MonoBehaviour

private bool _isWaiting = false;
private bool _isSendingCommandHistory = false;
private InfoCardAnimation infoCard;


void Awake()
{
Expand Down Expand Up @@ -73,6 +74,7 @@ public void StartSyncronizationForUser(int id)
async void SendStrokesToPlayer(int id)
{
LinkedList<Stroke> strokes = SketchMemoryScript.m_Instance.GetMemoryList;
StartSyncProgressDisplayForSrokes(id, strokes);
const int chunkSize = 5;
List<Stroke> strokeList = strokes.ToList();

Expand All @@ -85,7 +87,6 @@ async void SendStrokesToPlayer(int id)
counter += chunk.Count;
//Debug.Log($"Sent {strokesData.Length} bytes of serialized stroke data (batch {(i / chunkSize) + 1}) to player {id}.");
}
SynchHistoryComplete(id);
}

async void DeserializeReceivedStrokes(byte[] largeData)
Expand All @@ -108,7 +109,7 @@ async void DeserializeReceivedStrokes(byte[] largeData)

void OnLargeDataReceived(byte[] largeData)
{
Debug.Log($"[Multiplayer Scene Sync]Successfully received {largeData.Length} bytes from the autosave.");
//Debug.Log($"[Multiplayer Scene Sync]Successfully received {largeData.Length} bytes from the autosave.");

DeserializeReceivedStrokes(largeData);
}
Expand Down Expand Up @@ -149,7 +150,9 @@ public IEnumerator SendCommandHistory(int id)

int firstCommandTimestamp = commands.Any() ? commands.First().NetworkTimestamp ?? int.MaxValue : int.MaxValue;

CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp);
CreateBrushStrokeCommands(strokesWithoutCommand, firstCommandTimestamp); // this add the strokes without commands to the IEnumerable<BaseCommand> commands

StartSyncProgressDisplayForCommands(id, commands.ToList());

int packetCounter = 0;
int counter = 0;
Expand All @@ -166,13 +169,10 @@ public IEnumerator SendCommandHistory(int id)
MultiplayerManager.m_Instance.OnCommandPerformed(command);
packetCounter += estimatedMessages;
counter++;
SynchHistoryPercentage(id, commands.Count(), counter);
}

_isSendingCommandHistory = false;

SynchHistoryComplete(id);

}

private void CreateBrushStrokeCommands(List<Stroke> strokes, int LastTimestamp)
Expand Down Expand Up @@ -219,6 +219,43 @@ private int EstimateMessagesForCommand(BaseCommand command)
#endregion

#region Remote infoCard commands

public async void StartSyncProgressDisplayForSrokes(int TargetPlayerId, LinkedList<Stroke> strokes)
{
StartSynchHistory(TargetPlayerId);

int sentStrokes = 0;
foreach (var stroke in strokes)
{
while (await MultiplayerManager.m_Instance.CheckStrokeReception(stroke, TargetPlayerId))
{
await Task.Delay(200);
}
sentStrokes++;
SynchHistoryPercentage(TargetPlayerId, strokes.Count, sentStrokes);
}

SynchHistoryComplete(TargetPlayerId);
}

public async void StartSyncProgressDisplayForCommands(int TargetPlayerId, List<BaseCommand> commands)
{
StartSynchHistory(TargetPlayerId);

int sentStrokes = 0;
foreach (var command in commands)
{
while (await MultiplayerManager.m_Instance.CheckCommandReception(command, TargetPlayerId))
{
await Task.Delay(200);
}
sentStrokes++;
SynchHistoryPercentage(TargetPlayerId, commands.Count, sentStrokes);
}

SynchHistoryComplete(TargetPlayerId);
}

private void StartSynchHistory(int id)
{
MultiplayerManager.m_Instance.StartSynchHistory(id);
Expand All @@ -238,54 +275,66 @@ private void SynchHistoryComplete(int id)
#endregion

#region Local infoCard commands

private InfoCardAnimation infoCard;
private readonly object infoCardLock = new object();

public void DisplaySynchInfo()
{
OutputWindowScript.m_Instance.CreateInfoCardAtController(
InputManager.ControllerName.Brush,
"Synch Started!",
fPopScalar: 1.0f
);
RetrieveInfoCard();
lock (infoCardLock)
{
if (infoCard == null)
{
OutputWindowScript.m_Instance.CreateInfoCardAtController(
InputManager.ControllerName.Brush,
"Synch Started!",
fPopScalar: 1.0f
);
infoCard = RetrieveInfoCard();
}
}
}

public InfoCardAnimation RetrieveInfoCard()
{
InfoCardAnimation[] allInfoCards = UnityEngine.Object.FindObjectsOfType<InfoCardAnimation>();

InfoCardAnimation[] allInfoCards = FindObjectsOfType<InfoCardAnimation>();
foreach (var card in allInfoCards)
{
TextMeshPro textComponent = card.GetComponentInChildren<TextMeshPro>();
if (textComponent != null && textComponent.text.Contains("Synch"))
{
infoCard = card;
return card;
}
}

return null;
}

public void SynchInfoPercentageUpdate()
{
int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100);
string text = $"Synch {percentage}%";
lock (infoCardLock)
{
int percentage = (int)((float)SketchMemoryScript.AllStrokesCount() / numberOfCommandsExpected * 100);
string text = $"Synch {percentage}%";

if (infoCard == null) infoCard = RetrieveInfoCard();
if (infoCard == null) DisplaySynchInfo();
if (infoCard == null) DisplaySynchInfo();

infoCard.GetComponentInChildren<TextMeshPro>().text = text;
infoCard.UpdateHoldingDuration(5f);
infoCard.GetComponentInChildren<TextMeshPro>().text = text;
infoCard.UpdateHoldingDuration(5f);
}
}

public void HideSynchInfo()
{
if (infoCard == null) infoCard = RetrieveInfoCard();
if (infoCard == null) DisplaySynchInfo();
lock (infoCardLock)
{
if (infoCard == null) return;

infoCard.GetComponentInChildren<TextMeshPro>().text = "Synch Ended!";
infoCard.UpdateHoldingDuration(3.0f);
infoCard.GetComponentInChildren<TextMeshPro>().text = "Synch Ended!";
infoCard.UpdateHoldingDuration(3.0f);
}
}


#endregion

}
Expand Down
7 changes: 7 additions & 0 deletions Assets/Scripts/Multiplayer/Photon/PhotonManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,13 @@ public async Task<bool> CheckCommandReception(BaseCommand command, int playerId)
return await PhotonRPC.WaitForAcknowledgment(command.Guid);
}

public async Task<bool> CheckStrokeReception(Stroke stroke, int playerId)
{
PlayerRef targetPlayer = PlayerRef.FromEncoded(playerId);
PhotonRPC.RPC_CheckStroke(m_Runner, stroke.m_Guid, m_Runner.LocalPlayer, targetPlayer);
return await PhotonRPC.WaitForAcknowledgment(stroke.m_Guid);
}

public async Task<bool> UndoCommand(BaseCommand command)
{
PhotonRPC.RPC_Undo(m_Runner, command.GetType().ToString());
Expand Down
35 changes: 27 additions & 8 deletions Assets/Scripts/Multiplayer/Photon/PhotonRPC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@ public class PhotonRPC : SimulationBehaviour
{
private static Dictionary<Guid, Stroke> m_inProgressStrokes;
private static List<PendingCommand> m_pendingCommands;
private static Dictionary<Guid, TaskCompletionSource<bool>> CommandAcknowledgments = new();
private static Dictionary<Guid, TaskCompletionSource<bool>> m_acknowledgments;

public void Awake()
{
m_inProgressStrokes = new();
m_pendingCommands = new();
m_acknowledgments = new();
}

public void Update()
Expand Down Expand Up @@ -139,6 +140,17 @@ private static bool CheckifCommandGuidIsInStack(Guid commandGuid)
return false;
}

private static bool CheckifStrokeGuidIsInMemory(Guid strokeGuid)
{

if (SketchMemoryScript.m_Instance.IsStrokeInMemory(strokeGuid))
{
//Debug.Log($"Stroke with Guid {strokeGuid} already in memory.");
return true;
}
return false;
}

private static BaseCommand FindParentCommand(Guid parentGuid)
{
PendingCommand pendingParent = m_pendingCommands.FirstOrDefault(x => x.Guid == parentGuid);
Expand Down Expand Up @@ -372,20 +384,20 @@ private static void SwitchEnvironment(Guid environmentGuid, Guid commandGuid, in
public static async Task<bool> WaitForAcknowledgment(Guid commandGuid, int timeoutMilliseconds = 1000)
{
var tcs = new TaskCompletionSource<bool>();
CommandAcknowledgments[commandGuid] = tcs;
m_acknowledgments[commandGuid] = tcs;

var timeoutTask = Task.Delay(timeoutMilliseconds);
var acknowledgmentTask = tcs.Task;
var completedTask = await Task.WhenAny(acknowledgmentTask, timeoutTask);

if (completedTask == acknowledgmentTask)
{
CommandAcknowledgments.Remove(commandGuid);
m_acknowledgments.Remove(commandGuid);
return await acknowledgmentTask;
}
else
{
CommandAcknowledgments.Remove(commandGuid);
m_acknowledgments.Remove(commandGuid);
return false;
}
}
Expand Down Expand Up @@ -551,16 +563,23 @@ public static void RPC_HistorySyncCompleted(NetworkRunner runner, [RpcTarget] Pl
public static void RPC_CheckCommand(NetworkRunner runner, Guid commandGuid, PlayerRef initiatorPlayer, [RpcTarget] PlayerRef targetPlayer)
{
bool isCommandInStack = CheckifCommandGuidIsInStack(commandGuid);
RPC_ConfirmCommand(runner, commandGuid, isCommandInStack, initiatorPlayer);
RPC_Confirm(runner, commandGuid, isCommandInStack, initiatorPlayer);
}

[Rpc(InvokeLocal = false)]
public static void RPC_CheckStroke(NetworkRunner runner, Guid strokeGuid, PlayerRef initiatorPlayer, [RpcTarget] PlayerRef targetPlayer)
{
bool isCommandInStack = CheckifStrokeGuidIsInMemory(strokeGuid);
RPC_Confirm(runner, strokeGuid, isCommandInStack, initiatorPlayer);
}

[Rpc(InvokeLocal = false)]
public static void RPC_ConfirmCommand(NetworkRunner runner, Guid commandGuid, bool isCommandInStack, [RpcTarget] PlayerRef targetPlayer)
public static void RPC_Confirm(NetworkRunner runner, Guid commandGuid, bool isCommandInStack, [RpcTarget] PlayerRef targetPlayer)
{
if (CommandAcknowledgments.TryGetValue(commandGuid, out var tcs))
if (m_acknowledgments.TryGetValue(commandGuid, out var tcs))
{
tcs.SetResult(isCommandInStack);
CommandAcknowledgments.Remove(commandGuid);
m_acknowledgments.Remove(commandGuid);
}
}

Expand Down
5 changes: 5 additions & 0 deletions Assets/Scripts/SketchMemoryScript.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,11 @@ public static List<Stroke> GetStrokesBetween(int start, int end)
return result;
}

public bool IsStrokeInMemory(Guid strokeGuid)
{
return m_MemoryList.Any(stroke => stroke.m_Guid == strokeGuid);
}

public bool IsCommandInStack(Guid commandGuid)
{
return IsCommandInOperationStack(commandGuid) ||
Expand Down
4 changes: 4 additions & 0 deletions Assets/Scripts/Stroke.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ public Stroke()
{
m_NodeByTime = new LinkedListNode<Stroke>(this);
m_PlaybackNode = new LinkedListNode<Stroke>(this);
m_Guid = Guid.NewGuid();
}

/// Clones the passed stroke into a new NotCreated stroke.
Expand All @@ -185,6 +186,9 @@ public Stroke(Stroke existing) : base(existing)
// And we can't use field initializers for the linked list creation.
m_NodeByTime = new LinkedListNode<Stroke>(this);
m_PlaybackNode = new LinkedListNode<Stroke>(this);

if (existing.m_Guid != null)
m_Guid = Guid.NewGuid();
}

/// Makes a copy of stroke, if one has not already been made.
Expand Down
1 change: 1 addition & 0 deletions Assets/Scripts/StrokeData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class StrokeData
// Not currently serialized.
public int m_Seed;
public SketchGroupTag m_Group = SketchGroupTag.None;
public Guid m_Guid;

// Reference the BrushStrokeCommand that created this stroke with a WeakReference.
// This allows the garbage collector to collect the BrushStrokeCommand if it's no
Expand Down

0 comments on commit 330e69e

Please sign in to comment.