mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 15:08:47 -07:00
fix: push presence when a player comes back online
OnDisconnectedAsync told friends a player went offline, but nothing ever pushed the opposite transition — PlayerCameOnline existed in NotificationService with zero callers. The pair was asymmetric, so a SignalR reconnect (network blip, replica restart) greyed the player out on every friend's list for the rest of the session even though they were still playing. Only re-entering a room, which pushes presence via /goto, brought them back. AddConnectionAsync already computed the first-connection transition to maintain the online set but returned void; it now reports it, mirroring RemoveConnectionAsync. The push is deliberately conditional on a room already being known. The presence payload derives isOnline from roomInstance being non-null, so pushing with no room reads as OFFLINE — worse than staying quiet. On a fresh boot there is no room yet and the /goto that follows sends the real presence anyway, so this only fires on reconnects, where the cached room is exactly what friends need to see again.
This commit is contained in:
@@ -40,6 +40,7 @@ namespace DorkNet.Server.Hubs;
|
||||
[Authorize]
|
||||
public class NotifyHub(
|
||||
OnlinePresenceService presence,
|
||||
PlayerPresenceService playerRooms,
|
||||
NotificationService notifications,
|
||||
DorkNetDbContext db,
|
||||
ServerSettingsService serverSettings,
|
||||
@@ -95,10 +96,30 @@ public class NotifyHub(
|
||||
}
|
||||
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, GroupForPlayer(id));
|
||||
await presence.AddConnectionAsync(id, Context.ConnectionId);
|
||||
var cameOnline = await presence.AddConnectionAsync(id, Context.ConnectionId);
|
||||
logger.LogInformation(
|
||||
"[hub] connected player={PlayerId} conn={ConnectionId} group={Group}",
|
||||
id, Context.ConnectionId, GroupForPlayer(id));
|
||||
"[hub] connected player={PlayerId} conn={ConnectionId} group={Group} cameOnline={CameOnline}",
|
||||
id, Context.ConnectionId, GroupForPlayer(id), cameOnline);
|
||||
|
||||
// Tell friends they're back. OnDisconnectedAsync pushes
|
||||
// PlayerWentOffline, but nothing used to push the opposite
|
||||
// transition, so the pair was asymmetric: a SignalR reconnect
|
||||
// (network blip, replica restart) greyed the player out on every
|
||||
// friend's list for the rest of the session even though they were
|
||||
// still playing.
|
||||
//
|
||||
// Only push when a room is already known. The presence payload
|
||||
// derives isOnline from roomInstance being non-null, so a push with
|
||||
// no room reads as OFFLINE — worse than staying quiet. On a fresh
|
||||
// boot there is no room yet and the /goto that follows sends the
|
||||
// real presence anyway; this branch is for reconnects, where the
|
||||
// cached room is exactly what friends need to see again.
|
||||
if (cameOnline && playerRooms.GetRoom(id) is { } room)
|
||||
{
|
||||
var friendIds = await RelationshipQueries.EffectiveFriendIdsAsync(db, serverSettings, id);
|
||||
if (friendIds.Count > 0)
|
||||
await notifications.PlayerCameOnline(friendIds, id, room);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -45,7 +45,11 @@ public class OnlinePresenceService
|
||||
|
||||
public OnlinePresenceService(IConnectionMultiplexer? redis = null) => _redis = redis;
|
||||
|
||||
public async Task AddConnectionAsync(long playerId, string connectionId)
|
||||
/// <summary>Register one SignalR connection. Returns true when that was the
|
||||
/// player's FIRST active connection, i.e. they just came online and friends
|
||||
/// should be told. Mirrors <see cref="RemoveConnectionAsync"/>, which
|
||||
/// reports the opposite transition.</summary>
|
||||
public async Task<bool> AddConnectionAsync(long playerId, string connectionId)
|
||||
{
|
||||
if (_redis is { } mux)
|
||||
{
|
||||
@@ -59,13 +63,21 @@ public class OnlinePresenceService
|
||||
if (added)
|
||||
{
|
||||
var size = await db.SetLengthAsync(connsKey);
|
||||
if (size == 1) await db.SetAddAsync(OnlineSetKey, playerId);
|
||||
if (size == 1)
|
||||
{
|
||||
await db.SetAddAsync(OnlineSetKey, playerId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
var set = _localConns.GetOrAdd(playerId, _ => new HashSet<string>());
|
||||
lock (set) set.Add(connectionId);
|
||||
lock (set)
|
||||
{
|
||||
set.Add(connectionId);
|
||||
return set.Count == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Remove one SignalR connection. Returns true when that was
|
||||
|
||||
@@ -235,6 +235,7 @@ verified against the binary before the change, and the full test suite passes.
|
||||
| `SignalR push `Id` (notify)` | enum NAME sent for most ids; client only registered numbers — pushes vanished | numeric by default, with the six string-keyed ids listed explicitly |
|
||||
| `SignalR SubscriptionUpdateRoom` | sent as `SubscriptionUpdateRoom`; client listens on `RoomUpdate` | sent as `RoomUpdate` |
|
||||
| `Room ban notification` | sent id 24, for which the client has no handler | sent as ModerationKick (22) with IsBan |
|
||||
| `Presence on SignalR reconnect` | disconnect pushed offline; nothing ever pushed back online, so a reconnect greyed the player out for the rest of the session | connect re-pushes presence when a room is already known |
|
||||
|
||||
Everything else in the per-subsystem Defects sections below is still
|
||||
outstanding.
|
||||
|
||||
Reference in New Issue
Block a user