Dedicated server: park mods as server-owned objects

- maps.park_mods binds a Park Editor park mod (Mods/<folder>/parks/<key>.park.json)
  to a name and the map it was built on; maps.park_limits caps the parks and
  objects spawned at once (3 and 512 by default)
- park-mod list | add <name> [<name>...] [minutes] | remove <name> [<name>...] | clear,
  from the console, as /park-mod by admins, and by players through custom votes
- A spawned park is the layout of the server's own SteamID and epoch, sent like a
  player's: games since 1.0.0 already show it, it counts against no player's
  object limit, nobody can move or delete it, and it stays when players leave
- Objects players' games would not take (not Build Kit items, broken values) are
  skipped and logged; a missing or broken park is an error line when spawning
- Optional expiry (1-1440 min) with a warning in chat a minute before; every
  park goes on a map change and none survive a restart
- [parks] log lines name who did it: console, the admin, or "a vote"; a vote's
  refusal reaches chat
- park-mod is reserved for custom commands; README.txt documents the settings,
  commands and a park/unpark vote

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2G4APEkhHgVs59RDL3XWD
This commit is contained in:
Robin MöllerandClaude Opus 5.5 committed 2026-10-11 17:45:49 +00:00
1 parent f4292d6b73
commit 6909fe3a2e
13 files changed
+517 -6

No files matched your search

+6 -1
View File
@@ -509,7 +509,12 @@ void Host::check_vote(bool expired) {
switch (done.kind) {
case VoteKind::map: log_(command("map " + done.value)); break;
case VoteKind::time: log_(command("tod " + done.value)); break;
case VoteKind::custom: log_(command(done.value)); break; // the owner's command, as the console
case VoteKind::custom: { // the owner's command, as the console
by_vote_ = true;
struct Reset { bool &flag; ~Reset() { flag = false; } } reset{by_vote_};
log_(command(done.value));
break;
}
case VoteKind::kick:
if (find(done.target)) {
kicked_.insert(done.target);