DarkflameServer/dMasterServer/MasterServer.cpp

989 lines
32 KiB
C++
Raw Normal View History

#include <chrono>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <map>
#include <string>
#include <thread>
2022-01-21 16:46:19 +00:00
#include <fstream>
#ifdef _WIN32
#include <bcrypt/BCrypt.hpp>
#else
#include <bcrypt.h>
#endif
#include <csignal>
//DLU Includes:
#include "CDClientDatabase.h"
#include "CDClientManager.h"
#include "Database.h"
2022-07-10 19:40:26 +00:00
#include "MigrationRunner.h"
#include "Diagnostics.h"
#include "dCommonVars.h"
#include "dConfig.h"
#include "dLogger.h"
#include "dServer.h"
#include "AssetManager.h"
#include "BinaryPathFinder.h"
#include "eConnectionType.h"
#include "eMasterMessageType.h"
//RakNet includes:
#include "RakNetDefines.h"
//Packet includes:
#include "AuthPackets.h"
#include "Game.h"
#include "InstanceManager.h"
#include "MasterPackets.h"
#include "ObjectIDManager.h"
#include "BitstreamUtils.h"
2022-12-05 08:57:58 +00:00
#include "FdbToSqlite.h"
namespace Game {
dLogger* logger = nullptr;
dServer* server = nullptr;
InstanceManager* im = nullptr;
dConfig* config = nullptr;
AssetManager* assetManager = nullptr;
2022-12-15 12:02:38 +00:00
bool shouldShutdown = false;
std::mt19937 randomEngine;
} //namespace Game
bool shutdownSequenceStarted = false;
void ShutdownSequence(int32_t signal = -1);
int32_t FinalizeShutdown(int32_t signal = -1);
dLogger* SetupLogger();
void StartAuthServer();
void StartChatServer();
void HandlePacket(Packet* packet);
std::map<uint32_t, std::string> activeSessions;
SystemAddress authServerMasterPeerSysAddr;
SystemAddress chatServerMasterPeerSysAddr;
int main(int argc, char** argv) {
constexpr uint32_t masterFramerate = mediumFramerate;
constexpr uint32_t masterFrameDelta = mediumFrameDelta;
Diagnostics::SetProcessName("Master");
Diagnostics::SetProcessFileName(argv[0]);
Diagnostics::Initialize();
Add Aarch64 support (#231) * added mariadb-connector-cpp submodule * raknet aarch64 support * fix compile errors * mariadb connector swap (in progress) * update CMakeLists, add preprocessor definition to switch between mysql and mariadb connectors * update types with missing aarch64 check * corrected adding extra flag to properly compile mariadbconn in CMakeLists * updated readme with arm builds section * fix build failure if test folder does not exist * Remove mysql connector from all builds, add mariadbconnector to windows build * readd Linux check for backtrace lib to CMakeLists.txt * Separate system specific mariadbconncpp extra compile flags * Copy dlls to exes directory once built * fetch prebuilt binaries on windows so that ClangCL can be used * Delay load dll so that plugin directory is set correctly * Fixed typo in glibcxx compile flag * whitespacing, spaces -> tabs * Updated README.md, included instructions to update * Updated README.md added libssl-dev requirement and removed mysql connector references from macOS builds section * apple compile fixes for zlib and shared library name * add windows arm64 checks to raknet * remove extra . in shared library location * Setup plugins directory for the connector to search in, pass openssl_root_dir on for apple * Fix copy paths for single config generators and non windows * change plugin folder location, another single config generator fix * GENERATOR_IS_MULTI_CONFIG is a property not a variable * Fixed a few errors after merge * Fix plugin directory path, force windows to look at the right folder * fixed directory name for make_directory command * Update README.md Updated MacOS, Windows build instructions. * set INSTALL_PLUGINDIR so that the right directory is used * Support for relative rpath for docker build * added mariadb-connector-cpp submodule * raknet aarch64 support * fix compile errors * mariadb connector swap (in progress) * update CMakeLists, add preprocessor definition to switch between mysql and mariadb connectors * update types with missing aarch64 check * corrected adding extra flag to properly compile mariadbconn in CMakeLists * updated readme with arm builds section * fix build failure if test folder does not exist * Remove mysql connector from all builds, add mariadbconnector to windows build * readd Linux check for backtrace lib to CMakeLists.txt * Separate system specific mariadbconncpp extra compile flags * Copy dlls to exes directory once built * fetch prebuilt binaries on windows so that ClangCL can be used * Delay load dll so that plugin directory is set correctly * Fixed typo in glibcxx compile flag * whitespacing, spaces -> tabs * Updated README.md, included instructions to update * Updated README.md added libssl-dev requirement and removed mysql connector references from macOS builds section * apple compile fixes for zlib and shared library name * add windows arm64 checks to raknet * Setup plugins directory for the connector to search in, pass openssl_root_dir on for apple * Fix copy paths for single config generators and non windows * change plugin folder location, another single config generator fix * GENERATOR_IS_MULTI_CONFIG is a property not a variable * Fixed a few errors after merge * Fix plugin directory path, force windows to look at the right folder * fixed directory name for make_directory command * Update README.md Updated MacOS, Windows build instructions. * set INSTALL_PLUGINDIR so that the right directory is used * Support for relative rpath for docker build * Rebase on main * Remove extra git submodule * Update CMakeLists.txt * Remove CMakeLists.txt file from mariadb Remove the CMakeLists.txt file from the mariaDBConnector so we dont build the tests. Also add a config option to the CMakeVariables.txt so you can build the connector with multiple jobs * Compile on windows Specify the mariadbcpp.dll file location with a defined absolute path so windows knows it actually exists. * default to 1 job Default mariadb jobs running in parallel to 1 instead of 4 * Move mariadbcpp.dll file to the expected directory on windows * Changed plugin Updated the plugin location from the project binary directory to the expected location, the mariadb binary directory. * Addressed windows dll issues by moving files to the expected directory instead of a directory that wouldnt get created * Update README Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com> Co-authored-by: EmosewaMC <39972741+EmosewaMC@users.noreply.github.com>
2022-07-04 04:33:05 +00:00
#if defined(_WIN32) && defined(MARIADB_PLUGIN_DIR_OVERRIDE)
_putenv_s("MARIADB_PLUGIN_DIR", MARIADB_PLUGIN_DIR_OVERRIDE);
#endif
//Triggers the shutdown sequence at application exit
std::atexit([]() { ShutdownSequence(); });
signal(SIGINT, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); });
signal(SIGTERM, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); });
//Create all the objects we need to run our service:
Game::logger = SetupLogger();
2022-01-24 23:41:35 +00:00
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");
uint32_t clientNetVersion = 0;
if (!GeneralUtils::TryParse(Game::config->GetValue("client_net_version"), clientNetVersion)) {
Game::logger->Log("MasterServer", "Failed to parse (%s) as net version. Cannot start server as no clients could connect.",Game::config->GetValue("client_net_version").c_str());
Game::logger->Log("MasterServer", "As of version 1.1.1, client_net_version is required to be defined in sharedconfig.ini as opposed to in CMakeVariables.txt as NET_VERSION.");
Game::logger->Log("MasterServer", "Rerun cmake to ensure all config values exist. If client_net_version already exists in sharedconfig.ini, please ensure it is a valid number.");
Game::logger->Log("MasterServer", "like 171022");
return EXIT_FAILURE;
}
Game::logger->Log("MasterServer", "Using net version %s", Game::config->GetValue("client_net_version").c_str());
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__);
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
//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");
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
try {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
} catch (sql::SQLException& ex) {
Game::logger->Log("MasterServer", "Got an error while connecting to the database: %s", ex.what());
Game::logger->Log("MigrationRunner", "Migrations not run");
return EXIT_FAILURE;
}
try {
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()) {
clientPath = BinaryPathFinder::GetBinaryDir() / clientPath;
}
Game::assetManager = new AssetManager(clientPath);
} catch (std::runtime_error& ex) {
Game::logger->Log("MasterServer", "Got an error while setting up assets: %s", ex.what());
return EXIT_FAILURE;
}
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
MigrationRunner::RunMigrations();
const bool cdServerExists = std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite");
const bool oldCDServerExists = std::filesystem::exists(Game::assetManager->GetResPath() / "CDServer.sqlite");
const bool fdbExists = std::filesystem::exists(Game::assetManager->GetResPath() / "cdclient.fdb");
if (!cdServerExists) {
if (oldCDServerExists) {
// If the file doesn't exist in the new CDServer location, copy it there. We copy because we may not have write permissions from the previous directory.
Game::logger->Log("MasterServer", "CDServer.sqlite is not located at resServer, but is located at res path. Copying file...");
std::filesystem::copy_file(Game::assetManager->GetResPath() / "CDServer.sqlite", BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite");
} else {
Game::logger->Log("MasterServer",
"%s could not be found in resServer or res. Looking for %s to convert to sqlite.",
(BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite").c_str(),
(Game::assetManager->GetResPath() / "cdclient.fdb").c_str());
AssetMemoryBuffer cdClientBuffer = Game::assetManager->GetFileAsBuffer("cdclient.fdb");
if (!cdClientBuffer.m_Success) {
Game::logger->Log("MasterServer", "Failed to load %s", (Game::assetManager->GetResPath() / "cdclient.fdb").c_str());
throw std::runtime_error("Aborting initialization due to missing cdclient.fdb.");
}
Game::logger->Log("MasterServer", "Found %s. Converting to SQLite", (Game::assetManager->GetResPath() / "cdclient.fdb").c_str());
Game::logger->Flush();
if (FdbToSqlite::Convert((BinaryPathFinder::GetBinaryDir() / "resServer").string()).ConvertDatabase(cdClientBuffer) == false) {
Game::logger->Log("MasterServer", "Failed to convert fdb to sqlite.");
return EXIT_FAILURE;
}
cdClientBuffer.close();
}
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
}
//Connect to CDClient
try {
CDClientDatabase::Connect((BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite").string());
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
} catch (CppSQLite3Exception& e) {
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;
}
// Run migrations should any need to be run.
MigrationRunner::RunSQLiteMigrations();
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
//Get CDClient initial information
try {
CDClientManager::Instance();
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
} catch (CppSQLite3Exception& e) {
Game::logger->Log("WorldServer", "Failed to initialize CDServer SQLite Database");
Game::logger->Log("WorldServer", "May be caused by corrupted file: %s", (Game::assetManager->GetResPath() / "CDServer.sqlite").string().c_str());
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
Game::logger->Log("WorldServer", "Error: %s", e.errorMessage());
Game::logger->Log("WorldServer", "Error Code: %i", e.errorCode());
return EXIT_FAILURE;
}
2022-07-10 19:40:26 +00:00
//If the first command line argument is -a or --account then make the user
//input a username and password, with the password being hidden.
if (argc > 1 &&
(strcmp(argv[1], "-a") == 0 || strcmp(argv[1], "--account") == 0)) {
std::string username;
std::string password;
std::cout << "Enter a username: ";
std::cin >> username;
std::unique_ptr<sql::PreparedStatement> userLookupStatement(Database::CreatePreppedStmt("SELECT id FROM accounts WHERE name=? LIMIT 1;"));
userLookupStatement->setString(1, username.c_str());
std::unique_ptr<sql::ResultSet> res(userLookupStatement->executeQuery());
if (res->rowsCount() > 0) {
Game::logger->Log("MasterServer", "Account with name \"%s\" already exists", username.c_str());
std::cout << "Do you want to change the password of that account? [y/n]?";
std::string prompt = "";
std::cin >> prompt;
if (prompt == "y" || prompt == "yes"){
uint32_t accountId = 0;
res->next();
accountId = res->getUInt(1);
if (accountId == 0) return EXIT_FAILURE;
//Read the password from the console without echoing it.
#ifdef __linux__
//This function is obsolete, but it only meant to be used by the
//sysadmin to create their first account.
password = getpass("Enter a password: ");
#else
std::cout << "Enter a password: ";
std::cin >> password;
#endif
// Regenerate hash based on new password
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
std::unique_ptr<sql::PreparedStatement> userUpdateStatement(Database::CreatePreppedStmt("UPDATE accounts SET password = ? WHERE id = ?;"));
userUpdateStatement->setString(1, std::string(hash, BCRYPT_HASHSIZE).c_str());
userUpdateStatement->setUInt(2, accountId);
userUpdateStatement->execute();
Game::logger->Log("MasterServer", "Account \"%s\" password updated successfully!", username.c_str());
} else {
Game::logger->Log("MasterServer", "Account \"%s\" was not updated.", username.c_str());
}
return EXIT_SUCCESS;
}
//Read the password from the console without echoing it.
#ifdef __linux__
//This function is obsolete, but it only meant to be used by the
//sysadmin to create their first account.
password = getpass("Enter a password: ");
#else
std::cout << "Enter a password: ";
std::cin >> password;
#endif
//Generate new hash for bcrypt
char salt[BCRYPT_HASHSIZE];
char hash[BCRYPT_HASHSIZE];
int32_t bcryptState = ::bcrypt_gensalt(12, salt);
assert(bcryptState == 0);
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
assert(bcryptState == 0);
//Create account
try {
std::unique_ptr<sql::PreparedStatement> statement(Database::CreatePreppedStmt("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);"));
statement->setString(1, username.c_str());
statement->setString(2, std::string(hash, BCRYPT_HASHSIZE).c_str());
statement->setInt(3, 9);
statement->execute();
} catch(sql::SQLException& e) {
Game::logger->Log("MasterServer", "A SQL error occurred!:\n %s", e.what());
return EXIT_FAILURE;
}
Game::logger->Log("MasterServer", "Account created successfully!");
2022-01-24 23:41:35 +00:00
return EXIT_SUCCESS;
}
Game::randomEngine = std::mt19937(time(0));
uint32_t maxClients = 999;
uint32_t ourPort = 1000;
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::shouldShutdown);
//Query for the database for a server labeled "master"
auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'");
auto* result = masterLookupStatement->executeQuery();
auto master_server_ip = Game::config->GetValue("master_ip");
2021-12-08 13:57:16 +00:00
2021-12-09 17:49:46 +00:00
if (master_server_ip == "") {
master_server_ip = Game::server->GetIP();
}
2021-12-08 13:57:16 +00:00
//If we found a server, update it's IP and port to the current one.
if (result->next()) {
auto* updateStatement = Database::CreatePreppedStmt("UPDATE `servers` SET `ip` = ?, `port` = ? WHERE `id` = ?");
updateStatement->setString(1, master_server_ip.c_str());
updateStatement->setInt(2, Game::server->GetPort());
updateStatement->setInt(3, result->getInt("id"));
updateStatement->execute();
delete updateStatement;
2022-07-28 13:39:57 +00:00
} else {
//If we didn't find a server, create one.
auto* insertStatement = Database::CreatePreppedStmt("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171023)");
insertStatement->setString(1, master_server_ip.c_str());
insertStatement->setInt(2, Game::server->GetPort());
insertStatement->execute();
delete insertStatement;
}
//Create additional objects here:
ObjectIDManager::Instance()->Initialize(Game::logger);
Game::im = new InstanceManager(Game::logger, Game::server->GetIP());
//Depending on the config, start up servers:
if (Game::config->GetValue("prestart_servers") != "" && Game::config->GetValue("prestart_servers") == "1") {
StartChatServer();
2023-03-26 10:09:04 +00:00
Game::im->GetInstance(0, false, 0);
Game::im->GetInstance(1000, false, 0);
StartAuthServer();
}
auto t = std::chrono::high_resolution_clock::now();
Packet* packet = nullptr;
constexpr uint32_t logFlushTime = 15 * masterFramerate;
constexpr uint32_t sqlPingTime = 10 * 60 * masterFramerate;
constexpr uint32_t shutdownUniverseTime = 10 * 60 * masterFramerate;
constexpr uint32_t instanceReadyTimeout = 30 * masterFramerate;
uint32_t framesSinceLastFlush = 0;
uint32_t framesSinceLastSQLPing = 0;
uint32_t framesSinceKillUniverseCommand = 0;
while (true) {
//In world we'd update our other systems here.
//Check for packets here:
packet = Game::server->Receive();
if (packet) {
HandlePacket(packet);
Game::server->DeallocatePacket(packet);
packet = nullptr;
}
//Push our log every 15s:
if (framesSinceLastFlush >= logFlushTime) {
Game::logger->Flush();
framesSinceLastFlush = 0;
2022-07-28 13:39:57 +00:00
} else
framesSinceLastFlush++;
//Every 10 min we ping our sql server to keep it alive hopefully:
if (framesSinceLastSQLPing >= sqlPingTime) {
//Find out the master's IP for absolutely no reason:
std::string masterIP;
uint32_t masterPort;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
}
delete res;
delete stmt;
framesSinceLastSQLPing = 0;
2022-07-28 13:39:57 +00:00
} else
framesSinceLastSQLPing++;
//10m shutdown for universe kill command
2022-12-15 12:02:38 +00:00
if (Game::shouldShutdown) {
if (framesSinceKillUniverseCommand >= shutdownUniverseTime) {
//Break main loop and exit
break;
2022-07-28 13:39:57 +00:00
} else
framesSinceKillUniverseCommand++;
}
const auto instances = Game::im->GetInstances();
for (auto* instance : instances) {
if (instance == nullptr) {
break;
}
auto affirmTimeout = instance->GetAffirmationTimeout();
if (!instance->GetPendingAffirmations().empty()) {
affirmTimeout++;
2022-07-28 13:39:57 +00:00
} else {
affirmTimeout = 0;
}
instance->SetAffirmationTimeout(affirmTimeout);
if (affirmTimeout == instanceReadyTimeout) {
instance->Shutdown();
instance->SetIsShuttingDown(true);
Game::im->RedirectPendingRequests(instance);
}
}
//Remove dead instances
for (auto* instance : instances) {
if (instance == nullptr) {
break;
}
if (instance->GetShutdownComplete()) {
Game::im->RemoveInstance(instance);
}
}
t += std::chrono::milliseconds(masterFrameDelta);
std::this_thread::sleep_until(t);
}
return FinalizeShutdown(EXIT_SUCCESS);
}
dLogger* SetupLogger() {
std::string logPath =
(BinaryPathFinder::GetBinaryDir() / ("logs/MasterServer_" + std::to_string(time(nullptr)) + ".log")).string();
bool logToConsole = false;
bool logDebugStatements = false;
#ifdef _DEBUG
logToConsole = true;
logDebugStatements = true;
#endif
return new dLogger(logPath, logToConsole, logDebugStatements);
}
void HandlePacket(Packet* packet) {
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) {
Game::logger->Log("MasterServer", "A server has disconnected");
//Since this disconnection is intentional, we'll just delete it as
//we'll start a new one anyway if needed:
Instance* instance =
Game::im->GetInstanceBySysAddr(packet->systemAddress);
if (instance) {
Game::logger->Log("MasterServer", "Actually disconnected from zone %i clone %i instance %i port %i", instance->GetMapID(), instance->GetCloneID(), instance->GetInstanceID(), instance->GetPort());
Game::im->RemoveInstance(instance); //Delete the old
}
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) {
Game::logger->Log("MasterServer", "A server has lost the connection");
Instance* instance =
Game::im->GetInstanceBySysAddr(packet->systemAddress);
if (instance) {
LWOZONEID zoneID = instance->GetZoneID(); //Get the zoneID so we can recreate a server
Game::im->RemoveInstance(instance); //Delete the old
}
if (packet->systemAddress == chatServerMasterPeerSysAddr) {
chatServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS;
StartChatServer();
}
if (packet->systemAddress == authServerMasterPeerSysAddr) {
authServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS;
StartAuthServer();
}
}
if (packet->length < 4) return;
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::MASTER) {
switch (static_cast<eMasterMessageType>(packet->data[3])) {
case eMasterMessageType::REQUEST_PERSISTENT_ID: {
Game::logger->Log("MasterServer", "A persistent ID req");
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
uint64_t requestID = 0;
inStream.Read(requestID);
uint32_t objID = ObjectIDManager::Instance()->GeneratePersistentID();
MasterPackets::SendPersistentIDResponse(Game::server, packet->systemAddress, requestID, objID);
break;
}
case eMasterMessageType::REQUEST_ZONE_TRANSFER: {
2022-07-28 13:39:57 +00:00
Game::logger->Log("MasterServer", "Received zone transfer req");
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
uint64_t requestID = 0;
uint8_t mythranShift = false;
uint32_t zoneID = 0;
uint32_t zoneClone = 0;
inStream.Read(requestID);
inStream.Read(mythranShift);
inStream.Read(zoneID);
inStream.Read(zoneClone);
if (shutdownSequenceStarted) {
Game::logger->Log("MasterServer", "Shutdown sequence has been started. Not creating a new zone.");
break;
}
Instance* in = Game::im->GetInstance(zoneID, false, zoneClone);
for (auto* instance : Game::im->GetInstances()) {
2022-07-28 13:39:57 +00:00
Game::logger->Log("MasterServer", "Instance: %i/%i/%i -> %i", instance->GetMapID(), instance->GetCloneID(), instance->GetInstanceID(), instance == in);
}
if (in && !in->GetIsReady()) //Instance not ready, make a pending request
{
in->GetPendingRequests().push_back({ requestID, static_cast<bool>(mythranShift), packet->systemAddress });
Game::logger->Log("MasterServer", "Server not ready, adding pending request %llu %i %i", requestID, zoneID, zoneClone);
break;
}
//Instance is ready, transfer
Game::logger->Log("MasterServer", "Responding to transfer request %llu for zone %i %i", requestID, zoneID, zoneClone);
Game::im->RequestAffirmation(in, { requestID, static_cast<bool>(mythranShift), packet->systemAddress });
break;
}
case eMasterMessageType::SERVER_INFO: {
//MasterPackets::HandleServerInfo(packet);
//This is here because otherwise we'd have to include IM in
//non-master servers. This packet allows us to add World
//servers back if master crashed
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
uint32_t theirPort = 0;
uint32_t theirZoneID = 0;
uint32_t theirInstanceID = 0;
ServerType theirServerType;
uint32_t theirIPSize;
std::string theirIP = "";
inStream.Read(theirPort);
inStream.Read(theirZoneID);
inStream.Read(theirInstanceID);
inStream.Read(theirServerType);
inStream.Read(theirIPSize);
char character;
while(theirIPSize-- > 0) {
inStream.Read(character);
theirIP.push_back(character);
}
if (theirServerType == ServerType::World && !Game::im->IsPortInUse(theirPort)) {
Instance* in = new Instance(theirIP, theirPort, theirZoneID, theirInstanceID, 0, 12, 12);
SystemAddress copy;
copy.binaryAddress = packet->systemAddress.binaryAddress;
copy.port = packet->systemAddress.port;
in->SetSysAddr(copy);
Game::im->AddInstance(in);
2022-07-28 13:39:57 +00:00
} else {
auto instance = Game::im->FindInstance(
theirZoneID, static_cast<uint16_t>(theirInstanceID));
if (instance) {
instance->SetSysAddr(packet->systemAddress);
}
}
if (theirServerType == ServerType::Chat) {
SystemAddress copy;
copy.binaryAddress = packet->systemAddress.binaryAddress;
copy.port = packet->systemAddress.port;
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;
}
case eMasterMessageType::SET_SESSION_KEY: {
CINSTREAM_SKIP_HEADER;
uint32_t sessionKey = 0;
inStream.Read(sessionKey);
LUString username(33);
inStream.Read(username);
for (auto it : activeSessions) {
if (it.second == username.string) {
activeSessions.erase(it.first);
CBITSTREAM;
BitstreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::NEW_SESSION_ALERT);
bitStream.Write(sessionKey);
bitStream.Write<uint32_t>(username.string.size());
for (auto character : username.string) {
2022-07-17 03:40:46 +00:00
bitStream.Write(character);
}
SEND_PACKET_BROADCAST;
break;
}
}
activeSessions.insert(std::make_pair(sessionKey, username.string));
Game::logger->Log("MasterServer", "Got sessionKey %i for user %s", sessionKey, username.string.c_str());
break;
}
case eMasterMessageType::REQUEST_SESSION_KEY: {
CINSTREAM_SKIP_HEADER
LUWString username(33);
inStream.Read(username);
for (auto key : activeSessions) {
if (key.second == username.GetAsString()) {
CBITSTREAM;
BitstreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SESSION_KEY_RESPONSE);
bitStream.Write(key.first);
bitStream.Write(LUString(key.second, 64));
Game::server->Send(&bitStream, packet->systemAddress, false);
break;
}
}
break;
}
case eMasterMessageType::PLAYER_ADDED: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
LWOMAPID theirZoneID = 0;
LWOINSTANCEID theirInstanceID = 0;
inStream.Read(theirZoneID);
inStream.Read(theirInstanceID);
auto instance =
Game::im->FindInstance(theirZoneID, theirInstanceID);
if (instance) {
instance->AddPlayer(Player());
2022-07-28 13:39:57 +00:00
} else {
printf("Instance missing? What?");
}
break;
}
case eMasterMessageType::PLAYER_REMOVED: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
LWOMAPID theirZoneID = 0;
LWOINSTANCEID theirInstanceID = 0;
inStream.Read(theirZoneID);
inStream.Read(theirInstanceID);
auto instance =
Game::im->FindInstance(theirZoneID, theirInstanceID);
if (instance) {
instance->RemovePlayer(Player());
}
break;
}
case eMasterMessageType::CREATE_PRIVATE_ZONE: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
uint32_t mapId;
LWOCLONEID cloneId;
2022-07-17 03:40:46 +00:00
std::string password;
inStream.Read(mapId);
inStream.Read(cloneId);
2022-07-17 03:40:46 +00:00
uint32_t len;
inStream.Read<uint32_t>(len);
for (uint32_t i = 0; len > i; i++) {
2022-07-17 03:40:46 +00:00
char character;
inStream.Read<char>(character);
password += character;
}
Game::im->CreatePrivateInstance(mapId, cloneId, password.c_str());
break;
}
case eMasterMessageType::REQUEST_PRIVATE_ZONE: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
uint64_t requestID = 0;
uint8_t mythranShift = false;
2022-07-17 03:40:46 +00:00
std::string password;
inStream.Read(requestID);
inStream.Read(mythranShift);
2022-07-17 03:40:46 +00:00
uint32_t len;
inStream.Read<uint32_t>(len);
for (uint32_t i = 0; i < len; i++) {
2022-07-17 03:40:46 +00:00
char character; inStream.Read<char>(character);
password += character;
}
2022-07-17 03:40:46 +00:00
auto* instance = Game::im->FindPrivateInstance(password.c_str());
2022-07-28 13:39:57 +00:00
Game::logger->Log("MasterServer", "Join private zone: %llu %d %s %p", requestID, mythranShift, password.c_str(), instance);
if (instance == nullptr) {
return;
}
const auto& zone = instance->GetZoneID();
2022-07-28 13:39:57 +00:00
MasterPackets::SendZoneTransferResponse(Game::server, packet->systemAddress, requestID, (bool)mythranShift, zone.GetMapID(), instance->GetInstanceID(), zone.GetCloneID(), instance->GetIP(), instance->GetPort());
break;
}
case eMasterMessageType::WORLD_READY: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
LWOMAPID zoneID;
LWOINSTANCEID instanceID;
inStream.Read(zoneID);
inStream.Read(instanceID);
2022-07-28 13:39:57 +00:00
Game::logger->Log("MasterServer", "Got world ready %i %i", zoneID, instanceID);
auto* instance = Game::im->FindInstance(zoneID, instanceID);
if (instance == nullptr) {
2022-07-28 13:39:57 +00:00
Game::logger->Log("MasterServer", "Failed to find zone to ready");
return;
}
Game::logger->Log("MasterServer", "Ready zone %i", zoneID);
Game::im->ReadyInstance(instance);
break;
}
case eMasterMessageType::PREP_ZONE: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
int32_t zoneID;
inStream.Read(zoneID);
if (shutdownSequenceStarted) {
Game::logger->Log("MasterServer", "Shutdown sequence has been started. Not prepping a new zone.");
break;
} else {
Game::logger->Log("MasterServer", "Prepping zone %i", zoneID);
Game::im->GetInstance(zoneID, false, 0);
}
break;
}
case eMasterMessageType::AFFIRM_TRANSFER_RESPONSE: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
uint64_t requestID;
inStream.Read(requestID);
2022-07-28 13:39:57 +00:00
Game::logger->Log("MasterServer", "Got affirmation of transfer %llu", requestID);
2022-07-28 13:39:57 +00:00
auto* instance = Game::im->GetInstanceBySysAddr(packet->systemAddress);
if (instance == nullptr)
return;
Game::im->AffirmTransfer(instance, requestID);
2022-07-28 13:39:57 +00:00
Game::logger->Log("MasterServer", "Affirmation complete %llu", requestID);
break;
}
case eMasterMessageType::SHUTDOWN_RESPONSE: {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header = inStream.Read(header);
2022-04-10 01:33:38 +00:00
auto* instance = Game::im->GetInstanceBySysAddr(packet->systemAddress);
if (instance == nullptr) {
return;
}
Game::logger->Log("MasterServer", "Got shutdown response from zone %i clone %i instance %i port %i", instance->GetMapID(), instance->GetCloneID(), instance->GetInstanceID(), instance->GetPort());
instance->SetIsShuttingDown(true);
break;
}
case eMasterMessageType::SHUTDOWN_UNIVERSE: {
2022-07-28 13:39:57 +00:00
Game::logger->Log("MasterServer", "Received shutdown universe command, shutting down in 10 minutes.");
2022-12-15 12:02:38 +00:00
Game::shouldShutdown = true;
break;
}
default:
2022-07-28 13:39:57 +00:00
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__
2022-07-28 13:39:57 +00:00
//macOS doesn't need sudo to run on ports < 1024
auto result = system(((BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "ChatServer.exe").string()).c_str());
#else
2022-07-28 13:39:57 +00:00
if (std::atoi(Game::config->GetValue("use_sudo_chat").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "ChatServer").string() + "&").c_str());
2022-07-28 13:39:57 +00:00
} else {
auto result = 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__
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
#elif _WIN32
auto result = system(("start " + (BinaryPathFinder::GetBinaryDir() / "AuthServer.exe").string()).c_str());
#else
2022-07-28 13:39:57 +00:00
if (std::atoi(Game::config->GetValue("use_sudo_auth").c_str())) {
auto result = system(("sudo " + (BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
2022-07-28 13:39:57 +00:00
} else {
auto result = system(((BinaryPathFinder::GetBinaryDir() / "AuthServer").string() + "&").c_str());
}
#endif
}
void ShutdownSequence(int32_t signal) {
if (shutdownSequenceStarted) {
return;
}
if (!Game::im) {
FinalizeShutdown(EXIT_FAILURE);
}
Game::im->SetIsShuttingDown(true);
shutdownSequenceStarted = true;
Game::shouldShutdown = true;
2022-12-15 12:02:38 +00:00
{
CBITSTREAM;
BitstreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN);
2022-12-15 12:02:38 +00:00
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
Game::logger->Log("MasterServer", "Triggered master shutdown");
}
auto* objIdManager = ObjectIDManager::TryInstance();
if (objIdManager) {
objIdManager->SaveToDatabase();
Game::logger->Log("MasterServer", "Saved ObjectIDTracker to DB");
}
// 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()) {
2022-12-15 12:02:38 +00:00
instance->SetIsShuttingDown(true);
}
Game::logger->Log("MasterServer", "Attempting to shutdown instances, max 60 seconds...");
auto t = std::chrono::high_resolution_clock::now();
uint32_t framesSinceShutdownStart = 0;
constexpr uint32_t maxShutdownTime = 60 * mediumFramerate;
bool allInstancesShutdown = false;
Packet* packet = nullptr;
while (true) {
packet = Game::server->Receive();
2022-04-09 21:17:31 +00:00
if (packet) {
HandlePacket(packet);
Game::server->DeallocatePacket(packet);
packet = nullptr;
}
allInstancesShutdown = true;
for (auto* instance : Game::im->GetInstances()) {
if (instance == nullptr) {
continue;
}
if (!instance->GetShutdownComplete()) {
allInstancesShutdown = false;
}
}
if (allInstancesShutdown && authServerMasterPeerSysAddr == UNASSIGNED_SYSTEM_ADDRESS && chatServerMasterPeerSysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
Game::logger->Log("MasterServer", "Finished shutting down MasterServer!");
break;
}
t += std::chrono::milliseconds(mediumFrameDelta);
std::this_thread::sleep_until(t);
framesSinceShutdownStart++;
if (framesSinceShutdownStart == maxShutdownTime) {
Game::logger->Log("MasterServer", "Finished shutting down by timeout!");
break;
}
}
FinalizeShutdown(signal);
}
2022-04-09 22:35:40 +00:00
int32_t FinalizeShutdown(int32_t signal) {
2022-04-09 22:35:40 +00:00
//Delete our objects here:
Database::Destroy("MasterServer");
if (Game::config) delete Game::config;
Address Brick-By-Brick builds not properly saving and make migrations automatic (#725) * Properly store BBB in database Store the BBB data in the database as the received SD0 packet as opposed to just the raw lxfml. Addressed several memory leaks as well. * Add Sd0Conversion Add brick by brick conversion commands with 2 parameters to tell the program what to do with the data. Add zlib -> sd0 conversion. Files look good at a glance but should be tested in game to ensure stability. Tests to come. * moving to laptop ignore this commit. I need to move this to my laptop * Add functionality to delete bad models Adds functionality to delete bad models. Models are batched together and deleted in one commit. More testing is needed to ensure data safety. Positive tests on a live database reveal the broken models were truncated and complete ones were kept around successfully. Tests should be done to ensure larger sd0 models are properly saved and not truncated since this command should be able to be run any time. Valgrind tests need to be run as well to ensure no memory leaks exist. * Delete from query change Changed from delete to delete cascade and instead deleting from properties_contents as opposed to ugc. * Address numerous bugs DELETE CASCADE is not a valid SQL command so this was changed to a better delete statement. Added user confirmation before deleting a broken model. Address appending the string model appending bad data, causing excess deletion. Addressed memory leaks with sql::Blob * Error handling for string * Even more proper handling... * Add bounds check for cli command Output a message if a bad command is used. Update MasterServer.cpp * Remove user interference -Add back in mariadb build jobs so i dont nuke others systems - Remove all user interference and consolidate work into one command since 1 depends on the next. * Add comments test Revert "test" This reverts commit fb831f268b7a2f0ccd20595aff64902ab4f4b4ee. * Update CMakeMariaDBLists.txt Test * Improve migration runner Migration runner now runs automatically. - Resolved an issue where extremely large sql queries caused the database to go into an invalid state. - Made migrations run automatically on server start. - Resolved a tiny memory leak in migration runner? (discarded returned pointer) - Moved sd0 migrations of brick models to be run automatically with migration runner. - Created dummy file to tell when brick migrations have been run. * Update README Updated the README to reflect the new server migration state. * Make model deleter actually delete models My complicated sql actually did nothing... Tested that this new SQL properly gets rid of bad data. * Revert "Update CMakeMariaDBLists.txt" This reverts commit 8b859d8529755d7132cef072b2532589c098f683.
2022-10-24 22:20:36 +00:00
if (Game::im) delete Game::im;
if (Game::server) delete Game::server;
if (Game::logger) delete Game::logger;
2022-04-09 22:35:40 +00:00
if (signal != EXIT_SUCCESS) exit(signal);
return signal;
}