Skip to content
Open
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
56 changes: 52 additions & 4 deletions Assets/PurrLobby/Editor/LobbyManagerEditor.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
Expand All @@ -9,11 +11,22 @@ namespace PurrLobby.Editor
[CustomEditor(typeof(LobbyManager))]
public class LobbyManagerEditor : UnityEditor.Editor
{
private bool showLobbyCodeEncoder = false;
private bool showCreateRoomArgs = false;
private bool showSearchRoomArgs = false;
private bool showEvents = false;
private bool showRoomStatus = true;
private Dictionary<string, bool> memberFoldouts = new Dictionary<string, bool>();
private string[] encoderNames;

private void OnEnable()
{
var encoderTypes = LobbyCode.GetEncoderTypes();
if (encoderTypes.Count > 0)
{
encoderNames = encoderTypes.Select(t => t.Name).ToArray();
}
}
Comment thread
ripercheto marked this conversation as resolved.

public override void OnInspectorGUI()
{
Expand All @@ -23,8 +36,12 @@ public override void OnInspectorGUI()

EditorGUILayout.Space();

DrawEncoderDropdown();

EditorGUILayout.Space();

DrawCreateRoomArgs();

EditorGUILayout.Space();

DrawSearchRoomArgs();
Expand All @@ -40,7 +57,7 @@ public override void OnInspectorGUI()

private void DrawProviderDropdown(LobbyManager lobbyManager)
{
var providers = FindObjectsByType<MonoBehaviour>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
var providers = FindObjectsByType<MonoBehaviour>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
var providerOptions = new List<MonoBehaviour>();
foreach (var provider in providers)
{
Expand Down Expand Up @@ -73,6 +90,37 @@ private void DrawProviderDropdown(LobbyManager lobbyManager)
}
}

private void DrawEncoderDropdown()
{
var serializedObject = new SerializedObject(target);
var lobbyCodeProp = serializedObject.FindProperty("lobbyCodeEncoderType");
if (lobbyCodeProp != null)
{
showLobbyCodeEncoder = EditorGUILayout.Foldout(showLobbyCodeEncoder, "Lobby Code Encoder", true);
if (showLobbyCodeEncoder)
{
EditorGUI.indentLevel++;
if (encoderNames.Length == 0)
{
EditorGUILayout.HelpBox($"No implementation of {nameof(IBaseEncoder)} found.", MessageType.Warning);
return;
}

EditorGUI.BeginChangeCheck();
var encoderSelectedIndex = Array.IndexOf(encoderNames, lobbyCodeProp.stringValue);
encoderSelectedIndex = EditorGUILayout.Popup("Type", encoderSelectedIndex, encoderNames);
if (EditorGUI.EndChangeCheck())
{
lobbyCodeProp.stringValue = encoderNames[encoderSelectedIndex];
serializedObject.ApplyModifiedProperties();
}
EditorGUI.indentLevel--;
}
}

serializedObject.ApplyModifiedProperties();
}
Comment thread
ripercheto marked this conversation as resolved.

private void DrawCreateRoomArgs()
{
var serializedObject = new SerializedObject(target);
Expand Down Expand Up @@ -159,13 +207,13 @@ private void DrawRoomStatus(LobbyManager lobbyManager)

if (!lobbyManager)
return;

var currentRoom = lobbyManager.CurrentLobby;

if (currentRoom.IsValid)
{
EditorGUILayout.LabelField("Room ID:", currentRoom.LobbyId);
if(!string.IsNullOrWhiteSpace(currentRoom.LobbyCode))
if (!string.IsNullOrWhiteSpace(currentRoom.LobbyCode))
{
EditorGUILayout.LabelField("Lobby Code:", currentRoom.LobbyCode);
}
Expand Down
1 change: 1 addition & 0 deletions Assets/PurrLobby/LobbyScenes/LobbySample.unity
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
currentProvider: {fileID: 1097043114}
lobbyCodeEncoderType: Base36Encoder
createRoomArgs:
maxPlayers: 5
roomProperties:
Expand Down
1 change: 1 addition & 0 deletions Assets/PurrLobby/Runtime/Lobby/Lobby.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public static Lobby Create(string name, string lobbyId, int maxPlayers, bool isO
Name = name,
IsValid = true,
LobbyId = lobbyId,
LobbyCode = LobbyCode.Encode(uint.Parse(lobbyId)),
Comment thread
ripercheto marked this conversation as resolved.
MaxPlayers = maxPlayers,
Properties = properties ?? new Dictionary<string, string>(),
IsOwner = isOwner,
Expand Down
48 changes: 48 additions & 0 deletions Assets/PurrLobby/Runtime/Lobby/LobbyCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace PurrLobby
{
public static class LobbyCode
{
private static IBaseEncoder encoder;
public static string Encode(ulong value)
{
if (encoder == null)
{
return value.ToString();
}
return encoder.Encode(value);
}
public static ulong Decode(string value)
{
if (encoder == null)
{
return ulong.Parse(value);
}
return encoder.Decode(value);
}
Comment thread
ripercheto marked this conversation as resolved.

public static List<Type> GetEncoderTypes() =>
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => typeof(IBaseEncoder).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
.ToList();

public static void AssignEncoder(string name)
{
if (string.IsNullOrEmpty(name))
{
return;
}
var types = GetEncoderTypes();
var type = types.FirstOrDefault(t => t.Name == name);
if (type == null)
{
return;
}
encoder = (IBaseEncoder)Activator.CreateInstance(type);
}
Comment thread
ripercheto marked this conversation as resolved.
}
}
2 changes: 2 additions & 0 deletions Assets/PurrLobby/Runtime/Lobby/LobbyCode.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions Assets/PurrLobby/Runtime/Lobby/LobbyManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace PurrLobby
public class LobbyManager : MonoBehaviour
{
[SerializeField] private MonoBehaviour currentProvider;
[SerializeField] private string lobbyCodeEncoderType;
Comment thread
ripercheto marked this conversation as resolved.
private ILobbyProvider _currentProvider;

private readonly Queue<Action> _delayedActions = new Queue<Action>();
Expand Down Expand Up @@ -64,6 +65,8 @@ private void Awake()
else
PurrLogger.LogWarning("No lobby provider assigned to LobbyManager.");

LobbyCode.AssignEncoder(lobbyCodeEncoderType);

SetupDataHolder();
}

Expand Down Expand Up @@ -268,6 +271,16 @@ public void LeaveLobby(string lobbyId)
OnRoomLeft?.Invoke();
});
}

/// <summary>
/// Join the lobby with the given lobby code
/// </summary>
/// <param name="lobbyCode">lobby code of the lobby to join</param>
public void JoinLobbyByCode(string lobbyCode)
{
var roomId = LobbyCode.Decode(lobbyCode);
JoinLobby(roomId.ToString());
}
Comment on lines +279 to +283

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

JoinLobbyByCode doesn't handle decode exceptions; invalid codes crash silently instead of emitting OnRoomJoinFailed.

LobbyCode.Decode throws ArgumentException for empty input and FormatException for invalid characters. Both are thrown synchronously before entering RunTask, so they propagate unhandled to the UI caller (e.g., JoinButton.JoinRoom). The method should mirror JoinLobby's existing validation pattern:

🐛 Proposed fix
 public void JoinLobbyByCode(string lobbyCode)
 {
-    var roomId = LobbyCode.Decode(lobbyCode);
-    JoinLobby(roomId.ToString());
+    if (string.IsNullOrEmpty(lobbyCode))
+    {
+        OnRoomJoinFailed?.Invoke("Lobby code is null or empty.");
+        return;
+    }
+    try
+    {
+        var roomId = LobbyCode.Decode(lobbyCode);
+        JoinLobby(roomId.ToString());
+    }
+    catch (Exception ex)
+    {
+        OnRoomJoinFailed?.Invoke($"Invalid lobby code: {ex.Message}");
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Assets/PurrLobby/Runtime/Lobby/LobbyManager.cs` around lines 279 - 283,
JoinLobbyByCode currently calls LobbyCode.Decode synchronously which can throw
ArgumentException or FormatException and crash the caller; update
JoinLobbyByCode to mirror JoinLobby's validation by wrapping the
LobbyCode.Decode call in a try/catch that catches ArgumentException and
FormatException, calls OnRoomJoinFailed with a clear reason (and returns)
instead of letting exceptions propagate, and only calls
JoinLobby(roomId.ToString()) when decode succeeds; reference: JoinLobbyByCode,
LobbyCode.Decode, OnRoomJoinFailed, JoinLobby (and caller JoinButton.JoinRoom)
to locate the relevant code paths.


/// <summary>
/// Join the lobby with the given ID
Expand Down
8 changes: 8 additions & 0 deletions Assets/PurrLobby/Runtime/Misc/Encoders.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 52 additions & 0 deletions Assets/PurrLobby/Runtime/Misc/Encoders/Base36Encoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;

namespace PurrLobby
{
public class Base36Encoder : IBaseEncoder
{
private const int MaxBase36Length = 13; // ceil(log36(ulong.MaxValue))
private const string Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

public string Encode(ulong value)
{
if (value == 0)
{
return "0";
}

var result = "";
while (value > 0)
{
result = Chars[(int)(value % 36)] + result;
value /= 36;
}
return result;
}

public ulong Decode(string value)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("Value cannot be null or empty.");
}

value = value.ToUpper().Trim();
if (value.Length > MaxBase36Length)
{
throw new FormatException($"Input exceeds maximum Base36 length ({MaxBase36Length}).");
}

ulong result = 0;
foreach (var c in value)
{
var digit = Chars.IndexOf(c);
if (digit < 0)
{
throw new FormatException($"Invalid Base36 character: {c}");
}
result = result * 36 + (ulong)digit;
}
return result;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
}
2 changes: 2 additions & 0 deletions Assets/PurrLobby/Runtime/Misc/Encoders/Base36Encoder.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 51 additions & 0 deletions Assets/PurrLobby/Runtime/Misc/Encoders/Base62Encoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;

namespace PurrLobby
{
public class Base62Encoder : IBaseEncoder
{
private const int MaxBase62Length = 11; // ceil(log62(ulong.MaxValue))
private const string Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

public string Encode(ulong value)
{
if (value == 0)
{
return "0";
}

var result = "";
while (value > 0)
{
result = Chars[(int)(value % 62)] + result;
value /= 62;
}
return result;
}

public ulong Decode(string value)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("Value cannot be null or empty.");
}

if (value.Length > MaxBase62Length)
{
throw new FormatException($"Input exceeds maximum Base62 length ({MaxBase62Length}).");
}

ulong result = 0;
foreach (var c in value)
{
var digit = Chars.IndexOf(c);
if (digit < 0)
{
throw new FormatException($"Invalid Base62 character: {c}");
}
result = result * 62 + (ulong)digit;
}
return result;
}
}
}
2 changes: 2 additions & 0 deletions Assets/PurrLobby/Runtime/Misc/Encoders/Base62Encoder.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Assets/PurrLobby/Runtime/Misc/Encoders/IBaseEncoder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace PurrLobby
{
public interface IBaseEncoder
{
public string Encode(ulong value);
public ulong Decode(string value);
}
}
2 changes: 2 additions & 0 deletions Assets/PurrLobby/Runtime/Misc/Encoders/IBaseEncoder.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Assets/PurrLobby/Runtime/Misc/UI/JoinButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void JoinRoom()
}

onStartJoin?.Invoke();
lobbyManager.JoinLobby(roomIdInput.text);
lobbyManager.JoinLobbyByCode(roomIdInput.text);
}
}
}
2 changes: 1 addition & 1 deletion Assets/PurrLobby/Runtime/Misc/UI/LobbyEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void Init(Lobby room, LobbyManager lobbyManager)

public void OnClick()
{
_lobbyManager.JoinLobby(_room.LobbyId);
_lobbyManager.JoinLobbyByCode(_room.LobbyCode);
}
}
}
4 changes: 4 additions & 0 deletions Assets/PurrLobby/Runtime/Providers/SteamLobbyProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public async Task<Lobby> CreateLobbyAsync(int maxPlayers, Dictionary<string, str
return LobbyFactory.Create(
lobbyName,
lobbyId.m_SteamID.ToString(),
LobbyCode.Encode(lobbyId.m_SteamID),
maxPlayers,
true,
GetLobbyUsers(lobbyId),
Expand Down Expand Up @@ -252,6 +253,7 @@ public async Task<Lobby> JoinLobbyAsync(string lobbyId)
var lobby = LobbyFactory.Create(
Steamworks.SteamMatchmaking.GetLobbyData(_currentLobby, "Name"),
lobbyId,
LobbyCode.Encode(cLobbyId.m_SteamID),
Steamworks.SteamMatchmaking.GetLobbyMemberLimit(_currentLobby),
false,
GetLobbyUsers(cLobbyId),
Expand Down Expand Up @@ -570,6 +572,7 @@ private void OnLobbyDataUpdate(Steamworks.LobbyDataUpdate_t callback)
var updatedLobby = LobbyFactory.Create(
Steamworks.SteamMatchmaking.GetLobbyData(_currentLobby, "Name"),
_currentLobby.m_SteamID.ToString(),
LobbyCode.Encode(_currentLobby.m_SteamID),
Steamworks.SteamMatchmaking.GetLobbyMemberLimit(_currentLobby),
isOwner,
updatedLobbyUsers,
Expand Down Expand Up @@ -640,6 +643,7 @@ private void OnLobbyChatUpdate(Steamworks.LobbyChatUpdate_t callback)
var updatedLobby = LobbyFactory.Create(
data,
_currentLobby.m_SteamID.ToString(),
LobbyCode.Encode(_currentLobby.m_SteamID),
Steamworks.SteamMatchmaking.GetLobbyMemberLimit(_currentLobby),
isOwner,
updatedLobbyUsers,
Expand Down
Loading