From e054d1f4e4a9db34aaf5f09033b2c691bc82745d Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Wed, 30 Sep 2026 02:44:16 -0500 Subject: [PATCH] feat(gm): /reloadworld with the world_reload permission (GM 9) Co-Authored-By: Claude Opus 5.5 --- dCommon/Permissions.cpp | 1 + dGame/dUtilities/SlashCommandHandler.cpp | 39 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/dCommon/Permissions.cpp b/dCommon/Permissions.cpp index a4c5571b9..d3f6197c8 100644 --- a/dCommon/Permissions.cpp +++ b/dCommon/Permissions.cpp @@ -111,6 +111,7 @@ namespace { { "challenges_manage", "Server", "Community challenges", "Create, change, cancel and delete server-wide challenges and their rewards (rewards are mailed to everyone who took part)", 8 }, { "events_manage", "Server", "Scheduled events: features", "Scheduled events that switch game features (event_1..event_8) on and off", 8 }, { "cdclient_reload", "Server", "Reload CDClient", "Make every server read the client's cdclient.fdb again after it changed (master also does this by itself)", 9 }, + { "world_reload", "Server", "Reload worlds", "See the zone files each running world loaded and whether they changed on disk, and replace a zone's instances with ones on the files on disk now (players are moved over; master also does this by itself when a file changes)", 9 }, { "server_live_update", "Server", "Live updates", "Move every server and world instance onto a new build without a restart (players see a short loading screen), and cancel one", 9 }, { "instances_manage", "Server", "Instance limits", "Change players per instance and spare instances per zone (the master server applies them)", 9 }, { "backups", "Server", "Database backups", "Make, download and delete database backups (downloads also need your password)", 9 }, diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 2ec5fee20..e9ea349a1 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -10,6 +10,7 @@ #include "ChatPackets.h" #include "MasterPackets.h" #include "master/CDClientReload.h" +#include "master/WorldFiles.h" #include "WorldMigration.h" #include @@ -1830,6 +1831,44 @@ void SlashCommandHandler::Startup() { .dashboardPermission = "cdclient_reload" }); + RegisterCommand({ + .help = "Replaces every instance of a zone with one on the zone files on disk now", + .info = "Master starts a new instance of the zone (properties keep their clone, private instances their password) that loads the .luz, .lvl, triggers, terrain and navmesh on disk now, moves the players over (the Mythran shift, or the seamless mode with world_reload_seamless=1) and stops the old instances; empty ones are just stopped. Master also does this by itself a few seconds after a file a world loaded changes (world_watch_seconds). Usage: /reloadworld [zone, default this one] [warn seconds, 0-300, default 10]", + .aliases = {"reloadworld"}, + .handle = [](Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto words = GeneralUtils::SplitString(args, ' '); + WorldReloadRequest request; + request.zoneId = Game::server->GetZoneID(); + if (!words.empty() && !words[0].empty()) { + const auto zone = GeneralUtils::TryParse(words[0]); + if (!zone || *zone == 0) { + ChatPackets::SendSystemMessage(sysAddr, u"Usage: /reloadworld [zone] [warn seconds, 0-300]"); + return; + } + request.zoneId = *zone; + } + if (words.size() > 1) { + const auto warn = GeneralUtils::TryParse(words[1]); + if (!warn || *warn > InstanceMigrationRequest::MAX_WARN_SECONDS) { + ChatPackets::SendSystemMessage(sysAddr, u"Usage: /reloadworld [zone] [warn seconds, 0-300]"); + return; + } + request.warnSeconds = *warn; + } + if (request.zoneId == 0) { + ChatPackets::SendSystemMessage(sysAddr, u"Character selection loads no zone; name one: /reloadworld "); + return; + } + request.requesterId = entity->GetObjectID(); + if (auto* character = entity->GetCharacter()) request.requestedBy = character->GetName(); + MasterPackets::SendToMaster(request); + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16("Asked master to reload zone " + std::to_string(request.zoneId) + + "; you are told how each instance's move goes, and the server log says what was stopped.")); + }, + .requiredLevel = eGameMasterLevel::OPERATOR, + .dashboardPermission = "world_reload" + }); + RegisterCommand({ .help = "[claim] Community challenges and live events", .info = "Shows the server-wide community challenges running now, how far along they are and what you added, and the live events in this world. Also gives you the coins waiting from challenges you helped complete",