mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
fix: presence, profile edits, outfit menu and RecNetResult writes for the 2023 client
Every change below was checked against the 2023-03-21 client disassembly;
file:line evidence is in the code comments and in
docs/recroom-2023-client-api-complete.md.
Presence
- matchmake/none means "online, in no room" (its only caller is
TryGoToOfflineRoom, which ignores the reply). The server used to keep the
old room or invent a dorm, so the next heartbeat looked out of sync and
the client cancelled its load to chase that room.
- The join watchdog no longer clears presence 30 s into a long room load;
it is a 3 minute backstop that drops the room only.
- One fan-out (PresenceBroadcastService) for every transition: deferred
joins, matchmake/none, logout, failed joins, visibility changes, SignalR
connect/disconnect. isOnline is sent on its own; pushes never go to the
subject, because a self push moves the player.
- Online-status visibility (Public / Friends / Favorites / Appear offline)
is stored and honoured by GET /player and by the live pushes.
Profile
- PUT account/me/{emoji,personalpronouns,identityflags,bannerimage} were
404; added with the client's real form keys and persisted (new Players
columns plus migration). Every account read now returns them, and edits
push AccountUpdate to everyone.
Outfit menu
- Saved outfits kept OutfitSelectionsV2 and CustomAvatarItems, so loading a
slot restores what was saved instead of a stale outfit.
- isCreationAllowedForAccount answers the {Success, Error} envelope the
client parses; minPriceForPublicItem is a bare int; gifts/generate accepts
form bodies; lockeditems returns the full owned-item shape; colourways no
longer share their base item's AvatarItemId.
RecNetResult writes
- About 40 writes look status-only but parse the body as {Success, Error}.
An empty 200 showed "Failed to ..." after the server applied the change,
and a body without Success threw "Error message cannot be null". Clubs,
announcements, keepsakes, comments/read, subscriptions, purchase offers
and privacy settings now answer RecNetResult.
- Login token and device-code grants are real; remoteauth approves the
device code; createlogintoken returns a bare string.
- New: incentivizedreferrals/referrals, account/bulk/{email,phone},
GET strings, account/me/confirmphone, PlayMenuCarouselOrdering_1.
Route inventory and coverage test extended to match.
README now describes the 2023 branch.
This commit is contained in:
@@ -10,9 +10,27 @@ namespace DorkNet.Server.Services;
|
||||
public sealed class JoinTimeoutService(
|
||||
PlayerPresenceService presence,
|
||||
NotificationService notifications,
|
||||
PresenceBroadcastService fanout,
|
||||
ILogger<JoinTimeoutService> logger)
|
||||
{
|
||||
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);
|
||||
/// <summary>
|
||||
/// How long a join may stay unreported before the server gives up on it.
|
||||
///
|
||||
/// This used to be 30 seconds, which is shorter than a real 2023 room
|
||||
/// load: the client reports its join result only after the room's assets
|
||||
/// have downloaded and the player has spawned, and a large maker room
|
||||
/// regularly takes longer than that (in the field: matchmake/room at
|
||||
/// 09:16:35, Photon joined at 09:16:41, the load still running at 09:17:33).
|
||||
/// The watchdog then cleared presence mid-load, the next heartbeat found
|
||||
/// no room and answered with the dorm, and the client logged "presence
|
||||
/// heartbeat response indicates local presence is out-of-sync" and
|
||||
/// cancelled the load ("OnPlayerPresenceUpdate is cancelling room load").
|
||||
///
|
||||
/// The 2023 client reports its own failures (reportjoinresult with
|
||||
/// FailedTo*) and runs its fallback chain, so this is only a backstop for
|
||||
/// a client that silently stopped mid-join.
|
||||
/// </summary>
|
||||
public static TimeSpan Timeout { get; } = TimeSpan.FromMinutes(3);
|
||||
private readonly ConcurrentDictionary<long, PendingJoin> _pending = new();
|
||||
|
||||
public void MarkPending(long playerId, RoomInstanceDto? room, bool deferPresenceCommit = false)
|
||||
@@ -40,6 +58,10 @@ public sealed class JoinTimeoutService(
|
||||
_ = WatchAsync(playerId, pending);
|
||||
}
|
||||
|
||||
/// <summary>Drop the player's pending join without touching presence —
|
||||
/// the client abandoned it (matchmake/none, logout).</summary>
|
||||
public void Cancel(long playerId, string reason) => ClearForPlayer(playerId, reason);
|
||||
|
||||
public (RoomInstanceDto TargetRoom, bool DeferPresenceCommit)? GetPending(long playerId)
|
||||
{
|
||||
if (!_pending.TryGetValue(playerId, out var pending)) return null;
|
||||
@@ -75,11 +97,15 @@ public sealed class JoinTimeoutService(
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
presence.Clear(playerId);
|
||||
// Room only: the player is still online and about to run the
|
||||
// client's fallback chain. A full Clear also dropped the activity
|
||||
// marker, so friends saw them go offline after every failed join.
|
||||
presence.ClearRoom(playerId);
|
||||
logger.LogWarning(
|
||||
"[join-watchdog] cleared failed join player={PlayerId} room={RoomId}/{RoomName} instance={InstanceId} outcome={Outcome}",
|
||||
playerId, failedRoom.RoomId, failedRoom.Name, instanceId, outcome);
|
||||
return Task.CompletedTask;
|
||||
// Whatever room friends were last told about is gone; correct them.
|
||||
return fanout.PublishCurrentAsync(playerId);
|
||||
}
|
||||
|
||||
private async Task WatchAsync(long playerId, PendingJoin pending)
|
||||
@@ -110,8 +136,9 @@ public sealed class JoinTimeoutService(
|
||||
return;
|
||||
}
|
||||
|
||||
presence.Clear(playerId);
|
||||
presence.ClearRoom(playerId);
|
||||
await notifications.KickPlayerAsync(playerId, "Room join timed out. Returning to dorm.");
|
||||
await fanout.PublishCurrentAsync(playerId);
|
||||
logger.LogWarning(
|
||||
"[join-watchdog] timed out player={PlayerId} room={RoomId}/{RoomName} instance={InstanceId}",
|
||||
playerId, pending.TargetRoom.RoomId, pending.TargetRoom.Name, pending.InstanceId);
|
||||
|
||||
Reference in New Issue
Block a user