From 46f085eb4ba5a3c956eb9b0eaecb6fced5e39e3c Mon Sep 17 00:00:00 2001 From: EmosewaMC <39972741+EmosewaMC@users.noreply.github.com> Date: Tue, 6 Dec 2022 04:39:09 -0800 Subject: [PATCH 1/4] Resolve warnings and change init order Initialize dConfig first, before logger so we know whether or not to log to console Initialize namespace Game variables to nullptr so they are a known value if accessed before initialization. Removed unused Game variables Replaced config with a pointer instead of referencing something on the stack. Assign return values to system calls to silence warnings. Tested that the server still compiles, runs and allows me to load into the game. --- dAuthServer/AuthServer.cpp | 38 +++++++------- dChatServer/ChatServer.cpp | 46 ++++++++--------- dCommon/Game.h | 2 - dMasterServer/InstanceManager.cpp | 4 +- dMasterServer/MasterServer.cpp | 34 ++++++------- dWorldServer/WorldServer.cpp | 71 ++++++++++++--------------- tests/dGameTests/GameDependencies.cpp | 1 - 7 files changed, 92 insertions(+), 104 deletions(-) diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index a9f02f53..4999b62e 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -22,9 +22,9 @@ #include "Game.h" namespace Game { - dLogger* logger; - dServer* server; - dConfig* config; + dLogger* logger = nullptr; + dServer* server = nullptr; + dConfig* config = nullptr; } dLogger* SetupLogger(); @@ -37,22 +37,22 @@ int main(int argc, char** argv) { //Create all the objects we need to run our service: Game::logger = SetupLogger(); - if (!Game::logger) return 0; + if (!Game::logger) return EXIT_FAILURE; + + //Read our config: + Game::config = new dConfig("authconfig.ini"); + Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); + Game::logger->Log("AuthServer", "Starting Auth server..."); Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__); - //Read our config: - dConfig config("authconfig.ini"); - Game::config = &config; - Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); - Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); - //Connect to the MySQL Database - std::string mysql_host = config.GetValue("mysql_host"); - std::string mysql_database = config.GetValue("mysql_database"); - std::string mysql_username = config.GetValue("mysql_username"); - std::string mysql_password = config.GetValue("mysql_password"); + std::string mysql_host = Game::config->GetValue("mysql_host"); + std::string mysql_database = Game::config->GetValue("mysql_database"); + std::string mysql_username = Game::config->GetValue("mysql_username"); + std::string mysql_password = Game::config->GetValue("mysql_password"); try { Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); @@ -61,7 +61,7 @@ int main(int argc, char** argv) { Database::Destroy("AuthServer"); delete Game::server; delete Game::logger; - return 0; + return EXIT_FAILURE; } //Find out the master's IP: @@ -80,10 +80,10 @@ int main(int argc, char** argv) { //It's safe to pass 'localhost' here, as the IP is only used as the external IP. int maxClients = 50; int ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default. - if (config.GetValue("max_clients") != "") maxClients = std::stoi(config.GetValue("max_clients")); - if (config.GetValue("port") != "") ourPort = std::atoi(config.GetValue("port").c_str()); + if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); + if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); - Game::server = new dServer(config.GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config); + Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config); //Run it until server gets a kill message from Master: auto t = std::chrono::high_resolution_clock::now(); @@ -145,8 +145,8 @@ int main(int argc, char** argv) { Database::Destroy("AuthServer"); delete Game::server; delete Game::logger; + delete Game::config; - exit(EXIT_SUCCESS); return EXIT_SUCCESS; } diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index e3c6d6e9..2c36b222 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -20,11 +20,11 @@ #include "Game.h" namespace Game { - dLogger* logger; - dServer* server; - dConfig* config; - dChatFilter* chatFilter; - AssetManager* assetManager; + dLogger* logger = nullptr; + dServer* server = nullptr; + dConfig* config = nullptr; + dChatFilter* chatFilter = nullptr; + AssetManager* assetManager = nullptr; } //RakNet includes: @@ -42,19 +42,19 @@ int main(int argc, char** argv) { //Create all the objects we need to run our service: Game::logger = SetupLogger(); - if (!Game::logger) return 0; + if (!Game::logger) return EXIT_FAILURE; + + //Read our config: + Game::config = new dConfig("chatconfig.ini"); + Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); + Game::logger->Log("ChatServer", "Starting Chat server..."); Game::logger->Log("ChatServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); Game::logger->Log("ChatServer", "Compiled on: %s", __TIMESTAMP__); - //Read our config: - dConfig config("chatconfig.ini"); - Game::config = &config; - Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); - Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); - try { - std::string clientPathStr = config.GetValue("client_location"); + std::string clientPathStr = Game::config->GetValue("client_location"); if (clientPathStr.empty()) clientPathStr = "./res"; std::filesystem::path clientPath = std::filesystem::path(clientPathStr); if (clientPath.is_relative()) { @@ -69,10 +69,10 @@ int main(int argc, char** argv) { } //Connect to the MySQL Database - std::string mysql_host = config.GetValue("mysql_host"); - std::string mysql_database = config.GetValue("mysql_database"); - std::string mysql_username = config.GetValue("mysql_username"); - std::string mysql_password = config.GetValue("mysql_password"); + std::string mysql_host = Game::config->GetValue("mysql_host"); + std::string mysql_database = Game::config->GetValue("mysql_database"); + std::string mysql_username = Game::config->GetValue("mysql_username"); + std::string mysql_password = Game::config->GetValue("mysql_password"); try { Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); @@ -81,7 +81,7 @@ int main(int argc, char** argv) { Database::Destroy("ChatServer"); delete Game::server; delete Game::logger; - return 0; + return EXIT_FAILURE; } //Find out the master's IP: @@ -100,12 +100,12 @@ int main(int argc, char** argv) { //It's safe to pass 'localhost' here, as the IP is only used as the external IP. int maxClients = 50; int ourPort = 1501; - if (config.GetValue("max_clients") != "") maxClients = std::stoi(config.GetValue("max_clients")); - if (config.GetValue("port") != "") ourPort = std::atoi(config.GetValue("port").c_str()); + if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); + if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); - Game::server = new dServer(config.GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config); + Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config); - Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(config.GetValue("dont_generate_dcf")))); + Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); //Run it until server gets a kill message from Master: auto t = std::chrono::high_resolution_clock::now(); @@ -167,8 +167,8 @@ int main(int argc, char** argv) { Database::Destroy("ChatServer"); delete Game::server; delete Game::logger; + delete Game::config; - exit(EXIT_SUCCESS); return EXIT_SUCCESS; } diff --git a/dCommon/Game.h b/dCommon/Game.h index 1c4bbc85..38cadb70 100644 --- a/dCommon/Game.h +++ b/dCommon/Game.h @@ -5,7 +5,6 @@ class dServer; class dLogger; class InstanceManager; -class dpWorld; class dChatFilter; class dConfig; class RakPeerInterface; @@ -16,7 +15,6 @@ namespace Game { extern dLogger* logger; extern dServer* server; extern InstanceManager* im; - extern dpWorld* physicsWorld; extern dChatFilter* chatFilter; extern dConfig* config; extern std::mt19937 randomEngine; diff --git a/dMasterServer/InstanceManager.cpp b/dMasterServer/InstanceManager.cpp index 83378dbb..a1983dd4 100644 --- a/dMasterServer/InstanceManager.cpp +++ b/dMasterServer/InstanceManager.cpp @@ -74,7 +74,7 @@ Instance* InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LW cmd.append("&"); //Sends our next process to the background on Linux #endif - system(cmd.c_str()); + auto ret = system(cmd.c_str()); m_Instances.push_back(instance); @@ -322,7 +322,7 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon cmd.append("&"); //Sends our next process to the background on Linux #endif - system(cmd.c_str()); + auto ret = system(cmd.c_str()); m_Instances.push_back(instance); diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 4acb9b26..a4aa270a 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -43,11 +43,11 @@ #include "FdbToSqlite.h" namespace Game { - dLogger* logger; - dServer* server; - InstanceManager* im; - dConfig* config; - AssetManager* assetManager; + dLogger* logger = nullptr; + dServer* server = nullptr; + InstanceManager* im = nullptr; + dConfig* config = nullptr; + AssetManager* assetManager = nullptr; } //namespace Game bool shutdownSequenceStarted = false; @@ -79,14 +79,14 @@ int main(int argc, char** argv) { Game::logger = SetupLogger(); if (!Game::logger) return EXIT_FAILURE; - Game::logger->Log("MasterServer", "Starting Master server..."); - Game::logger->Log("MasterServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); - Game::logger->Log("MasterServer", "Compiled on: %s", __TIMESTAMP__); - Game::config = new dConfig("masterconfig.ini"); Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); + Game::logger->Log("MasterServer", "Starting Master server..."); + Game::logger->Log("MasterServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); + Game::logger->Log("MasterServer", "Compiled on: %s", __TIMESTAMP__); + //Connect to the MySQL Database std::string mysql_host = Game::config->GetValue("mysql_host"); std::string mysql_database = Game::config->GetValue("mysql_database"); @@ -732,28 +732,28 @@ void HandlePacket(Packet* packet) { void StartChatServer() { #ifdef __APPLE__ //macOS doesn't need sudo to run on ports < 1024 - system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); + auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); #elif _WIN32 - system(("start " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str()); + auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str()); #else if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) { - system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); + auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); } else { - system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); + auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); } #endif } void StartAuthServer() { #ifdef __APPLE__ - system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); + auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); #elif _WIN32 - system(("start " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str()); + auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str()); #else if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) { - system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); + auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); } else { - system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); + auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); } #endif } diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index acd38ad3..5887597d 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -61,17 +61,14 @@ #include "ZCompression.h" namespace Game { - dLogger* logger; - dServer* server; - dZoneManager* zoneManager; - dpWorld* physicsWorld; - dChatFilter* chatFilter; - dConfig* config; + dLogger* logger = nullptr; + dServer* server = nullptr; + dpWorld* physicsWorld = nullptr; + dChatFilter* chatFilter = nullptr; + dConfig* config = nullptr; + AssetManager* assetManager = nullptr; + RakPeerInterface* chatServer = nullptr; std::mt19937 randomEngine; - - AssetManager* assetManager; - - RakPeerInterface* chatServer; SystemAddress chatSysAddr; } @@ -127,26 +124,21 @@ int main(int argc, char** argv) { //Create all the objects we need to run our service: Game::logger = SetupLogger(zoneID, instanceID); - if (!Game::logger) return 0; + if (!Game::logger) return EXIT_FAILURE; + + //Read our config: + Game::config = new dConfig("worldconfig.ini"); + Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); - Game::logger->SetLogToConsole(true); //We want this info to always be logged. Game::logger->Log("WorldServer", "Starting World server..."); Game::logger->Log("WorldServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); Game::logger->Log("WorldServer", "Compiled on: %s", __TIMESTAMP__); -#ifndef _DEBUG - Game::logger->SetLogToConsole(false); //By default, turn it back off if not in debug. -#endif - - //Read our config: - dConfig config("worldconfig.ini"); - Game::config = &config; - Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); - Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); - if (config.GetValue("disable_chat") == "1") chatDisabled = true; + if (Game::config->GetValue("disable_chat") == "1") chatDisabled = true; try { - std::string clientPathStr = config.GetValue("client_location"); + std::string clientPathStr = Game::config->GetValue("client_location"); if (clientPathStr.empty()) clientPathStr = "./res"; std::filesystem::path clientPath = std::filesystem::path(clientPathStr); if (clientPath.is_relative()) { @@ -166,28 +158,28 @@ int main(int argc, char** argv) { Game::logger->Log("WorldServer", "Unable to connect to CDServer SQLite Database"); Game::logger->Log("WorldServer", "Error: %s", e.errorMessage()); Game::logger->Log("WorldServer", "Error Code: %i", e.errorCode()); - return -1; + return EXIT_FAILURE; } CDClientManager::Instance()->Initialize(); //Connect to the MySQL Database - std::string mysql_host = config.GetValue("mysql_host"); - std::string mysql_database = config.GetValue("mysql_database"); - std::string mysql_username = config.GetValue("mysql_username"); - std::string mysql_password = config.GetValue("mysql_password"); + std::string mysql_host = Game::config->GetValue("mysql_host"); + std::string mysql_database = Game::config->GetValue("mysql_database"); + std::string mysql_username = Game::config->GetValue("mysql_username"); + std::string mysql_password = Game::config->GetValue("mysql_password"); - Diagnostics::SetProduceMemoryDump(config.GetValue("generate_dump") == "1"); + Diagnostics::SetProduceMemoryDump(Game::config->GetValue("generate_dump") == "1"); - if (!config.GetValue("dump_folder").empty()) { - Diagnostics::SetOutDirectory(config.GetValue("dump_folder")); + if (!Game::config->GetValue("dump_folder").empty()) { + Diagnostics::SetOutDirectory(Game::config->GetValue("dump_folder")); } try { Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); } catch (sql::SQLException& ex) { Game::logger->Log("WorldServer", "Got an error while connecting to the database: %s", ex.what()); - return 0; + return EXIT_FAILURE; } //Find out the master's IP: @@ -206,13 +198,13 @@ int main(int argc, char** argv) { ObjectIDManager::Instance()->Initialize(); UserManager::Instance()->Initialize(); LootGenerator::Instance(); - Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(config.GetValue("dont_generate_dcf")))); + Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, zoneID); //Connect to the chat server: int chatPort = 1501; - if (config.GetValue("chat_server_port") != "") chatPort = std::atoi(config.GetValue("chat_server_port").c_str()); + if (Game::config->GetValue("chat_server_port") != "") chatPort = std::atoi(Game::config->GetValue("chat_server_port").c_str()); auto chatSock = SocketDescriptor(uint16_t(ourPort + 2), 0); Game::chatServer = RakNetworkFactory::GetRakPeerInterface(); @@ -1279,16 +1271,15 @@ void WorldShutdownSequence() { } void FinalizeShutdown() { - //Delete our objects here: - if (Game::zoneManager) delete Game::zoneManager; - Game::logger->Log("WorldServer", "Shutdown complete, zone (%i), instance (%i)", Game::server->GetZoneID(), instanceID); + //Delete our objects here: Metrics::Clear(); Database::Destroy("WorldServer"); - delete Game::chatFilter; - delete Game::server; - delete Game::logger; + if (Game::chatFilter) delete Game::chatFilter; + if (Game::server) delete Game::server; + if (Game::logger) delete Game::logger; + if (Game::config) delete Game::config; worldShutdownSequenceComplete = true; diff --git a/tests/dGameTests/GameDependencies.cpp b/tests/dGameTests/GameDependencies.cpp index 5ac3339a..7b0a8412 100644 --- a/tests/dGameTests/GameDependencies.cpp +++ b/tests/dGameTests/GameDependencies.cpp @@ -4,7 +4,6 @@ namespace Game { dLogger* logger; dServer* server; dZoneManager* zoneManager; - dpWorld* physicsWorld; dChatFilter* chatFilter; dConfig* config; std::mt19937 randomEngine; From a14e16237b8d20a1b43fc5770169c5d3ca4b5ad1 Mon Sep 17 00:00:00 2001 From: EmosewaMC <39972741+EmosewaMC@users.noreply.github.com> Date: Tue, 6 Dec 2022 19:30:43 -0800 Subject: [PATCH 2/4] Only start Master of config files exist Also default the logging to console to on on the off chance the files exist but are wrong / corrupted. --- dAuthServer/AuthServer.cpp | 4 ++-- dChatServer/ChatServer.cpp | 4 ++-- dMasterServer/MasterServer.cpp | 29 +++++++++++++++++++++++++++-- dWorldServer/WorldServer.cpp | 4 ++-- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index 4999b62e..aefc822b 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -40,8 +40,8 @@ int main(int argc, char** argv) { if (!Game::logger) return EXIT_FAILURE; //Read our config: - Game::config = new dConfig("authconfig.ini"); - Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); + Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string()); + Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); Game::logger->Log("AuthServer", "Starting Auth server..."); diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 2c36b222..043e7869 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -45,8 +45,8 @@ int main(int argc, char** argv) { if (!Game::logger) return EXIT_FAILURE; //Read our config: - Game::config = new dConfig("chatconfig.ini"); - Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); + Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string()); + Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); Game::logger->Log("ChatServer", "Starting Chat server..."); diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index a4aa270a..4c18566e 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -79,8 +79,33 @@ int main(int argc, char** argv) { Game::logger = SetupLogger(); if (!Game::logger) return EXIT_FAILURE; - Game::config = new dConfig("masterconfig.ini"); - Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); + if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "authconfig.ini")) { + Game::logger->Log("MasterServer", "Couldnt find authconfig.ini"); + return EXIT_FAILURE; + } + + if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "chatconfig.ini")) { + Game::logger->Log("MasterServer", "Couldnt find chatconfig.ini"); + return EXIT_FAILURE; + } + + if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "masterconfig.ini")) { + Game::logger->Log("MasterServer", "Couldnt find masterconfig.ini"); + return EXIT_FAILURE; + } + + if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini")) { + Game::logger->Log("MasterServer", "Couldnt find sharedconfig.ini"); + return EXIT_FAILURE; + } + + if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "worldconfig.ini")) { + Game::logger->Log("MasterServer", "Couldnt find worldconfig.ini"); + return EXIT_FAILURE; + } + + Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "masterconfig.ini").string()); + Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); Game::logger->Log("MasterServer", "Starting Master server..."); diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 5887597d..68ec0e57 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -127,8 +127,8 @@ int main(int argc, char** argv) { if (!Game::logger) return EXIT_FAILURE; //Read our config: - Game::config = new dConfig("worldconfig.ini"); - Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); + Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "worldconfig.ini").string()); + Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); Game::logger->Log("WorldServer", "Starting World server..."); From 435761f64bdf75edf717ed6a12b966624c16447c Mon Sep 17 00:00:00 2001 From: EmosewaMC <39972741+EmosewaMC@users.noreply.github.com> Date: Thu, 15 Dec 2022 04:02:38 -0800 Subject: [PATCH 3/4] Improve shutdown --- dAuthServer/AuthServer.cpp | 41 ++++++------ dChatServer/ChatServer.cpp | 49 ++++++++------- dCommon/Game.h | 3 + dMasterServer/InstanceManager.cpp | 6 +- dMasterServer/MasterServer.cpp | 88 ++++++++++---------------- dNet/dServer.cpp | 8 ++- dNet/dServer.h | 20 +++++- dWorldServer/WorldServer.cpp | 91 +++++++++++++++------------ tests/dGameTests/GameDependencies.cpp | 1 + 9 files changed, 160 insertions(+), 147 deletions(-) diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index aefc822b..4fffc5d0 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -22,9 +22,10 @@ #include "Game.h" namespace Game { - dLogger* logger = nullptr; - dServer* server = nullptr; - dConfig* config = nullptr; + dLogger* logger; + dServer* server; + dConfig* config; + bool shouldShutdown = false; } dLogger* SetupLogger(); @@ -37,22 +38,22 @@ int main(int argc, char** argv) { //Create all the objects we need to run our service: Game::logger = SetupLogger(); - if (!Game::logger) return EXIT_FAILURE; - - //Read our config: - Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string()); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); - + if (!Game::logger) return 0; Game::logger->Log("AuthServer", "Starting Auth server..."); Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__); + //Read our config: + dConfig config("authconfig.ini"); + Game::config = &config; + Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); + //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); + std::string mysql_host = config.GetValue("mysql_host"); + std::string mysql_database = config.GetValue("mysql_database"); + std::string mysql_username = config.GetValue("mysql_username"); + std::string mysql_password = config.GetValue("mysql_password"); try { Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); @@ -61,7 +62,7 @@ int main(int argc, char** argv) { Database::Destroy("AuthServer"); delete Game::server; delete Game::logger; - return EXIT_FAILURE; + return 0; } //Find out the master's IP: @@ -80,10 +81,10 @@ int main(int argc, char** argv) { //It's safe to pass 'localhost' here, as the IP is only used as the external IP. int maxClients = 50; int ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default. - if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); - if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); + if (config.GetValue("max_clients") != "") maxClients = std::stoi(config.GetValue("max_clients")); + if (config.GetValue("port") != "") ourPort = std::atoi(config.GetValue("port").c_str()); - Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config); + Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, Game::shouldShutdown); //Run it until server gets a kill message from Master: auto t = std::chrono::high_resolution_clock::now(); @@ -92,7 +93,7 @@ int main(int argc, char** argv) { int framesSinceMasterDisconnect = 0; int framesSinceLastSQLPing = 0; - while (true) { + while (!Game::shouldShutdown) { //Check if we're still connected to master: if (!Game::server->GetIsConnectedToMaster()) { framesSinceMasterDisconnect++; @@ -145,8 +146,8 @@ int main(int argc, char** argv) { Database::Destroy("AuthServer"); delete Game::server; delete Game::logger; - delete Game::config; + exit(EXIT_SUCCESS); return EXIT_SUCCESS; } diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 043e7869..d2eb7fc7 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -20,11 +20,12 @@ #include "Game.h" namespace Game { - dLogger* logger = nullptr; - dServer* server = nullptr; - dConfig* config = nullptr; - dChatFilter* chatFilter = nullptr; - AssetManager* assetManager = nullptr; + dLogger* logger; + dServer* server; + dConfig* config; + dChatFilter* chatFilter; + AssetManager* assetManager; + bool shouldShutdown = false; } //RakNet includes: @@ -42,19 +43,19 @@ int main(int argc, char** argv) { //Create all the objects we need to run our service: Game::logger = SetupLogger(); - if (!Game::logger) return EXIT_FAILURE; - - //Read our config: - Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string()); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); - + if (!Game::logger) return 0; Game::logger->Log("ChatServer", "Starting Chat server..."); Game::logger->Log("ChatServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); Game::logger->Log("ChatServer", "Compiled on: %s", __TIMESTAMP__); + //Read our config: + dConfig config("chatconfig.ini"); + Game::config = &config; + Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); + try { - std::string clientPathStr = Game::config->GetValue("client_location"); + std::string clientPathStr = config.GetValue("client_location"); if (clientPathStr.empty()) clientPathStr = "./res"; std::filesystem::path clientPath = std::filesystem::path(clientPathStr); if (clientPath.is_relative()) { @@ -69,10 +70,10 @@ int main(int argc, char** argv) { } //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); + std::string mysql_host = config.GetValue("mysql_host"); + std::string mysql_database = config.GetValue("mysql_database"); + std::string mysql_username = config.GetValue("mysql_username"); + std::string mysql_password = config.GetValue("mysql_password"); try { Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); @@ -81,7 +82,7 @@ int main(int argc, char** argv) { Database::Destroy("ChatServer"); delete Game::server; delete Game::logger; - return EXIT_FAILURE; + return 0; } //Find out the master's IP: @@ -100,12 +101,12 @@ int main(int argc, char** argv) { //It's safe to pass 'localhost' here, as the IP is only used as the external IP. int maxClients = 50; int ourPort = 1501; - if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); - if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); + if (config.GetValue("max_clients") != "") maxClients = std::stoi(config.GetValue("max_clients")); + if (config.GetValue("port") != "") ourPort = std::atoi(config.GetValue("port").c_str()); - Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config); + Game::server = new dServer(config.GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, Game::shouldShutdown); - Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); + Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(config.GetValue("dont_generate_dcf")))); //Run it until server gets a kill message from Master: auto t = std::chrono::high_resolution_clock::now(); @@ -114,7 +115,7 @@ int main(int argc, char** argv) { int framesSinceMasterDisconnect = 0; int framesSinceLastSQLPing = 0; - while (true) { + while (!Game::shouldShutdown) { //Check if we're still connected to master: if (!Game::server->GetIsConnectedToMaster()) { framesSinceMasterDisconnect++; @@ -167,8 +168,8 @@ int main(int argc, char** argv) { Database::Destroy("ChatServer"); delete Game::server; delete Game::logger; - delete Game::config; + exit(EXIT_SUCCESS); return EXIT_SUCCESS; } diff --git a/dCommon/Game.h b/dCommon/Game.h index 38cadb70..cbddafec 100644 --- a/dCommon/Game.h +++ b/dCommon/Game.h @@ -5,6 +5,7 @@ class dServer; class dLogger; class InstanceManager; +class dpWorld; class dChatFilter; class dConfig; class RakPeerInterface; @@ -15,10 +16,12 @@ namespace Game { extern dLogger* logger; extern dServer* server; extern InstanceManager* im; + extern dpWorld* physicsWorld; extern dChatFilter* chatFilter; extern dConfig* config; extern std::mt19937 randomEngine; extern RakPeerInterface* chatServer; extern AssetManager* assetManager; extern SystemAddress chatSysAddr; + extern bool shouldShutdown; } diff --git a/dMasterServer/InstanceManager.cpp b/dMasterServer/InstanceManager.cpp index a1983dd4..327a18b9 100644 --- a/dMasterServer/InstanceManager.cpp +++ b/dMasterServer/InstanceManager.cpp @@ -74,7 +74,7 @@ Instance* InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LW cmd.append("&"); //Sends our next process to the background on Linux #endif - auto ret = system(cmd.c_str()); + system(cmd.c_str()); m_Instances.push_back(instance); @@ -322,7 +322,7 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon cmd.append("&"); //Sends our next process to the background on Linux #endif - auto ret = system(cmd.c_str()); + system(cmd.c_str()); m_Instances.push_back(instance); @@ -391,5 +391,5 @@ void Instance::Shutdown() { Game::server->Send(&bitStream, this->m_SysAddr, false); - Game::logger->Log("Instance", "Triggered world shutdown"); + Game::logger->Log("Instance", "Triggered world shutdown for zone/clone/instance %i/%i/%i", GetMapID(), GetCloneID(), GetInstanceID()); } diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 4c18566e..2f54243a 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -43,11 +43,12 @@ #include "FdbToSqlite.h" namespace Game { - dLogger* logger = nullptr; - dServer* server = nullptr; - InstanceManager* im = nullptr; - dConfig* config = nullptr; - AssetManager* assetManager = nullptr; + dLogger* logger; + dServer* server; + InstanceManager* im; + dConfig* config; + AssetManager* assetManager; + bool shouldShutdown = false; } //namespace Game bool shutdownSequenceStarted = false; @@ -58,7 +59,6 @@ void StartAuthServer(); void StartChatServer(); void HandlePacket(Packet* packet); std::map activeSessions; -bool shouldShutdown = false; SystemAddress chatServerMasterPeerSysAddr; int main(int argc, char** argv) { @@ -79,39 +79,14 @@ int main(int argc, char** argv) { Game::logger = SetupLogger(); if (!Game::logger) return EXIT_FAILURE; - if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "authconfig.ini")) { - Game::logger->Log("MasterServer", "Couldnt find authconfig.ini"); - return EXIT_FAILURE; - } - - if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "chatconfig.ini")) { - Game::logger->Log("MasterServer", "Couldnt find chatconfig.ini"); - return EXIT_FAILURE; - } - - if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "masterconfig.ini")) { - Game::logger->Log("MasterServer", "Couldnt find masterconfig.ini"); - return EXIT_FAILURE; - } - - if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini")) { - Game::logger->Log("MasterServer", "Couldnt find sharedconfig.ini"); - return EXIT_FAILURE; - } - - if (!std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "worldconfig.ini")) { - Game::logger->Log("MasterServer", "Couldnt find worldconfig.ini"); - return EXIT_FAILURE; - } - - Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "masterconfig.ini").string()); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); - Game::logger->Log("MasterServer", "Starting Master server..."); Game::logger->Log("MasterServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); Game::logger->Log("MasterServer", "Compiled on: %s", __TIMESTAMP__); + Game::config = new dConfig("masterconfig.ini"); + Game::logger->SetLogToConsole(bool(std::stoi(Game::config->GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); + //Connect to the MySQL Database std::string mysql_host = Game::config->GetValue("mysql_host"); std::string mysql_database = Game::config->GetValue("mysql_database"); @@ -241,7 +216,7 @@ int main(int argc, char** argv) { if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); if (Game::config->GetValue("port") != "") ourPort = std::stoi(Game::config->GetValue("port")); - Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config); + Game::server = new dServer(config.GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, Game::shouldShutdown); //Query for the database for a server labeled "master" auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'"); @@ -328,7 +303,7 @@ int main(int argc, char** argv) { framesSinceLastSQLPing++; //10m shutdown for universe kill command - if (shouldShutdown) { + if (Game::shouldShutdown) { if (framesSinceKillUniverseCommand >= 40000) { //Break main loop and exit break; @@ -406,7 +381,7 @@ void HandlePacket(Packet* packet) { Game::im->RemoveInstance(instance); //Delete the old } - if (packet->systemAddress == chatServerMasterPeerSysAddr && !shouldShutdown) { + if (packet->systemAddress == chatServerMasterPeerSysAddr && !Game::shouldShutdown) { StartChatServer(); } } @@ -422,7 +397,7 @@ void HandlePacket(Packet* packet) { //Game::im->GetInstance(zoneID.GetMapID(), false, 0); //Create the new } - if (packet->systemAddress == chatServerMasterPeerSysAddr && !shouldShutdown) { + if (packet->systemAddress == chatServerMasterPeerSysAddr && !Game::shouldShutdown) { StartChatServer(); } } @@ -744,7 +719,7 @@ void HandlePacket(Packet* packet) { case MSG_MASTER_SHUTDOWN_UNIVERSE: { Game::logger->Log("MasterServer", "Received shutdown universe command, shutting down in 10 minutes."); - shouldShutdown = true; + Game::shouldShutdown = true; break; } @@ -757,28 +732,28 @@ void HandlePacket(Packet* packet) { void StartChatServer() { #ifdef __APPLE__ //macOS doesn't need sudo to run on ports < 1024 - auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); + system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); #elif _WIN32 - auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str()); + system(("start " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str()); #else if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) { - auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); + system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); } else { - auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); + system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); } #endif } void StartAuthServer() { #ifdef __APPLE__ - auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); + system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); #elif _WIN32 - auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str()); + system(("start " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str()); #else if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) { - auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); + system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); } else { - auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); + system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); } #endif } @@ -790,14 +765,11 @@ void ShutdownSequence() { shutdownSequenceStarted = true; - if (Game::im) { - for (auto* instance : Game::im->GetInstances()) { - if (instance == nullptr) { - continue; - } - - instance->Shutdown(); - } + { + CBITSTREAM; + PacketUtils::WriteHeader(bitStream, MASTER, MSG_MASTER_SHUTDOWN); + Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); + Game::logger->Log("MasterServer", "Triggered master shutdown"); } auto* objIdManager = ObjectIDManager::TryInstance(); @@ -813,6 +785,10 @@ void ShutdownSequence() { exit(EXIT_SUCCESS); } + for (auto instance : Game::im->GetInstances()) { + instance->SetIsShuttingDown(true); + } + Game::logger->Log("MasterServer", "Attempting to shutdown instances, max 60 seconds..."); while (true) { diff --git a/dNet/dServer.cpp b/dNet/dServer.cpp index 481667b8..3a5ef822 100644 --- a/dNet/dServer.cpp +++ b/dNet/dServer.cpp @@ -36,7 +36,7 @@ public: } } ReceiveDownloadCompleteCB; -dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, dLogger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, unsigned int zoneID) { +dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, dLogger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, bool& shouldShutdown, unsigned int zoneID) { mIP = ip; mPort = port; mZoneID = zoneID; @@ -52,6 +52,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect mReplicaManager = nullptr; mServerType = serverType; mConfig = config; + mShouldShutdown = &shouldShutdown; //Attempt to start our server here: mIsOkay = Startup(); @@ -124,8 +125,11 @@ Packet* dServer::ReceiveFromMaster() { ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(requestID, packet); break; } + case MSG_MASTER_SHUTDOWN: + *mShouldShutdown = true; + break; - //When we handle these packets in World instead dServer, we just return the packet's pointer. + //When we handle these packets in World instead dServer, we just return the packet's pointer. default: return packet; diff --git a/dNet/dServer.h b/dNet/dServer.h index 0fbdecce..608b3b83 100644 --- a/dNet/dServer.h +++ b/dNet/dServer.h @@ -18,7 +18,20 @@ class dServer { public: // Default constructor should only used for testing! dServer() {}; - dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, dLogger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, unsigned int zoneID = 0); + dServer( + const std::string& ip, + int port, + int instanceID, + int maxConnections, + bool isInternal, + bool useEncryption, + dLogger* logger, + const std::string masterIP, + int masterPort, + ServerType serverType, + dConfig* config, + bool& shouldShutdown, + unsigned int zoneID = 0); ~dServer(); Packet* ReceiveFromMaster(); @@ -64,6 +77,11 @@ private: RakPeerInterface* mPeer = nullptr; ReplicaManager* mReplicaManager = nullptr; NetworkIDManager* mNetIDManager = nullptr; + + /** + * Whether or not to shut down the server. Pointer to Game::shouldShutdown. + */ + bool* mShouldShutdown = nullptr; SocketDescriptor mSocketDescriptor; std::string mIP; int mPort; diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 68ec0e57..2fdb373b 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -61,20 +61,23 @@ #include "ZCompression.h" namespace Game { - dLogger* logger = nullptr; - dServer* server = nullptr; - dpWorld* physicsWorld = nullptr; - dChatFilter* chatFilter = nullptr; - dConfig* config = nullptr; - AssetManager* assetManager = nullptr; - RakPeerInterface* chatServer = nullptr; + dLogger* logger; + dServer* server; + dZoneManager* zoneManager; + dpWorld* physicsWorld; + dChatFilter* chatFilter; + dConfig* config; std::mt19937 randomEngine; + + AssetManager* assetManager; + + RakPeerInterface* chatServer; SystemAddress chatSysAddr; -} + bool shouldShutdown = false; +} // namespace Game bool chatDisabled = false; bool chatConnected = false; -bool worldShutdownSequenceStarted = false; bool worldShutdownSequenceComplete = false; void WorldShutdownSequence(); void WorldShutdownProcess(uint32_t zoneId); @@ -124,21 +127,26 @@ int main(int argc, char** argv) { //Create all the objects we need to run our service: Game::logger = SetupLogger(zoneID, instanceID); - if (!Game::logger) return EXIT_FAILURE; - - //Read our config: - Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "worldconfig.ini").string()); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); + if (!Game::logger) return 0; + Game::logger->SetLogToConsole(true); //We want this info to always be logged. Game::logger->Log("WorldServer", "Starting World server..."); Game::logger->Log("WorldServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); Game::logger->Log("WorldServer", "Compiled on: %s", __TIMESTAMP__); - if (Game::config->GetValue("disable_chat") == "1") chatDisabled = true; +#ifndef _DEBUG + Game::logger->SetLogToConsole(false); //By default, turn it back off if not in debug. +#endif + + //Read our config: + dConfig config("worldconfig.ini"); + Game::config = &config; + Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); + if (config.GetValue("disable_chat") == "1") chatDisabled = true; try { - std::string clientPathStr = Game::config->GetValue("client_location"); + std::string clientPathStr = config.GetValue("client_location"); if (clientPathStr.empty()) clientPathStr = "./res"; std::filesystem::path clientPath = std::filesystem::path(clientPathStr); if (clientPath.is_relative()) { @@ -158,28 +166,28 @@ int main(int argc, char** argv) { Game::logger->Log("WorldServer", "Unable to connect to CDServer SQLite Database"); Game::logger->Log("WorldServer", "Error: %s", e.errorMessage()); Game::logger->Log("WorldServer", "Error Code: %i", e.errorCode()); - return EXIT_FAILURE; + return -1; } CDClientManager::Instance()->Initialize(); //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); + std::string mysql_host = config.GetValue("mysql_host"); + std::string mysql_database = config.GetValue("mysql_database"); + std::string mysql_username = config.GetValue("mysql_username"); + std::string mysql_password = config.GetValue("mysql_password"); - Diagnostics::SetProduceMemoryDump(Game::config->GetValue("generate_dump") == "1"); + Diagnostics::SetProduceMemoryDump(config.GetValue("generate_dump") == "1"); - if (!Game::config->GetValue("dump_folder").empty()) { - Diagnostics::SetOutDirectory(Game::config->GetValue("dump_folder")); + if (!config.GetValue("dump_folder").empty()) { + Diagnostics::SetOutDirectory(config.GetValue("dump_folder")); } try { Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); } catch (sql::SQLException& ex) { Game::logger->Log("WorldServer", "Got an error while connecting to the database: %s", ex.what()); - return EXIT_FAILURE; + return 0; } //Find out the master's IP: @@ -198,13 +206,13 @@ int main(int argc, char** argv) { ObjectIDManager::Instance()->Initialize(); UserManager::Instance()->Initialize(); LootGenerator::Instance(); - Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); + Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(config.GetValue("dont_generate_dcf")))); - Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, zoneID); + Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, Game::shouldShutdown, zoneID); //Connect to the chat server: int chatPort = 1501; - if (Game::config->GetValue("chat_server_port") != "") chatPort = std::atoi(Game::config->GetValue("chat_server_port").c_str()); + if (config.GetValue("chat_server_port") != "") chatPort = std::atoi(config.GetValue("chat_server_port").c_str()); auto chatSock = SocketDescriptor(uint16_t(ourPort + 2), 0); Game::chatServer = RakNetworkFactory::GetRakPeerInterface(); @@ -315,9 +323,9 @@ int main(int argc, char** argv) { framesSinceMasterDisconnect++; int framesToWaitForMaster = ready ? 10 : 200; - if (framesSinceMasterDisconnect >= framesToWaitForMaster && !worldShutdownSequenceStarted) { + if (framesSinceMasterDisconnect >= framesToWaitForMaster && !Game::shouldShutdown) { Game::logger->Log("WorldServer", "Game loop running but no connection to master for %d frames, shutting down", framesToWaitForMaster); - worldShutdownSequenceStarted = true; + Game::shouldShutdown = true; } } else framesSinceMasterDisconnect = 0; @@ -413,7 +421,7 @@ int main(int argc, char** argv) { //If we haven't had any players for a while, time out and shut down: if (framesSinceLastUser == (cloneID != 0 ? 4000 : 40000)) { - worldShutdownSequenceStarted = true; + Game::shouldShutdown = true; } } else { framesSinceLastUser = 0; @@ -470,7 +478,7 @@ int main(int argc, char** argv) { } } - if (worldShutdownSequenceStarted && !worldShutdownSequenceComplete) { + if (Game::shouldShutdown && !worldShutdownSequenceComplete) { WorldShutdownProcess(zoneID); break; } @@ -789,7 +797,7 @@ void HandlePacket(Packet* packet) { } case MSG_MASTER_SHUTDOWN: { - worldShutdownSequenceStarted = true; + Game::shouldShutdown = true; Game::logger->Log("WorldServer", "Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); break; } @@ -1259,11 +1267,11 @@ void WorldShutdownProcess(uint32_t zoneId) { } void WorldShutdownSequence() { - if (worldShutdownSequenceStarted || worldShutdownSequenceComplete) { + if (Game::shouldShutdown || worldShutdownSequenceComplete) { return; } - worldShutdownSequenceStarted = true; + Game::shouldShutdown = true; Game::logger->Log("WorldServer", "Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), instanceID); WorldShutdownProcess(Game::server->GetZoneID()); @@ -1271,15 +1279,16 @@ void WorldShutdownSequence() { } void FinalizeShutdown() { + //Delete our objects here: + if (Game::zoneManager) delete Game::zoneManager; + Game::logger->Log("WorldServer", "Shutdown complete, zone (%i), instance (%i)", Game::server->GetZoneID(), instanceID); - //Delete our objects here: Metrics::Clear(); Database::Destroy("WorldServer"); - if (Game::chatFilter) delete Game::chatFilter; - if (Game::server) delete Game::server; - if (Game::logger) delete Game::logger; - if (Game::config) delete Game::config; + delete Game::chatFilter; + delete Game::server; + delete Game::logger; worldShutdownSequenceComplete = true; diff --git a/tests/dGameTests/GameDependencies.cpp b/tests/dGameTests/GameDependencies.cpp index 7b0a8412..5ac3339a 100644 --- a/tests/dGameTests/GameDependencies.cpp +++ b/tests/dGameTests/GameDependencies.cpp @@ -4,6 +4,7 @@ namespace Game { dLogger* logger; dServer* server; dZoneManager* zoneManager; + dpWorld* physicsWorld; dChatFilter* chatFilter; dConfig* config; std::mt19937 randomEngine; From 1afe71756332574988eea8fcc4f5979450c0faa4 Mon Sep 17 00:00:00 2001 From: EmosewaMC <39972741+EmosewaMC@users.noreply.github.com> Date: Thu, 15 Dec 2022 05:46:03 -0800 Subject: [PATCH 4/4] Properly exit Properly exit based on the path taken to shutdown master. Tested that shutting down through sigint or sigterm returns -1 Tested that a segfault exits the program properly Need to test that players who are trying to connect while master is shutting down are not able to spawn more child worlds. --- dAuthServer/AuthServer.cpp | 2 +- dChatServer/ChatServer.cpp | 2 +- dMasterServer/InstanceManager.cpp | 2 +- dMasterServer/MasterServer.cpp | 82 ++++++++++++++++++++++--------- dNet/dServer.cpp | 4 +- dNet/dServer.h | 2 +- dWorldServer/WorldServer.cpp | 2 +- 7 files changed, 65 insertions(+), 31 deletions(-) diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index 4fffc5d0..092b7fa4 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -84,7 +84,7 @@ int main(int argc, char** argv) { if (config.GetValue("max_clients") != "") maxClients = std::stoi(config.GetValue("max_clients")); if (config.GetValue("port") != "") ourPort = std::atoi(config.GetValue("port").c_str()); - Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, Game::shouldShutdown); + Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown); //Run it until server gets a kill message from Master: auto t = std::chrono::high_resolution_clock::now(); diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index d2eb7fc7..ace5e287 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -104,7 +104,7 @@ int main(int argc, char** argv) { if (config.GetValue("max_clients") != "") maxClients = std::stoi(config.GetValue("max_clients")); if (config.GetValue("port") != "") ourPort = std::atoi(config.GetValue("port").c_str()); - Game::server = new dServer(config.GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, Game::shouldShutdown); + Game::server = new dServer(config.GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(config.GetValue("dont_generate_dcf")))); diff --git a/dMasterServer/InstanceManager.cpp b/dMasterServer/InstanceManager.cpp index 327a18b9..de64c4ba 100644 --- a/dMasterServer/InstanceManager.cpp +++ b/dMasterServer/InstanceManager.cpp @@ -150,7 +150,7 @@ void InstanceManager::RemoveInstance(Instance* instance) { if (m_Instances[i] == instance) { instance->SetShutdownComplete(true); - RedirectPendingRequests(instance); + if (!Game::shouldShutdown) RedirectPendingRequests(instance); delete m_Instances[i]; diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 2f54243a..388654f5 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -52,13 +52,14 @@ namespace Game { } //namespace Game bool shutdownSequenceStarted = false; -void ShutdownSequence(); -int FinalizeShutdown(); +void ShutdownSequence(int signal = -1); +int FinalizeShutdown(int signal = -1); dLogger* SetupLogger(); void StartAuthServer(); void StartChatServer(); void HandlePacket(Packet* packet); std::map activeSessions; +SystemAddress authServerMasterPeerSysAddr; SystemAddress chatServerMasterPeerSysAddr; int main(int argc, char** argv) { @@ -71,9 +72,9 @@ int main(int argc, char** argv) { #endif //Triggers the shutdown sequence at application exit - std::atexit(ShutdownSequence); - signal(SIGINT, [](int) { ShutdownSequence(); }); - signal(SIGTERM, [](int) { ShutdownSequence(); }); + std::atexit([]() { ShutdownSequence(); }); + signal(SIGINT, [](int signal) { ShutdownSequence(EXIT_FAILURE); }); + signal(SIGTERM, [](int signal) { ShutdownSequence(EXIT_FAILURE); }); //Create all the objects we need to run our service: Game::logger = SetupLogger(); @@ -216,7 +217,7 @@ int main(int argc, char** argv) { if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); if (Game::config->GetValue("port") != "") ourPort = std::stoi(Game::config->GetValue("port")); - Game::server = new dServer(config.GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, Game::shouldShutdown); + Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::shouldShutdown); //Query for the database for a server labeled "master" auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'"); @@ -253,8 +254,8 @@ int main(int argc, char** argv) { if (Game::config->GetValue("prestart_servers") != "" && Game::config->GetValue("prestart_servers") == "1") { StartChatServer(); - Game::im->GetInstance(0, false, 0)->SetIsReady(true); - Game::im->GetInstance(1000, false, 0)->SetIsReady(true); + Game::im->GetInstance(0, false, 0); + Game::im->GetInstance(1000, false, 0); StartAuthServer(); } @@ -350,9 +351,7 @@ int main(int argc, char** argv) { t += std::chrono::milliseconds(highFrameRate); std::this_thread::sleep_until(t); } - FinalizeShutdown(); - exit(EXIT_SUCCESS); - return EXIT_SUCCESS; + return FinalizeShutdown(EXIT_SUCCESS); } dLogger* SetupLogger() { @@ -381,9 +380,15 @@ void HandlePacket(Packet* packet) { Game::im->RemoveInstance(instance); //Delete the old } - if (packet->systemAddress == chatServerMasterPeerSysAddr && !Game::shouldShutdown) { + if (packet->systemAddress == chatServerMasterPeerSysAddr) { + chatServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS; StartChatServer(); } + + if (packet->systemAddress == authServerMasterPeerSysAddr) { + authServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS; + StartAuthServer(); + } } if (packet->data[0] == ID_CONNECTION_LOST) { @@ -397,9 +402,15 @@ void HandlePacket(Packet* packet) { //Game::im->GetInstance(zoneID.GetMapID(), false, 0); //Create the new } - if (packet->systemAddress == chatServerMasterPeerSysAddr && !Game::shouldShutdown) { + if (packet->systemAddress == chatServerMasterPeerSysAddr) { + chatServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS; StartChatServer(); } + + if (packet->systemAddress == authServerMasterPeerSysAddr) { + authServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS; + StartAuthServer(); + } } if (packet->data[1] == MASTER) { @@ -495,6 +506,14 @@ void HandlePacket(Packet* packet) { chatServerMasterPeerSysAddr = copy; } + if (theirServerType == ServerType::Auth) { + SystemAddress copy; + copy.binaryAddress = packet->systemAddress.binaryAddress; + copy.port = packet->systemAddress.port; + + authServerMasterPeerSysAddr = copy; + } + Game::logger->Log("MasterServer", "Received server info, instance: %i port: %i", theirInstanceID, theirPort); break; @@ -726,10 +745,14 @@ void HandlePacket(Packet* packet) { default: Game::logger->Log("MasterServer", "Unknown master packet ID from server: %i", packet->data[3]); } + } } -} void StartChatServer() { + if (Game::shouldShutdown) { + Game::logger->Log("MasterServer", "Currently shutting down. Chat will not be restarted."); + return; + } #ifdef __APPLE__ //macOS doesn't need sudo to run on ports < 1024 system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); @@ -742,9 +765,13 @@ void StartChatServer() { system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str()); } #endif -} + } void StartAuthServer() { + if (Game::shouldShutdown) { + Game::logger->Log("MasterServer", "Currently shutting down. Auth will not be restarted."); + return; + } #ifdef __APPLE__ system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str()); #elif _WIN32 @@ -758,12 +785,13 @@ void StartAuthServer() { #endif } -void ShutdownSequence() { +void ShutdownSequence(int signal) { if (shutdownSequenceStarted) { return; } shutdownSequenceStarted = true; + Game::shouldShutdown = true; { CBITSTREAM; @@ -782,7 +810,14 @@ void ShutdownSequence() { auto ticks = 0; if (!Game::im) { - exit(EXIT_SUCCESS); + FinalizeShutdown(EXIT_FAILURE); + } + + // A server might not be finished spinning up yet, remove all of those here. + for (auto instance : Game::im->GetInstances()) { + if (!instance->GetIsReady()) { + Game::im->RemoveInstance(instance); + } } for (auto instance : Game::im->GetInstances()) { @@ -792,7 +827,6 @@ void ShutdownSequence() { Game::logger->Log("MasterServer", "Attempting to shutdown instances, max 60 seconds..."); while (true) { - auto packet = Game::server->Receive(); if (packet) { HandlePacket(packet); @@ -811,8 +845,8 @@ void ShutdownSequence() { done = false; } } - - if (done) { + + if (done && authServerMasterPeerSysAddr == UNASSIGNED_SYSTEM_ADDRESS && chatServerMasterPeerSysAddr == UNASSIGNED_SYSTEM_ADDRESS) { Game::logger->Log("MasterServer", "Finished shutting down MasterServer!"); break; } @@ -828,10 +862,10 @@ void ShutdownSequence() { } } - FinalizeShutdown(); + FinalizeShutdown(signal); } -int FinalizeShutdown() { +int FinalizeShutdown(int signal) { //Delete our objects here: Database::Destroy("MasterServer"); if (Game::config) delete Game::config; @@ -839,6 +873,6 @@ int FinalizeShutdown() { if (Game::server) delete Game::server; if (Game::logger) delete Game::logger; - exit(EXIT_SUCCESS); - return EXIT_SUCCESS; + if (signal != EXIT_SUCCESS) exit(signal); + return signal; } diff --git a/dNet/dServer.cpp b/dNet/dServer.cpp index 3a5ef822..f41b9699 100644 --- a/dNet/dServer.cpp +++ b/dNet/dServer.cpp @@ -36,7 +36,7 @@ public: } } ReceiveDownloadCompleteCB; -dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, dLogger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, bool& shouldShutdown, unsigned int zoneID) { +dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, dLogger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, bool* shouldShutdown, unsigned int zoneID) { mIP = ip; mPort = port; mZoneID = zoneID; @@ -52,7 +52,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect mReplicaManager = nullptr; mServerType = serverType; mConfig = config; - mShouldShutdown = &shouldShutdown; + mShouldShutdown = shouldShutdown; //Attempt to start our server here: mIsOkay = Startup(); diff --git a/dNet/dServer.h b/dNet/dServer.h index 608b3b83..af4c8322 100644 --- a/dNet/dServer.h +++ b/dNet/dServer.h @@ -30,7 +30,7 @@ public: int masterPort, ServerType serverType, dConfig* config, - bool& shouldShutdown, + bool* shouldShutdown, unsigned int zoneID = 0); ~dServer(); diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 2fdb373b..abb2c19f 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -208,7 +208,7 @@ int main(int argc, char** argv) { LootGenerator::Instance(); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(config.GetValue("dont_generate_dcf")))); - Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, Game::shouldShutdown, zoneID); + Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::shouldShutdown, zoneID); //Connect to the chat server: int chatPort = 1501;