/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by the * Free Software Foundation; either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ /* ScriptData Name: server_commandscript %Complete: 100 Comment: All server related commands Category: commandscripts EndScriptData */ #include "AvgDiffTracker.h" #include "Chat.h" #include "Config.h" #include "GitRevision.h" #include "Language.h" #include "MySQLThreading.h" #include "Player.h" #include "Realm.h" #include "ScriptMgr.h" #include "ServerMotd.h" #include "StringConvert.h" #include "VMapFactory.h" #include "VMapMgr2.h" #include #include #include #include #include #if AC_COMPILER == AC_COMPILER_GNU #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif using namespace Acore::ChatCommands; class server_commandscript : public CommandScript { public: server_commandscript() : CommandScript("server_commandscript") { } ChatCommandTable GetCommands() const override { static ChatCommandTable serverIdleRestartCommandTable = { { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" }, { "", SEC_CONSOLE, true, &HandleServerIdleRestartCommand, "" } }; static ChatCommandTable serverIdleShutdownCommandTable = { { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" }, { "", SEC_CONSOLE, true, &HandleServerIdleShutDownCommand, "" } }; static ChatCommandTable serverRestartCommandTable = { { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" }, { "", SEC_ADMINISTRATOR, true, &HandleServerRestartCommand, "" } }; static ChatCommandTable serverShutdownCommandTable = { { "cancel", SEC_ADMINISTRATOR, true, &HandleServerShutDownCancelCommand, "" }, { "", SEC_ADMINISTRATOR, true, &HandleServerShutDownCommand, "" } }; static ChatCommandTable serverSetCommandTable = { { "difftime", SEC_CONSOLE, true, &HandleServerSetDiffTimeCommand, "" }, { "loglevel", SEC_CONSOLE, true, &HandleServerSetLogLevelCommand, "" }, { "motd", SEC_ADMINISTRATOR, true, &HandleServerSetMotdCommand, "" }, { "closed", SEC_CONSOLE, true, &HandleServerSetClosedCommand, "" } }; static ChatCommandTable serverCommandTable = { { "corpses", SEC_GAMEMASTER, true, &HandleServerCorpsesCommand, "" }, { "debug", SEC_ADMINISTRATOR, true, &HandleServerDebugCommand, "" }, { "exit", SEC_CONSOLE, true, &HandleServerExitCommand, "" }, { "idlerestart", SEC_CONSOLE, true, nullptr, "", serverIdleRestartCommandTable }, { "idleshutdown", SEC_CONSOLE, true, nullptr, "", serverIdleShutdownCommandTable }, { "info", SEC_PLAYER, true, &HandleServerInfoCommand, "" }, { "motd", SEC_PLAYER, true, &HandleServerMotdCommand, "" }, { "restart", SEC_ADMINISTRATOR, true, nullptr, "", serverRestartCommandTable }, { "shutdown", SEC_ADMINISTRATOR, true, nullptr, "", serverShutdownCommandTable }, { "set", SEC_ADMINISTRATOR, true, nullptr, "", serverSetCommandTable } }; static ChatCommandTable commandTable = { { "server", SEC_PLAYER, true, nullptr, "", serverCommandTable } }; return commandTable; } // Triggering corpses expire check in world static bool HandleServerCorpsesCommand(ChatHandler* /*handler*/, char const* /*args*/) { sWorld->RemoveOldCorpses(); return true; } static bool HandleServerDebugCommand(ChatHandler* handler, char const* /*args*/) { uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD)); std::string dbPortOutput; { uint16 dbPort = 0; if (QueryResult res = LoginDatabase.PQuery("SELECT port FROM realmlist WHERE id = %u", realm.Id.Realm)) dbPort = (*res)[0].GetUInt16(); if (dbPort) dbPortOutput = Acore::StringFormat("Realmlist (Realm Id: %u) configured in port %" PRIu16, realm.Id.Realm, dbPort); else dbPortOutput = Acore::StringFormat("Realm Id: %u not found in `realmlist` table. Please check your setup", realm.Id.Realm); } handler->PSendSysMessage("%s", GitRevision::GetFullVersion()); handler->PSendSysMessage("Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); handler->PSendSysMessage("Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); handler->PSendSysMessage("Using MySQL version: %u", MySQL::GetLibraryVersion()); handler->PSendSysMessage("Using CMake version: %s", GitRevision::GetCMakeVersion()); handler->PSendSysMessage("Compiled on: %s", GitRevision::GetHostOSVersion()); handler->PSendSysMessage("Worldserver listening connections on port %" PRIu16, worldPort); handler->PSendSysMessage("%s", dbPortOutput.c_str()); bool vmapIndoorCheck = sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK); bool vmapLOSCheck = VMAP::VMapFactory::createOrGetVMapMgr()->isLineOfSightCalcEnabled(); bool vmapHeightCheck = VMAP::VMapFactory::createOrGetVMapMgr()->isHeightCalcEnabled(); bool mmapEnabled = sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS); std::string dataDir = sWorld->GetDataPath(); std::vector subDirs; subDirs.emplace_back("maps"); if (vmapIndoorCheck || vmapLOSCheck || vmapHeightCheck) { handler->PSendSysMessage("VMAPs status: Enabled. LineOfSight: %i, getHeight: %i, indoorCheck: %i", vmapLOSCheck, vmapHeightCheck, vmapIndoorCheck); subDirs.emplace_back("vmaps"); } else handler->SendSysMessage("VMAPs status: Disabled"); if (mmapEnabled) { handler->SendSysMessage("MMAPs status: Enabled"); subDirs.emplace_back("mmaps"); } else handler->SendSysMessage("MMAPs status: Disabled"); for (std::string const& subDir : subDirs) { std::filesystem::path mapPath(dataDir); mapPath /= subDir; if (!std::filesystem::exists(mapPath)) { handler->PSendSysMessage("%s directory doesn't exist!. Using path: %s", subDir.c_str(), mapPath.generic_string().c_str()); continue; } auto end = std::filesystem::directory_iterator(); std::size_t folderSize = std::accumulate(std::filesystem::directory_iterator(mapPath), end, std::size_t(0), [](std::size_t val, std::filesystem::path const& mapFile) { if (std::filesystem::is_regular_file(mapFile)) val += std::filesystem::file_size(mapFile); return val; }); handler->PSendSysMessage("%s directory located in %s. Total size: " SZFMTD " bytes", subDir.c_str(), mapPath.generic_string().c_str(), folderSize); } LocaleConstant defaultLocale = sWorld->GetDefaultDbcLocale(); uint32 availableLocalesMask = (1 << defaultLocale); for (uint8 i = 0; i < TOTAL_LOCALES; ++i) { LocaleConstant locale = static_cast(i); if (locale == defaultLocale) continue; if (sWorld->GetAvailableDbcLocale(locale) != defaultLocale) availableLocalesMask |= (1 << locale); } std::string availableLocales; for (uint8 i = 0; i < TOTAL_LOCALES; ++i) { if (!(availableLocalesMask & (1 << i))) continue; availableLocales += localeNames[i]; if (i != TOTAL_LOCALES - 1) availableLocales += " "; } handler->PSendSysMessage("Using %s DBC Locale as default. All available DBC locales: %s", localeNames[defaultLocale], availableLocales.c_str()); handler->PSendSysMessage("Using World DB: %s", sWorld->GetDBVersion()); handler->PSendSysMessage("Using World DB Revision: %s", sWorld->GetWorldDBRevision()); handler->PSendSysMessage("Using Character DB Revision: %s", sWorld->GetCharacterDBRevision()); handler->PSendSysMessage("Using Auth DB Revision: %s", sWorld->GetAuthDBRevision()); return true; } static bool HandleServerInfoCommand(ChatHandler* handler, char const* /*args*/) { std::string realmName = sWorld->GetRealmName(); uint32 playerCount = sWorld->GetPlayerCount(); uint32 activeSessionCount = sWorld->GetActiveSessionCount(); uint32 queuedSessionCount = sWorld->GetQueuedSessionCount(); uint32 connPeak = sWorld->GetMaxActiveSessionCount(); std::string uptime = secsToTimeString(sWorld->GetUptime()).append("."); uint32 updateTime = sWorld->GetUpdateTime(); uint32 avgUpdateTime = avgDiffTracker.getAverage(); handler->PSendSysMessage("%s", GitRevision::GetFullVersion()); if (!queuedSessionCount) handler->PSendSysMessage("Connected players: %u. Characters in world: %u.", activeSessionCount, playerCount); else handler->PSendSysMessage("Connected players: %u. Characters in world: %u. Queue: %u.", activeSessionCount, playerCount, queuedSessionCount); handler->PSendSysMessage("Connection peak: %u.", connPeak); handler->PSendSysMessage(LANG_UPTIME, uptime.c_str()); handler->PSendSysMessage("Update time diff: %ums, average: %ums.", updateTime, avgUpdateTime); if (handler->GetSession()) if (Player* p = handler->GetSession()->GetPlayer()) if (p->IsDeveloper()) handler->PSendSysMessage("DEV wavg: %ums, nsmax: %ums, nsavg: %ums. LFG avg: %ums, max: %ums.", avgDiffTracker.getTimeWeightedAverage(), devDiffTracker.getMax(), devDiffTracker.getAverage(), lfgDiffTracker.getAverage(), lfgDiffTracker.getMax()); //! Can't use sWorld->ShutdownMsg here in case of console command if (sWorld->IsShuttingDown()) handler->PSendSysMessage(LANG_SHUTDOWN_TIMELEFT, secsToTimeString(sWorld->GetShutDownTimeLeft()).append(".").c_str()); return true; } // Display the 'Message of the day' for the realm static bool HandleServerMotdCommand(ChatHandler* handler, char const* /*args*/) { handler->PSendSysMessage(LANG_MOTD_CURRENT, Motd::GetMotd()); return true; } static bool HandleServerShutDownCancelCommand(ChatHandler* /*handler*/, char const* /*args*/) { sWorld->ShutdownCancel(); return true; } static bool HandleServerShutDownCommand(ChatHandler* /*handler*/, char const* args) { if (!*args) return false; char* timeStr = strtok((char*) args, " "); char* exitCodeStr = strtok(nullptr, ""); int32 time = atoi(timeStr); // Prevent interpret wrong arg value as 0 secs shutdown time if ((time == 0 && (timeStr[0] != '0' || timeStr[1] != '\0')) || time < 0) return false; if (exitCodeStr) { int32 exitCode = atoi(exitCodeStr); // Handle atoi() errors if (exitCode == 0 && (exitCodeStr[0] != '0' || exitCodeStr[1] != '\0')) return false; // Exit code should be in range of 0-125, 126-255 is used // in many shells for their own return codes and code > 255 // is not supported in many others if (exitCode < 0 || exitCode > 125) return false; sWorld->ShutdownServ(time, 0, exitCode); } else sWorld->ShutdownServ(time, 0, SHUTDOWN_EXIT_CODE); return true; } static bool HandleServerRestartCommand(ChatHandler* /*handler*/, char const* args) { if (!*args) return false; char* timeStr = strtok((char*) args, " "); char* exitCodeStr = strtok(nullptr, ""); int32 time = atoi(timeStr); // Prevent interpret wrong arg value as 0 secs shutdown time if ((time == 0 && (timeStr[0] != '0' || timeStr[1] != '\0')) || time < 0) return false; if (exitCodeStr) { int32 exitCode = atoi(exitCodeStr); // Handle atoi() errors if (exitCode == 0 && (exitCodeStr[0] != '0' || exitCodeStr[1] != '\0')) return false; // Exit code should be in range of 0-125, 126-255 is used // in many shells for their own return codes and code > 255 // is not supported in many others if (exitCode < 0 || exitCode > 125) return false; sWorld->ShutdownServ(time, SHUTDOWN_MASK_RESTART, exitCode); } else sWorld->ShutdownServ(time, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE); return true; } static bool HandleServerIdleRestartCommand(ChatHandler* /*handler*/, char const* args) { if (!*args) return false; char* timeStr = strtok((char*) args, " "); char* exitCodeStr = strtok(nullptr, ""); int32 time = atoi(timeStr); // Prevent interpret wrong arg value as 0 secs shutdown time if ((time == 0 && (timeStr[0] != '0' || timeStr[1] != '\0')) || time < 0) return false; if (exitCodeStr) { int32 exitCode = atoi(exitCodeStr); // Handle atoi() errors if (exitCode == 0 && (exitCodeStr[0] != '0' || exitCodeStr[1] != '\0')) return false; // Exit code should be in range of 0-125, 126-255 is used // in many shells for their own return codes and code > 255 // is not supported in many others if (exitCode < 0 || exitCode > 125) return false; sWorld->ShutdownServ(time, SHUTDOWN_MASK_RESTART | SHUTDOWN_MASK_IDLE, exitCode); } else sWorld->ShutdownServ(time, SHUTDOWN_MASK_RESTART | SHUTDOWN_MASK_IDLE, RESTART_EXIT_CODE); return true; } static bool HandleServerIdleShutDownCommand(ChatHandler* /*handler*/, char const* args) { if (!*args) return false; char* timeStr = strtok((char*) args, " "); char* exitCodeStr = strtok(nullptr, ""); int32 time = atoi(timeStr); // Prevent interpret wrong arg value as 0 secs shutdown time if ((time == 0 && (timeStr[0] != '0' || timeStr[1] != '\0')) || time < 0) return false; if (exitCodeStr) { int32 exitCode = atoi(exitCodeStr); // Handle atoi() errors if (exitCode == 0 && (exitCodeStr[0] != '0' || exitCodeStr[1] != '\0')) return false; // Exit code should be in range of 0-125, 126-255 is used // in many shells for their own return codes and code > 255 // is not supported in many others if (exitCode < 0 || exitCode > 125) return false; sWorld->ShutdownServ(time, SHUTDOWN_MASK_IDLE, exitCode); } else sWorld->ShutdownServ(time, SHUTDOWN_MASK_IDLE, SHUTDOWN_EXIT_CODE); return true; } // Exit the realm static bool HandleServerExitCommand(ChatHandler* handler, char const* /*args*/) { handler->SendSysMessage(LANG_COMMAND_EXIT); World::StopNow(SHUTDOWN_EXIT_CODE); return true; } // Define the 'Message of the day' for the realm static bool HandleServerSetMotdCommand(ChatHandler* handler, char const* args) { Motd::SetMotd(args); handler->PSendSysMessage(LANG_MOTD_NEW, args); return true; } // Set whether we accept new clients static bool HandleServerSetClosedCommand(ChatHandler* handler, char const* args) { if (strncmp(args, "on", 3) == 0) { handler->SendSysMessage(LANG_WORLD_CLOSED); sWorld->SetClosed(true); return true; } else if (strncmp(args, "off", 4) == 0) { handler->SendSysMessage(LANG_WORLD_OPENED); sWorld->SetClosed(false); return true; } handler->SendSysMessage(LANG_USE_BOL); handler->SetSentErrorMessage(true); return false; } // Set the level of logging static bool HandleServerSetLogLevelCommand(ChatHandler* /*handler*/, char const* args) { if (!*args) return false; char* type = strtok((char*)args, " "); char* name = strtok(nullptr, " "); char* level = strtok(nullptr, " "); if (!type || !name || !level || *name == '\0' || *level == '\0' || (*type != 'a' && *type != 'l')) return false; sLog->SetLogLevel(name, *Acore::StringTo(level), *type == 'l'); return true; } // set diff time record interval static bool HandleServerSetDiffTimeCommand(ChatHandler* /*handler*/, char const* args) { if (!*args) return false; char* newTimeStr = strtok((char*)args, " "); if (!newTimeStr) return false; int32 newTime = atoi(newTimeStr); if (newTime < 0) return false; sWorld->SetRecordDiffInterval(newTime); printf("Record diff every %u ms\n", newTime); return true; } }; void AddSC_server_commandscript() { new server_commandscript(); }