Reject a second live WebSocket for the same session name.
Two tabs with the cookie already set never POST /api/session, so name-online on login did not cover the design rule. Claim the name under the same lock as the online check. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7,6 +7,7 @@ namespace HSchool.Server.Net;
|
||||
internal sealed class ClientRegistry
|
||||
{
|
||||
private readonly ConcurrentDictionary<uint, GameClient> _clients = new();
|
||||
private readonly object _names = new();
|
||||
private uint _nextPlayerId;
|
||||
|
||||
public int Count => _clients.Count;
|
||||
@@ -28,14 +29,40 @@ internal sealed class ClientRegistry
|
||||
|
||||
public bool IsUserNameOnline(string normalizedUserName)
|
||||
{
|
||||
foreach (var client in _clients.Values)
|
||||
lock (_names)
|
||||
{
|
||||
if (client.UserName is null)
|
||||
return HasUserName(normalizedUserName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One live socket per name. Checked under the same lock as <see cref="IsUserNameOnline"/>
|
||||
/// so a second tab cannot sneak a Welcome in between the HTTP check and SetUserName.
|
||||
/// </summary>
|
||||
public bool TryClaimUserName(GameClient client, string userName)
|
||||
{
|
||||
lock (_names)
|
||||
{
|
||||
if (HasUserName(userName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
client.SetUserName(userName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasUserName(string normalizedUserName)
|
||||
{
|
||||
foreach (var existing in _clients.Values)
|
||||
{
|
||||
if (existing.UserName is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.Equals(client.NormalizedUserName, normalizedUserName, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(existing.NormalizedUserName, normalizedUserName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user