mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
fix: make room-details and chat-thread byte-identical to the client's readers
Both bugs were the same shape: we answered HTTP 200 with a key set the client's generated reader did not agree with, the reader threw mid-deserialise, and it surfaced later as a bare "Failed to copy room" / a chat that never opened, with no URL in the client log and no payload in the server log. Rather than guess at fields, tools/extract-client-dto-keys.py reads the readers out of the ISIL dump. Cpp2IL emits each generated reader's property names as string literals in registration order (three spellings per field: Pascal, camel, lower), so the extracted catalogue is the client's own contract. 156 reader types land in DorkNet.Server.Tests/data/client-dto-keys-2023.json, and ClientDtoParityTests asserts our real responses against them — nothing missing, nothing extra. Chat (EKEFFNBIOHJ) was missing exactly one key: LatestMessage. It was only emitted when a thread HAD messages, so it vanished from the one response where it was needed — the thread that thread/withmembers has just created, which by definition has none. It is now always present, null when there is nothing to report; a registered slot wants a value and an absent key is not one. Room details (GLEGPFFPDBE) was missing DataBlob, DataBlobHash, MaxPlayers, ToxmodEnabled and RankingContext, and sent 15 keys the reader has no slot for. The 15 are gone: UgcVersion, UgcSubVersion, MinUgcSubVersion, PublishState, PublishStateAvailability, BoostCount, ExcludeFromLists, IsJuniorCreated, IsRecRoomApproved, IsRoomLinkedToRecRoomStudio, IsStudioRoom, StudioSessionId, RestrictedCircuitsAllowListNames, AllowNewUsers, AllowsNewUsers. That deletion also removes a comment asserting the 2023 client reads UgcVersion to build its asset-bundle URL. It does not — the literal appears nowhere in the entire decompile, and neither do PublishState or BoostCount.
This commit is contained in:
@@ -2109,8 +2109,6 @@ public class RoomsController(
|
||||
SupportsQuest2 = true,
|
||||
SupportsMobile = room.SupportsMobile,
|
||||
SupportsJuniors = room.AllowsJuniors,
|
||||
AllowNewUsers = room.AllowNewUsers,
|
||||
AllowsNewUsers = room.AllowNewUsers,
|
||||
MinLevel = room.MinLevel,
|
||||
CreatedAt = (room.CreatedAt == default ? DateTime.UtcNow : room.CreatedAt)
|
||||
.ToString("yyyy-MM-ddTHH:mm:ssZ"),
|
||||
@@ -2122,39 +2120,13 @@ public class RoomsController(
|
||||
VisitCount = room.VisitCount,
|
||||
},
|
||||
IsDorm = room.IsDormRoom,
|
||||
IsStudioRoom = room.IsStudioRoom,
|
||||
IsRoomLinkedToRecRoomStudio = room.IsRoomLinkedToRecRoomStudio,
|
||||
StudioSessionId = room.StudioSessionId,
|
||||
CloningAllowed = room.CloningAllowed,
|
||||
DisableMicAutoMute = room.DisableMicAutoMute,
|
||||
DisableRoomComments = false,
|
||||
EncryptVoiceChat = false,
|
||||
LoadScreenLocked = false,
|
||||
// Studio/UGC version family. The 2023 client reads UgcVersion to
|
||||
// decide the asset-bundle version it requests
|
||||
// (unity_assets/{id}/{target}/{UgcVersion}) and PersistenceVersion
|
||||
// to recognise a baked Studio room at all. Matches the real
|
||||
// RecRocks room.json (UgcVersion=1, PersistenceVersion=41). For a
|
||||
// non-Studio room these stay at the legacy 0 so behaviour is
|
||||
// unchanged.
|
||||
PersistenceVersion = room.IsStudioRoom ? StudioPersistenceVersion : 0,
|
||||
UgcVersion = room.IsStudioRoom ? StudioUgcVersion : 0,
|
||||
UgcSubVersion = room.IsStudioRoom ? StudioPersistenceVersion : 0,
|
||||
MinUgcSubVersion = room.IsStudioRoom ? StudioPersistenceVersion : 0,
|
||||
IsDeveloperOwned = room.IsStudioRoom || HasRoomTag(room, "developer"),
|
||||
IsJuniorCreated = false,
|
||||
IsRecRoomApproved = false,
|
||||
ExcludeFromLists = false,
|
||||
BoostCount = 0,
|
||||
RestrictedCircuitsAllowListNames = Array.Empty<string>(),
|
||||
PublishStateAvailability = new
|
||||
{
|
||||
CanSaveAsBeta = false,
|
||||
CanSaveAsUpdate = true,
|
||||
AvailableUpdateTokenCount = 3,
|
||||
NextAvailableUpdateDateTimeUtc = (string?)null,
|
||||
},
|
||||
PublishState = 0,
|
||||
MaxPlayerCalculationMode = room.MaxPlayerCalculationMode,
|
||||
SubRooms = subRooms,
|
||||
Roles = wireRoles,
|
||||
@@ -2162,6 +2134,23 @@ public class RoomsController(
|
||||
PromoImages = JsonStringArrayOrEmpty(room.PromoImagesJson),
|
||||
PromoExternalContent = JsonArrayOrEmpty(room.PromoExternalContentJson),
|
||||
Tags = tags,
|
||||
// The client's room-details reader (GLEGPFFPDBE) registers these
|
||||
// five alongside the rest. A registered slot wants a value, and
|
||||
// leaving one absent is not the same as sending null — it is what
|
||||
// makes the reader throw mid-deserialise on a response the server
|
||||
// already answered 200 to. Kept last so the additions are visible
|
||||
// against the reader's own list; key ORDER does not matter to a
|
||||
// name-keyed reader, presence does.
|
||||
DataBlob = dataBlobName,
|
||||
DataBlobHash = (string?)null,
|
||||
// Room-level cap mirrors sub-room 0, which is where the client
|
||||
// reads it from for a single-sub-room room.
|
||||
MaxPlayers = sceneRows is { Count: > 0 } ? sceneRows[0].MaxPlayers : 8,
|
||||
// ToxMod is Rec Room's hosted voice moderation. There is no such
|
||||
// service here, so it is off — the client only uses this to decide
|
||||
// whether to show the indicator.
|
||||
ToxmodEnabled = false,
|
||||
RankingContext = (object?)null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user