feat: Implement adjustable room capacity and quest team size

- Added MaxCapacity property to RoomEntity and updated related controllers to utilize it.
- Modified RoomsController to set MaxPlayers based on room's MaxCapacity.
- Introduced QuestTeamSize class to dynamically adjust quest party sizes beyond the default limit.
- Updated DevMenu to include options for debugging and managing room settings.
- Created UI elements for adjusting MaxCapacity in the admin interface.
- Added configuration file for DevMenu with default settings.
This commit is contained in:
Alexa
2026-06-08 01:27:30 +03:00
parent 64746cdaa2
commit 482c73e1bc
15 changed files with 661 additions and 57 deletions
+1
View File
@@ -142,6 +142,7 @@ export interface RoomDetail {
supportsWalkVR: boolean;
supportsTeleportVR: boolean;
allowsJuniors: boolean;
maxCapacity: number;
disableMicAutoMute: boolean;
roomWarningMask: number;
customRoomWarning: string;
@@ -166,6 +166,7 @@ function GeneralTab({ room, onSaved }: { room: RoomDetail; onSaved: () => void }
const [tagsCsv, setTagsCsv] = useState(room.tagsCsv);
const [hotScore, setHotScore] = useState(String(room.hotScore));
const [imageName, setImageName] = useState(room.imageName);
const [maxCapacity, setMaxCapacity] = useState(String(room.maxCapacity ?? 8));
const [busy, setBusy] = useState(false);
// Re-sync local state if a refresh brings new server values in.
@@ -176,12 +177,14 @@ function GeneralTab({ room, onSaved }: { room: RoomDetail; onSaved: () => void }
setTagsCsv(room.tagsCsv);
setHotScore(String(room.hotScore));
setImageName(room.imageName);
setMaxCapacity(String(room.maxCapacity ?? 8));
}, [room.id, room.updatedAt]);
const save = async () => {
setBusy(true);
try {
const hot = Number(hotScore);
const cap = parseInt(maxCapacity, 10);
await api(`/rooms/${room.id}/props`, {
method: 'POST',
body: {
@@ -191,6 +194,7 @@ function GeneralTab({ room, onSaved }: { room: RoomDetail; onSaved: () => void }
TagsCsv: tagsCsv,
HotScore: Number.isFinite(hot) ? hot : undefined,
ImageName: imageName,
MaxCapacity: Number.isFinite(cap) ? cap : undefined,
},
});
toast.push('Saved', 'success');
@@ -236,6 +240,20 @@ function GeneralTab({ room, onSaved }: { room: RoomDetail; onSaved: () => void }
className="input tabular-nums"
/>
</label>
<label className="flex flex-col gap-1">
<span className="label">Max players (capacity)</span>
<input
type="number"
min={1}
max={80}
value={maxCapacity}
onChange={e => setMaxCapacity(e.target.value)}
className="input tabular-nums"
/>
<span className="text-[11px] text-ink-500">
Advertised cap on the 2020 client (not hard-enforced yet). 1–80.
</span>
</label>
<label className="flex flex-col gap-1">
<span className="label">Tags (comma-separated)</span>
<input