From 6605f61047599a8ba3676580737e493a10f2d13b Mon Sep 17 00:00:00 2001 From: Alexa <53471801+Alexa-RR@users.noreply.github.com> Date: Mon, 28 Sep 2026 00:34:56 +0300 Subject: [PATCH] feat(admin): IsAGRoom (IsRRO) toggle on room props, for the editable-Rec-Royale experiment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Maker Pen permission check runs a room precondition that looks the current room up in AGRoomRuntimeConfig — a table baked into the client's resources.assets — and returns a per-original boolean. Rec Center and Stunt Runner allow creation there; Rec Royale does not, so no blob, role or server response can grant the pen while the client treats the room as that original. The table is consulted only for rooms with IsRRO (our IsAGRoom) set. Expose IsAGRoom on rooms/{id}/props (+ a checkbox on the General tab) so a CLONE of Rec Royale can be flipped to a non-original with a real save attached; whether the client still loads the baked Frontier scene for a non-original is what that experiment answers. docs/admin.md explains the gate and the two possible outcomes. --- .../Controllers/Admin/AdminController.cs | 17 ++++++++++++++++- DorkNet.Server/admin-ui/src/pages/Room.tsx | 11 +++++++++++ docs/admin.md | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/DorkNet.Server/Controllers/Admin/AdminController.cs b/DorkNet.Server/Controllers/Admin/AdminController.cs index 298bde0..f95e432 100644 --- a/DorkNet.Server/Controllers/Admin/AdminController.cs +++ b/DorkNet.Server/Controllers/Admin/AdminController.cs @@ -975,7 +975,8 @@ public class AdminController( double? HotScore, int? State, string? ImageName, - int? MaxCapacity); + int? MaxCapacity, + bool? IsAGRoom); /// POST api/admin/v1/rooms/{id}/props — partial /// update of room properties. Only the non-null fields in the @@ -1005,6 +1006,20 @@ public class AdminController( room.MaxCapacity = Math.Clamp(cap, 1, 80); changes.Add($"maxCapacity={room.MaxCapacity}"); } + if (body.IsAGRoom is bool ag) + { + // IsAGRoom is what the client reads as IsRRO. True: load the + // baked scene from the client's own assets AND consult the + // client-baked AGRoomRuntimeConfig table (which, per original, + // decides e.g. whether creation is allowed — Rec Center yes, Rec + // Royale no). False: treat the room as a custom room (roles and + // blob decide everything). Flipping a CLONE of a baked original + // to false is the server-side experiment for "editable Rec + // Royale"; whether the client still loads the baked scene for a + // non-original is what that experiment answers. + room.IsAGRoom = ag; + changes.Add($"isAGRoom={ag}"); + } if (changes.Count == 0) return Ok(new { room.Id, unchanged = true }); diff --git a/DorkNet.Server/admin-ui/src/pages/Room.tsx b/DorkNet.Server/admin-ui/src/pages/Room.tsx index 395ddf8..4a6eae7 100644 --- a/DorkNet.Server/admin-ui/src/pages/Room.tsx +++ b/DorkNet.Server/admin-ui/src/pages/Room.tsx @@ -165,6 +165,7 @@ function GeneralTab({ room, onSaved }: { room: RoomDetail; onSaved: () => void } const [description, setDescription] = useState(room.description); const [accessibility, setAccessibility] = useState(room.accessibility); const [cloningAllowed, setCloningAllowed] = useState(room.cloningAllowed); + const [isAGRoom, setIsAGRoom] = useState(room.isAGRoom); const [tagsCsv, setTagsCsv] = useState(room.tagsCsv); const [hotScore, setHotScore] = useState(String(room.hotScore)); const [imageName, setImageName] = useState(room.imageName); @@ -176,6 +177,7 @@ function GeneralTab({ room, onSaved }: { room: RoomDetail; onSaved: () => void } setDescription(room.description); setAccessibility(room.accessibility); setCloningAllowed(room.cloningAllowed); + setIsAGRoom(room.isAGRoom); setTagsCsv(room.tagsCsv); setHotScore(String(room.hotScore)); setImageName(room.imageName); @@ -193,6 +195,7 @@ function GeneralTab({ room, onSaved }: { room: RoomDetail; onSaved: () => void } Description: description, Accessibility: accessibility, CloningAllowed: cloningAllowed, + IsAGRoom: isAGRoom, TagsCsv: tagsCsv, HotScore: Number.isFinite(hot) ? hot : undefined, ImageName: imageName, @@ -283,6 +286,14 @@ function GeneralTab({ room, onSaved }: { room: RoomDetail; onSaved: () => void } /> Cloning allowed +