Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Runtime/Client/LootLockerHTTPClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ private void CallListenersAndMarkDone(LootLockerHTTPExecutionQueueItem execution

private IEnumerator RefreshSession(string refreshForPlayerUlid, string forExecutionItemId, Action<LootLockerSessionResponse, string, string> onSessionRefreshedCallback)
{
var playerData = LootLockerStateData.GetStateForPlayerOrDefaultStateOrEmpty(refreshForPlayerUlid);
var playerData = LootLockerStateData.GetPlayerDataForPlayerWithUlidWithoutChangingState(refreshForPlayerUlid);
if (playerData == null)
{
LootLockerLogger.Log($"No stored player data for player with ulid {refreshForPlayerUlid}. Can't refresh session.", LootLockerLogger.LogLevel.Warning);
Expand Down Expand Up @@ -863,6 +863,12 @@ private void HandleSessionRefreshResult(LootLockerResponse newSessionResponse, s
LootLockerLogger.Log($"Session refresh callback ulid {forPlayerWithUlid} does not match the execution item ulid {executionItem.RequestData.ForPlayerWithUlid}. Ignoring.", LootLockerLogger.LogLevel.Error);
return;
}
if (newSessionResponse == null || !newSessionResponse.success)
{
LootLockerLogger.Log($"Session refresh failed for player with ulid {forPlayerWithUlid}.", LootLockerLogger.LogLevel.Error);
return;
}

var playerData = LootLockerStateData.GetStateForPlayerOrDefaultStateOrEmpty(executionItem.RequestData.ForPlayerWithUlid);
string tokenBeforeRefresh = executionItem.RequestData.ExtraHeaders.TryGetValue("x-session-token", out var existingToken) ? existingToken : "";
string tokenAfterRefresh = playerData?.SessionToken;
Expand Down Expand Up @@ -923,19 +929,19 @@ private static bool IsAuthorizedAdminRequest(LootLockerHTTPExecutionQueueItem re

private static bool CanRefreshUsingRefreshToken(LootLockerHTTPRequestData cachedRequest)
{
var playerData = LootLockerStateData.GetStateForPlayerOrDefaultStateOrEmpty(cachedRequest.ForPlayerWithUlid);
var playerData = LootLockerStateData.GetPlayerDataForPlayerWithUlidWithoutChangingState(cachedRequest.ForPlayerWithUlid);
if (!LootLockerAuthPlatformSettings.PlatformsWithRefreshTokens.Contains(playerData == null ? LL_AuthPlatforms.None : playerData.CurrentPlatform.Platform))
{
return false;
}
// The failed request isn't a refresh session request but we have a refresh token stored, so try to refresh the session automatically before failing
string json = cachedRequest.Content.dataType == LootLockerHTTPRequestDataType.JSON ? ((LootLockerJsonBodyRequestContent)cachedRequest.Content).jsonBody : null;
return (string.IsNullOrEmpty(json) || !json.Contains("refresh_token")) && !string.IsNullOrEmpty(LootLockerStateData.GetStateForPlayerOrDefaultStateOrEmpty(cachedRequest.ForPlayerWithUlid)?.RefreshToken);
return (string.IsNullOrEmpty(json) || !json.Contains("refresh_token")) && !string.IsNullOrEmpty(playerData?.RefreshToken);
}

private static bool CanStartNewSessionUsingCachedAuthData(string forPlayerWithUlid)
{
var playerData = LootLockerStateData.GetStateForPlayerOrDefaultStateOrEmpty(forPlayerWithUlid);
var playerData = LootLockerStateData.GetPlayerDataForPlayerWithUlidWithoutChangingState(forPlayerWithUlid);
if (playerData == null)
{
return false;
Expand Down
72 changes: 33 additions & 39 deletions Runtime/Client/LootLockerPresenceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ public enum LootLockerPresenceConnectionState
Authenticating,
Active,
Reconnecting,
Failed
Failed,
Destroying,
Destroyed
}

#endregion
Expand Down Expand Up @@ -202,8 +204,6 @@ public class LootLockerPresenceClient : MonoBehaviour, IDisposable
private Coroutine pingCoroutine;
private Coroutine statusUpdateCoroutine;
private Coroutine webSocketListenerCoroutine;
private bool isDestroying = false;
private bool isDisposed = false;
private bool isClientInitiatedDisconnect = false; // Track if disconnect is expected (due to session end)
private LootLockerPresenceCallback pendingConnectionCallback; // Store callback until authentication completes

Expand Down Expand Up @@ -254,7 +254,13 @@ public class LootLockerPresenceClient : MonoBehaviour, IDisposable
/// <summary>
/// Get connection statistics including latency to LootLocker
/// </summary>
public LootLockerPresenceConnectionStats ConnectionStats => connectionStats;
public LootLockerPresenceConnectionStats ConnectionStats {
get {
connectionStats.connectionState = connectionState;
return connectionStats;
}
set { connectionStats = value; }
}

#endregion

Expand All @@ -271,7 +277,6 @@ private void Update()

private void OnDestroy()
{
isDestroying = true;
Dispose();
}

Expand All @@ -281,9 +286,10 @@ private void OnDestroy()
/// </summary>
public void Dispose()
{
if (isDisposed) return;

isDisposed = true;
if (connectionState == LootLockerPresenceConnectionState.Destroying || connectionState == LootLockerPresenceConnectionState.Destroyed) return;

ChangeConnectionState(LootLockerPresenceConnectionState.Destroying);

shouldReconnect = false;

StopCoroutines();
Expand All @@ -296,7 +302,7 @@ public void Dispose()
pendingPingTimestamps.Clear();
recentLatencies.Clear();

ChangeConnectionState(LootLockerPresenceConnectionState.Disconnected);
ChangeConnectionState(LootLockerPresenceConnectionState.Destroyed);
}

/// <summary>
Expand Down Expand Up @@ -383,9 +389,10 @@ internal void Initialize(string playerUlid, string sessionToken)
/// </summary>
internal void Connect(LootLockerPresenceCallback onComplete = null)
{
if (isDisposed)
if (connectionState == LootLockerPresenceConnectionState.Destroying ||
connectionState == LootLockerPresenceConnectionState.Destroyed)
{
onComplete?.Invoke(false, "Client has been disposed");
onComplete?.Invoke(false, "Client has been destroyed");
return;
}

Expand Down Expand Up @@ -415,17 +422,11 @@ internal void Connect(LootLockerPresenceCallback onComplete = null)
internal void Disconnect(LootLockerPresenceCallback onComplete = null)
{
// Prevent multiple disconnect attempts
if (isDestroying || isDisposed)
{
onComplete?.Invoke(true, null);
return;
}

// Check if already disconnected
if (connectionState == LootLockerPresenceConnectionState.Disconnected ||
if (connectionState == LootLockerPresenceConnectionState.Destroying ||
connectionState == LootLockerPresenceConnectionState.Destroyed ||
connectionState == LootLockerPresenceConnectionState.Disconnected ||
connectionState == LootLockerPresenceConnectionState.Failed)
{
LootLockerLogger.Log($"Presence client already in disconnected state: {connectionState}", LootLockerLogger.LogLevel.Debug);
onComplete?.Invoke(true, null);
return;
}
Expand Down Expand Up @@ -537,9 +538,10 @@ internal void SendPing(LootLockerPresenceCallback onComplete = null)

private IEnumerator ConnectCoroutine()
{
if (isDestroying || isDisposed)
if (connectionState == LootLockerPresenceConnectionState.Destroying ||
connectionState == LootLockerPresenceConnectionState.Destroyed)
{
HandleConnectionError("Presence client is destroying or disposed");
HandleConnectionError("Presence client is destroyed");
yield break;
}
if (string.IsNullOrEmpty(sessionToken))
Expand Down Expand Up @@ -646,7 +648,8 @@ private void HandleAuthenticationError(string errorMessage)
private IEnumerator DisconnectCoroutine(LootLockerPresenceCallback onComplete = null)
{
// Don't attempt disconnect if already destroyed
if (isDestroying || isDisposed)
if (connectionState == LootLockerPresenceConnectionState.Destroying ||
connectionState == LootLockerPresenceConnectionState.Destroyed)
{
onComplete?.Invoke(true, null);
yield break;
Expand Down Expand Up @@ -873,9 +876,9 @@ private IEnumerator ListenForMessagesCoroutine()
var receiveTask = webSocket.ReceiveAsync(new ArraySegment<byte>(buffer),
cancellationTokenSource.Token);

yield return new WaitUntil(() => receiveTask.IsCompleted || receiveTask.IsFaulted || isDestroying || isDisposed);
yield return new WaitUntil(() => receiveTask.IsCompleted || receiveTask.IsFaulted || connectionState == LootLockerPresenceConnectionState.Destroying || connectionState == LootLockerPresenceConnectionState.Destroyed);

if(isDestroying || isDisposed)
if(connectionState == LootLockerPresenceConnectionState.Destroying || connectionState == LootLockerPresenceConnectionState.Destroyed)
{
yield break;
}
Expand Down Expand Up @@ -1084,15 +1087,7 @@ private void ChangeConnectionState(LootLockerPresenceConnectionState newState, s
// Update connection stats with new state
connectionStats.connectionState = newState;

LootLockerLogger.Log($"Presence connection state changed: {previousState} -> {newState}", LootLockerLogger.LogLevel.Debug);

// Stop ping routine if we're no longer active
if (newState != LootLockerPresenceConnectionState.Active && pingCoroutine != null)
{
LootLockerLogger.Log("Stopping ping routine due to connection state change", LootLockerLogger.LogLevel.Debug);
StopCoroutine(pingCoroutine);
pingCoroutine = null;
}
LootLockerLogger.Log($"Presence state changed from {previousState} to {newState} for player {playerUlid}", LootLockerLogger.LogLevel.Debug);

// Then notify external systems via the unified event system
LootLockerEventSystem.TriggerPresenceConnectionStateChanged(playerUlid, previousState, newState, error);
Expand All @@ -1102,29 +1097,28 @@ private void ChangeConnectionState(LootLockerPresenceConnectionState newState, s
private IEnumerator PingCoroutine()
{

while (IsConnectedAndAuthenticated && !isDestroying)
while (IsConnectedAndAuthenticated)
{
SendPing();
yield return new WaitForSeconds(PING_INTERVAL);
}

LootLockerLogger.Log($"Ping routine ended. Connected: {IsConnectedAndAuthenticated}, Destroying: {isDestroying}", LootLockerLogger.LogLevel.Debug);
}

private IEnumerator ScheduleReconnectCoroutine(float customDelay = -1f)
{
if (!shouldReconnect || isDestroying || reconnectAttempts >= MAX_RECONNECT_ATTEMPTS)
if (!shouldReconnect || reconnectAttempts >= MAX_RECONNECT_ATTEMPTS)
{
yield break;
}

reconnectAttempts++;
float delayToUse = customDelay > 0 ? customDelay : RECONNECT_DELAY;
LootLockerLogger.Log($"Scheduling Presence reconnect attempt {reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS} in {delayToUse} seconds", LootLockerLogger.LogLevel.Debug);
ChangeConnectionState(LootLockerPresenceConnectionState.Reconnecting);

yield return new WaitForSeconds(delayToUse);

if (shouldReconnect && !isDestroying)
if (shouldReconnect && connectionState == LootLockerPresenceConnectionState.Reconnecting)
{
StartCoroutine(ConnectCoroutine());
}
Expand Down
13 changes: 10 additions & 3 deletions Runtime/Client/LootLockerPresenceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,14 @@ public static LootLockerPresenceManager Get()
void ILootLockerService.Initialize()
{
if (IsInitialized) return;

#if UNITY_EDITOR
_isEnabled = LootLockerConfig.current.enablePresence && LootLockerConfig.current.enablePresenceInEditor;
#else
_isEnabled = LootLockerConfig.current.enablePresence;
_autoConnectEnabled = LootLockerConfig.current.enablePresenceAutoConnect;
_autoDisconnectOnFocusChange = LootLockerConfig.current.enablePresenceAutoDisconnectOnFocusChange;
#endif
_autoConnectEnabled = LootLockerConfig.current.enablePresenceAutoConnect;
_autoDisconnectOnFocusChange = LootLockerConfig.current.enablePresenceAutoDisconnectOnFocusChange;

IsInitialized = true;
}
Expand Down Expand Up @@ -880,7 +885,9 @@ private void _DisconnectPresenceForUlid(string playerUlid, LootLockerPresenceCal
// Check connection state to prevent multiple disconnect attempts
var connectionState = client.ConnectionState;
if (connectionState == LootLockerPresenceConnectionState.Disconnected ||
connectionState == LootLockerPresenceConnectionState.Failed)
connectionState == LootLockerPresenceConnectionState.Failed ||
connectionState == LootLockerPresenceConnectionState.Destroying ||
connectionState == LootLockerPresenceConnectionState.Destroyed)
{
alreadyDisconnectedOrFailed = true;
}
Expand Down
10 changes: 10 additions & 0 deletions Runtime/Editor/ProjectSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,16 @@ private void DrawPresenceSettings()
}

EditorGUILayout.Space();

// Enable presence in editor toggle
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_CustomSettings.FindProperty("enablePresenceInEditor"), new GUIContent("Enable Presence in Editor"));
if (EditorGUI.EndChangeCheck())
{
gameSettings.enablePresenceInEditor = m_CustomSettings.FindProperty("enablePresenceInEditor").boolValue;
}

EditorGUILayout.Space();
}

EditorGUILayout.Space();
Expand Down
Loading
Loading