feat(admin): attach a real PersistedRoomData save to a baked RRO sub-room

POST rooms/{id}/subrooms/{sub}/blob/upload (multipart file, ?mode=full|
basics) rewrites a save from ANY client version for this room —
activity_id → the room's LocationReplicationId, output_log / sub_room_id /
unknown-to-2023 fields dropped, header versions clamped to the 2023
maxima; "basics" additionally keeps only the game-role (field 50) and
game-rule (40/41) objects and strips the circuit payloads, since a newer
CircuitsV2 graph is rejected by the client's own CV2 gate regardless —
stores it as room_{id}_upload_*.dat, records the RoomDataBlobs row and
points the sub-room (and room, for sub-room 0) at it. The CDN's RRO role
overlay + clamp then apply on serve exactly as for Rec Center's saved
blob. Admin SPA: "Upload save" file input + mode select on the Sub-rooms
tab for baked originals.

Purpose: A/B a genuine custom-Rec-Royale save (Squads chip room) against
the generated stub, whose four objects the client currently fails to
instantiate (NullReferenceException in CreationManager.InstantiateRequest-
ForViewData, then "Not object found for Persistence View").
Test: Uploading_a_real_save_attaches_it_to_the_room_rewritten_for_2023.
This commit is contained in:
Alexa
2026-09-28 00:16:51 +03:00
parent 806b160acc
commit d7b1722abd
6 changed files with 397 additions and 0 deletions
+16
View File
@@ -238,6 +238,22 @@ actions:
spawnable-tool table; a saved Squads chip is header + prefab id + tag +
node id + an empty wrapper payload), so the match logic exists on first
join without anyone spawning it by hand.
- **Upload save** — `POST api/admin/v1/rooms/{id}/subrooms/{subRoomId}/blob/upload?mode=full|basics`,
multipart `file` = a real `PersistedRoomData` save from ANY client
version (`.binpb` / `.room` / `.dat`, e.g. a RecNet export of a custom
Rec Royale room). The server rewrites it for this room
(`RoomDataBlobService.PrepareUploadedRoomBlob`): `activity_id` → the
room's `LocationReplicationId`, drops `output_log`, `sub_room_id` and
every field the 2023 schema doesn't know, clamps the header versions to
the 2023 maxima, and in `basics` mode keeps only the game-role (field 50)
and game-rule (fields 40/41) objects while stripping the circuit payloads
(`circuit_data`, connectable graph, `circuit_v2_data` — a newer
CircuitsV2 graph is rejected by the client's own CV2 version gate no
matter what). Stored under a fresh `room_{id}_upload_*.dat`, recorded in
`RoomDataBlobs`, and the sub-room (and room, for sub-room 0) is pointed
at it. On serve the CDN applies the RRO role overlay and clamp exactly
as for Rec Center's saved blob. Response carries a `summary`
(`viewsIn/viewsKept/roleViews/gameRuleViews/versionIn/versionOut`).
- **Reset to baked scene** — `POST api/admin/v1/rooms/{id}/subrooms/{subRoomId}/blob/reset`.
Clears both pointers. This is the escape hatch if a save left the room
unplayable. Saved blobs stay in `RoomDataBlobs`, so the in-game restore
+5
View File
@@ -190,6 +190,11 @@ Solos `983db81a68927f419edb0daf1243e93e`, Squads
Configuration chip, optional — `GameConfigurationTool` falls back to its
baked asset. The stub emits the wrapper chip for the two Rec Royale rooms.
Attaching a real save: `POST api/admin/v1/rooms/{id}/subrooms/{sub}/blob/upload`
(multipart `file`, `?mode=basics|full`) rewrites any-version
`PersistedRoomData` for the room — see `docs/admin.md` → "Upload save".
Used to A/B a generated stub against a genuine custom-Rec-Royale save.
Admin switch: `POST api/admin/v1/rooms/{id}/subrooms/{sub}/blob/enable`
stamps `RoomService.SyntheticDefaultRoomDataBlobName(id)` on the scene and
room; `.../blob/reset` clears it. See `docs/admin.md` → "RRO room data