2021-12-05 17:54:36 +00:00
# include <chrono>
# include <cstdlib>
# include <ctime>
# include <iostream>
# include <map>
# include <string>
# include <thread>
2022-01-21 16:46:19 +00:00
# include <fstream>
2021-12-05 17:54:36 +00:00
# include <bcrypt/BCrypt.hpp>
# include <csignal>
//DLU Includes:
# include "CDClientDatabase.h"
# include "CDClientManager.h"
# include "Database.h"
2022-07-10 19:40:26 +00:00
# include "MigrationRunner.h"
2021-12-05 17:54:36 +00:00
# include "Diagnostics.h"
# include "dCommonVars.h"
# include "dConfig.h"
2023-10-21 23:31:55 +00:00
# include "Logger.h"
2021-12-05 17:54:36 +00:00
# include "dServer.h"
2022-11-01 18:21:26 +00:00
# include "AssetManager.h"
2022-11-27 11:59:59 +00:00
# include "BinaryPathFinder.h"
2023-05-03 21:38:32 +00:00
# include "eConnectionType.h"
# include "eMasterMessageType.h"
2021-12-05 17:54:36 +00:00
//RakNet includes:
# include "RakNetDefines.h"
//Packet includes:
# include "AuthPackets.h"
# include "Game.h"
# include "InstanceManager.h"
# include "MasterPackets.h"
2024-01-05 12:31:22 +00:00
# include "PersistentIDManager.h"
2022-12-05 08:57:58 +00:00
# include "FdbToSqlite.h"
2023-09-21 01:06:28 +00:00
# include "BitStreamUtils.h"
2023-12-30 07:00:43 +00:00
# include "Start.h"
2024-01-06 05:32:09 +00:00
# include "Server.h"
2024-06-09 22:31:57 +00:00
# include "CDZoneTableTable.h"
2024-07-03 22:37:19 +00:00
# include "eGameMasterLevel.h"
2021-12-05 17:54:36 +00:00
namespace Game {
2023-10-21 23:31:55 +00:00
Logger * logger = nullptr ;
2022-12-15 14:13:49 +00:00
dServer * server = nullptr ;
InstanceManager * im = nullptr ;
dConfig * config = nullptr ;
AssetManager * assetManager = nullptr ;
2024-01-02 03:50:00 +00:00
Game : : signal_t lastSignal = 0 ;
bool universeShutdownRequested = false ;
2023-03-20 13:10:52 +00:00
std : : mt19937 randomEngine ;
2021-12-05 17:54:36 +00:00
} //namespace Game
bool shutdownSequenceStarted = false ;
2024-01-02 03:50:00 +00:00
int ShutdownSequence ( int32_t signal = - 1 ) ;
2022-12-16 10:24:02 +00:00
int32_t FinalizeShutdown ( int32_t signal = - 1 ) ;
2021-12-05 17:54:36 +00:00
void HandlePacket ( Packet * packet ) ;
std : : map < uint32_t , std : : string > activeSessions ;
2022-12-15 13:46:03 +00:00
SystemAddress authServerMasterPeerSysAddr ;
2021-12-20 09:08:57 +00:00
SystemAddress chatServerMasterPeerSysAddr ;
2021-12-05 17:54:36 +00:00
int main ( int argc , char * * argv ) {
2022-12-16 04:39:29 +00:00
constexpr uint32_t masterFramerate = mediumFramerate ;
constexpr uint32_t masterFrameDelta = mediumFrameDelta ;
2021-12-05 17:54:36 +00:00
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
2021-12-05 17:54:36 +00:00
//Triggers the shutdown sequence at application exit
2022-12-15 13:46:03 +00:00
std : : atexit ( [ ] ( ) { ShutdownSequence ( ) ; } ) ;
2024-01-02 03:50:00 +00:00
std : : signal ( SIGINT , Game : : OnSignal ) ;
std : : signal ( SIGTERM , Game : : OnSignal ) ;
2021-12-05 17:54:36 +00:00
2024-01-06 05:32:09 +00:00
Game : : config = new dConfig ( " masterconfig.ini " ) ;
2021-12-05 17:54:36 +00:00
//Create all the objects we need to run our service:
2024-01-06 05:32:09 +00:00
Server : : SetupLogger ( " MasterServer " ) ;
2022-01-24 23:41:35 +00:00
if ( ! Game : : logger ) return EXIT_FAILURE ;
2021-12-05 17:54:36 +00:00
2024-01-05 00:21:03 +00:00
if ( ! dConfig : : Exists ( " authconfig.ini " ) ) LOG ( " Could not find authconfig.ini, using default settings " ) ;
if ( ! dConfig : : Exists ( " chatconfig.ini " ) ) LOG ( " Could not find chatconfig.ini, using default settings " ) ;
if ( ! dConfig : : Exists ( " masterconfig.ini " ) ) LOG ( " Could not find masterconfig.ini, using default settings " ) ;
if ( ! dConfig : : Exists ( " sharedconfig.ini " ) ) LOG ( " Could not find sharedconfig.ini, using default settings " ) ;
if ( ! dConfig : : Exists ( " worldconfig.ini " ) ) LOG ( " Could not find worldconfig.ini, using default settings " ) ;
2022-12-15 14:13:49 +00:00
2024-01-05 00:21:03 +00:00
const auto clientNetVersionString = Game : : config - > GetValue ( " client_net_version " ) ;
2024-02-10 11:05:25 +00:00
const uint32_t clientNetVersion = GeneralUtils : : TryParse < uint32_t > ( clientNetVersionString ) . value_or ( 171022 ) ;
2023-08-04 02:38:33 +00:00
2024-01-05 00:21:03 +00:00
LOG ( " Using net version %i " , clientNetVersion ) ;
2023-08-04 02:38:33 +00:00
2023-10-21 23:31:55 +00:00
LOG ( " Starting Master server... " ) ;
2023-12-31 06:26:49 +00:00
LOG ( " Version: %s " , PROJECT_VERSION ) ;
2023-10-21 23:31:55 +00:00
LOG ( " Compiled on: %s " , __TIMESTAMP__ ) ;
2022-12-06 12:39:09 +00:00
2022-10-24 22:20:36 +00:00
//Connect to the MySQL Database
try {
2023-11-18 00:47:18 +00:00
Database : : Connect ( ) ;
2022-10-24 22:20:36 +00:00
} catch ( sql : : SQLException & ex ) {
2023-10-21 23:31:55 +00:00
LOG ( " Got an error while connecting to the database: %s " , ex . what ( ) ) ;
LOG ( " Migrations not run " ) ;
2022-10-24 22:20:36 +00:00
return EXIT_FAILURE ;
}
2022-07-18 23:44:21 +00:00
2022-11-01 18:21:26 +00:00
try {
2022-12-04 22:25:58 +00:00
std : : string clientPathStr = Game : : config - > GetValue ( " client_location " ) ;
2022-11-27 11:59:59 +00:00
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 ) ;
2022-11-01 18:21:26 +00:00
} catch ( std : : runtime_error & ex ) {
2023-10-21 23:31:55 +00:00
LOG ( " Got an error while setting up assets: %s " , ex . what ( ) ) ;
2023-11-09 01:56:59 +00:00
LOG ( " Is the provided client_location in Windows Onedrive? If so, remove it from Onedrive. " ) ;
2022-11-01 18:21:26 +00:00
return EXIT_FAILURE ;
}
2022-10-24 22:20:36 +00:00
MigrationRunner : : RunMigrations ( ) ;
2024-01-04 04:18:27 +00:00
const auto resServerPath = BinaryPathFinder : : GetBinaryDir ( ) / " resServer " ;
const bool cdServerExists = std : : filesystem : : exists ( resServerPath / " CDServer.sqlite " ) ;
2022-12-22 06:34:11 +00:00
const bool oldCDServerExists = std : : filesystem : : exists ( Game : : assetManager - > GetResPath ( ) / " CDServer.sqlite " ) ;
const bool fdbExists = std : : filesystem : : exists ( Game : : assetManager - > GetResPath ( ) / " cdclient.fdb " ) ;
2024-01-04 04:18:27 +00:00
const bool resServerPathExists = std : : filesystem : : is_directory ( resServerPath ) ;
if ( ! resServerPathExists ) {
LOG ( " %s does not exist, creating it. " , ( resServerPath ) . c_str ( ) ) ;
2024-01-05 12:31:22 +00:00
if ( ! std : : filesystem : : create_directories ( resServerPath ) ) {
2024-01-04 04:18:27 +00:00
LOG ( " Failed to create %s " , ( resServerPath ) . string ( ) . c_str ( ) ) ;
return EXIT_FAILURE ;
}
}
2022-12-22 06:34:11 +00:00
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.
2023-10-21 23:31:55 +00:00
LOG ( " CDServer.sqlite is not located at resServer, but is located at res path. Copying file... " ) ;
2024-01-04 04:18:27 +00:00
std : : filesystem : : copy_file ( Game : : assetManager - > GetResPath ( ) / " CDServer.sqlite " , resServerPath / " CDServer.sqlite " ) ;
2022-12-22 06:34:11 +00:00
} else {
2023-10-21 23:31:55 +00:00
LOG ( " %s could not be found in resServer or res. Looking for %s to convert to sqlite. " ,
2024-01-04 04:18:27 +00:00
( resServerPath / " CDServer.sqlite " ) . string ( ) . c_str ( ) ,
( Game : : assetManager - > GetResPath ( ) / " cdclient.fdb " ) . string ( ) . c_str ( ) ) ;
2023-01-07 05:04:20 +00:00
2023-12-23 17:24:16 +00:00
auto cdclientStream = Game : : assetManager - > GetFile ( " cdclient.fdb " ) ;
if ( ! cdclientStream ) {
2024-01-04 04:18:27 +00:00
LOG ( " Failed to load %s " , ( Game : : assetManager - > GetResPath ( ) / " cdclient.fdb " ) . string ( ) . c_str ( ) ) ;
2023-01-07 05:04:20 +00:00
throw std : : runtime_error ( " Aborting initialization due to missing cdclient.fdb. " ) ;
2022-12-22 06:34:11 +00:00
}
2023-01-07 05:04:20 +00:00
2024-01-04 04:18:27 +00:00
LOG ( " Found %s. Converting to SQLite " , ( Game : : assetManager - > GetResPath ( ) / " cdclient.fdb " ) . string ( ) . c_str ( ) ) ;
2023-01-07 05:04:20 +00:00
Game : : logger - > Flush ( ) ;
2024-01-04 04:18:27 +00:00
if ( FdbToSqlite : : Convert ( resServerPath . string ( ) ) . ConvertDatabase ( cdclientStream ) = = false ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to convert fdb to sqlite. " ) ;
2023-01-07 05:04:20 +00:00
return EXIT_FAILURE ;
2022-12-22 06:34:11 +00:00
}
2022-10-30 07:38:43 +00:00
}
2022-10-24 22:20:36 +00:00
}
//Connect to CDClient
try {
2022-12-22 06:34:11 +00:00
CDClientDatabase : : Connect ( ( BinaryPathFinder : : GetBinaryDir ( ) / " resServer " / " CDServer.sqlite " ) . string ( ) ) ;
2022-10-24 22:20:36 +00:00
} catch ( CppSQLite3Exception & e ) {
2023-10-21 23:31:55 +00:00
LOG ( " Unable to connect to CDServer SQLite Database " ) ;
LOG ( " Error: %s " , e . errorMessage ( ) ) ;
LOG ( " Error Code: %i " , e . errorCode ( ) ) ;
2022-10-24 22:20:36 +00:00
return EXIT_FAILURE ;
2022-07-18 23:44:21 +00:00
}
2022-10-30 07:38:43 +00:00
// Run migrations should any need to be run.
MigrationRunner : : RunSQLiteMigrations ( ) ;
2021-12-05 17:54:36 +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 ;
2024-07-03 22:37:19 +00:00
const auto checkIsAdmin = [ ] ( ) {
std : : string admin ;
std : : cout < < " What level of privilege should this account have? Please enter a number between 0 (Player) and 9 (Admin) inclusive. No entry will default to 0. " < < std : : endl ;
std : : cin > > admin ;
return admin ;
} ;
2023-11-18 00:47:18 +00:00
auto accountId = Database : : Get ( ) - > GetAccountInfo ( username ) ;
2024-07-03 22:37:19 +00:00
if ( accountId & & accountId - > id ! = 0 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Account with name \" %s \" already exists " , username . c_str ( ) ) ;
2023-03-05 19:11:32 +00:00
std : : cout < < " Do you want to change the password of that account? [y/n]? " ;
std : : string prompt = " " ;
std : : cin > > prompt ;
2023-10-14 07:01:41 +00:00
if ( prompt = = " y " | | prompt = = " yes " ) {
2023-03-05 19:11:32 +00:00
//Read the password from the console without echoing it.
2023-10-14 07:01:41 +00:00
# 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
2023-03-05 19:11:32 +00:00
// 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 ) ;
2023-11-18 00:47:18 +00:00
Database : : Get ( ) - > UpdateAccountPassword ( accountId - > id , std : : string ( hash , BCRYPT_HASHSIZE ) ) ;
2023-03-05 19:11:32 +00:00
2023-10-21 23:31:55 +00:00
LOG ( " Account \" %s \" password updated successfully! " , username . c_str ( ) ) ;
2023-03-05 19:11:32 +00:00
} else {
2023-10-21 23:31:55 +00:00
LOG ( " Account \" %s \" was not updated. " , username . c_str ( ) ) ;
2023-03-05 19:11:32 +00:00
}
2024-07-03 22:37:19 +00:00
std : : cout < < " Update admin privileges? [y/n]? " ;
std : : string admin ;
std : : cin > > admin ;
bool updateAdmin = admin = = " y " | | admin = = " yes " ;
if ( updateAdmin ) {
auto gmLevel = GeneralUtils : : TryParse < int32_t > ( checkIsAdmin ( ) ) . value_or ( 0 ) ;
if ( gmLevel > 9 | | gmLevel < 0 ) {
LOG ( " Invalid admin level. Defaulting to 0 " ) ;
gmLevel = 0 ;
}
Database : : Get ( ) - > UpdateAccountGmLevel ( accountId - > id , static_cast < eGameMasterLevel > ( gmLevel ) ) ;
}
2023-03-05 19:11:32 +00:00
return EXIT_SUCCESS ;
}
2021-12-05 17:54:36 +00:00
//Read the password from the console without echoing it.
2023-10-14 07:01:41 +00:00
# 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
2021-12-05 17:54:36 +00:00
//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
2023-03-05 19:11:32 +00:00
try {
2023-11-18 00:47:18 +00:00
Database : : Get ( ) - > InsertNewAccount ( username , std : : string ( hash , BCRYPT_HASHSIZE ) ) ;
2024-01-05 12:31:22 +00:00
} catch ( sql : : SQLException & e ) {
LOG ( " A SQL error occurred!: \n %s " , e . what ( ) ) ;
2023-03-05 19:11:32 +00:00
return EXIT_FAILURE ;
}
2021-12-05 17:54:36 +00:00
2023-10-21 23:31:55 +00:00
LOG ( " Account created successfully! " ) ;
2024-07-03 22:37:19 +00:00
accountId = Database : : Get ( ) - > GetAccountInfo ( username ) ;
if ( accountId ) {
auto gmLevel = GeneralUtils : : TryParse < int32_t > ( checkIsAdmin ( ) ) . value_or ( 0 ) ;
if ( gmLevel > 9 | | gmLevel < 0 ) {
LOG ( " Invalid admin level. Defaulting to 0 " ) ;
gmLevel = 0 ;
}
Database : : Get ( ) - > UpdateAccountGmLevel ( accountId - > id , static_cast < eGameMasterLevel > ( gmLevel ) ) ;
}
2022-01-24 23:41:35 +00:00
return EXIT_SUCCESS ;
2021-12-05 17:54:36 +00:00
}
2023-03-20 13:10:52 +00:00
Game : : randomEngine = std : : mt19937 ( time ( 0 ) ) ;
2022-12-16 10:24:02 +00:00
uint32_t maxClients = 999 ;
2024-01-05 00:21:03 +00:00
uint32_t ourPort = 2000 ;
std : : string ourIP = " localhost " ;
const auto maxClientsString = Game : : config - > GetValue ( " max_clients " ) ;
if ( ! maxClientsString . empty ( ) ) maxClients = std : : stoi ( maxClientsString ) ;
const auto masterServerPortString = Game : : config - > GetValue ( " master_server_port " ) ;
if ( ! masterServerPortString . empty ( ) ) ourPort = std : : atoi ( masterServerPortString . c_str ( ) ) ;
const auto externalIPString = Game : : config - > GetValue ( " external_ip " ) ;
if ( ! externalIPString . empty ( ) ) ourIP = externalIPString ;
2021-12-05 17:54:36 +00:00
2024-01-05 00:21:03 +00:00
Game : : server = new dServer ( ourIP , ourPort , 0 , maxClients , true , false , Game : : logger , " " , 0 , ServerType : : Master , Game : : config , & Game : : lastSignal ) ;
2021-12-05 17:54:36 +00:00
2024-01-05 00:21:03 +00:00
std : : string master_server_ip = " localhost " ;
const auto masterServerIPString = Game : : config - > GetValue ( " master_ip " ) ;
if ( ! masterServerIPString . empty ( ) ) master_server_ip = masterServerIPString ;
2021-12-05 17:54:36 +00:00
2024-01-05 00:21:03 +00:00
if ( master_server_ip = = " " ) master_server_ip = Game : : server - > GetIP ( ) ;
2021-12-08 13:57:16 +00:00
2023-11-18 00:47:18 +00:00
Database : : Get ( ) - > SetMasterIp ( master_server_ip , Game : : server - > GetPort ( ) ) ;
2021-12-05 17:54:36 +00:00
//Create additional objects here:
2024-01-05 12:31:22 +00:00
PersistentIDManager : : Initialize ( ) ;
2021-12-05 17:54:36 +00:00
Game : : im = new InstanceManager ( Game : : logger , Game : : server - > GetIP ( ) ) ;
2024-06-09 22:31:57 +00:00
//Get CDClient initial information
try {
CDClientManager : : LoadValuesFromDatabase ( ) ;
} catch ( CppSQLite3Exception & e ) {
LOG ( " Failed to initialize CDServer SQLite Database " ) ;
LOG ( " May be caused by corrupted file: %s " , ( Game : : assetManager - > GetResPath ( ) / " CDServer.sqlite " ) . string ( ) . c_str ( ) ) ;
LOG ( " Error: %s " , e . errorMessage ( ) ) ;
LOG ( " Error Code: %i " , e . errorCode ( ) ) ;
return EXIT_FAILURE ;
}
2021-12-05 17:54:36 +00:00
//Depending on the config, start up servers:
2024-01-05 00:21:03 +00:00
if ( Game : : config - > GetValue ( " prestart_servers " ) ! = " 0 " ) {
2021-12-20 09:08:57 +00:00
StartChatServer ( ) ;
2021-12-05 17:54:36 +00:00
2023-03-26 10:09:04 +00:00
Game : : im - > GetInstance ( 0 , false , 0 ) ;
Game : : im - > GetInstance ( 1000 , false , 0 ) ;
2021-12-20 09:08:57 +00:00
StartAuthServer ( ) ;
2021-12-05 17:54:36 +00:00
}
auto t = std : : chrono : : high_resolution_clock : : now ( ) ;
Packet * packet = nullptr ;
2022-12-16 04:39:29 +00:00
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 ;
2022-12-16 10:24:02 +00:00
uint32_t framesSinceLastFlush = 0 ;
uint32_t framesSinceLastSQLPing = 0 ;
uint32_t framesSinceKillUniverseCommand = 0 ;
2021-12-05 17:54:36 +00:00
2024-01-02 03:50:00 +00:00
Game : : logger - > Flush ( ) ;
while ( ! Game : : ShouldShutdown ( ) ) {
2021-12-05 17:54:36 +00:00
//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:
2022-12-16 04:39:29 +00:00
if ( framesSinceLastFlush > = logFlushTime ) {
2021-12-05 17:54:36 +00:00
Game : : logger - > Flush ( ) ;
framesSinceLastFlush = 0 ;
} else
framesSinceLastFlush + + ;
//Every 10 min we ping our sql server to keep it alive hopefully:
2022-12-16 04:39:29 +00:00
if ( framesSinceLastSQLPing > = sqlPingTime ) {
2021-12-05 17:54:36 +00:00
//Find out the master's IP for absolutely no reason:
std : : string masterIP ;
2022-12-16 10:24:02 +00:00
uint32_t masterPort ;
2023-11-18 00:47:18 +00:00
auto masterInfo = Database : : Get ( ) - > GetMasterInfo ( ) ;
if ( masterInfo ) {
masterIP = masterInfo - > ip ;
masterPort = masterInfo - > port ;
2021-12-05 17:54:36 +00:00
}
framesSinceLastSQLPing = 0 ;
} else
framesSinceLastSQLPing + + ;
//10m shutdown for universe kill command
2024-01-02 03:50:00 +00:00
if ( Game : : universeShutdownRequested ) {
2022-12-16 04:39:29 +00:00
if ( framesSinceKillUniverseCommand > = shutdownUniverseTime ) {
2022-01-24 23:35:26 +00:00
//Break main loop and exit
2024-01-02 03:50:00 +00:00
Game : : lastSignal = - 1 ;
2021-12-05 17:54:36 +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 + + ;
} else {
affirmTimeout = 0 ;
}
instance - > SetAffirmationTimeout ( affirmTimeout ) ;
2022-12-16 04:39:29 +00:00
if ( affirmTimeout = = instanceReadyTimeout ) {
2021-12-05 17:54:36 +00:00
instance - > Shutdown ( ) ;
2022-07-18 23:08:33 +00:00
instance - > SetIsShuttingDown ( true ) ;
2021-12-05 17:54:36 +00:00
Game : : im - > RedirectPendingRequests ( instance ) ;
}
}
//Remove dead instances
for ( auto * instance : instances ) {
if ( instance = = nullptr ) {
break ;
}
if ( instance - > GetShutdownComplete ( ) ) {
Game : : im - > RemoveInstance ( instance ) ;
}
}
2022-12-16 04:39:29 +00:00
t + = std : : chrono : : milliseconds ( masterFrameDelta ) ;
2021-12-05 17:54:36 +00:00
std : : this_thread : : sleep_until ( t ) ;
}
2024-01-02 03:50:00 +00:00
return ShutdownSequence ( EXIT_SUCCESS ) ;
2021-12-05 17:54:36 +00:00
}
void HandlePacket ( Packet * packet ) {
2024-05-31 04:53:03 +00:00
if ( packet - > length < 1 ) return ;
2021-12-05 17:54:36 +00:00
if ( packet - > data [ 0 ] = = ID_DISCONNECTION_NOTIFICATION ) {
2023-10-21 23:31:55 +00:00
LOG ( " A server has disconnected " ) ;
2021-12-05 17:54:36 +00:00
//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 ) {
2023-10-21 23:31:55 +00:00
LOG ( " Actually disconnected from zone %i clone %i instance %i port %i " , instance - > GetMapID ( ) , instance - > GetCloneID ( ) , instance - > GetInstanceID ( ) , instance - > GetPort ( ) ) ;
2021-12-05 17:54:36 +00:00
Game : : im - > RemoveInstance ( instance ) ; //Delete the old
}
2021-12-20 09:08:57 +00:00
2022-12-15 13:46:03 +00:00
if ( packet - > systemAddress = = chatServerMasterPeerSysAddr ) {
chatServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS ;
2021-12-20 09:08:57 +00:00
StartChatServer ( ) ;
}
2022-12-15 13:46:03 +00:00
if ( packet - > systemAddress = = authServerMasterPeerSysAddr ) {
authServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS ;
StartAuthServer ( ) ;
}
2021-12-05 17:54:36 +00:00
}
if ( packet - > data [ 0 ] = = ID_CONNECTION_LOST ) {
2023-10-21 23:31:55 +00:00
LOG ( " A server has lost the connection " ) ;
2021-12-05 17:54:36 +00:00
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
}
2021-12-20 09:08:57 +00:00
2022-12-15 13:46:03 +00:00
if ( packet - > systemAddress = = chatServerMasterPeerSysAddr ) {
chatServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS ;
2021-12-20 09:08:57 +00:00
StartChatServer ( ) ;
}
2022-12-15 13:46:03 +00:00
if ( packet - > systemAddress = = authServerMasterPeerSysAddr ) {
authServerMasterPeerSysAddr = UNASSIGNED_SYSTEM_ADDRESS ;
StartAuthServer ( ) ;
}
2021-12-05 17:54:36 +00:00
}
2023-05-06 18:32:53 +00:00
if ( packet - > length < 4 ) return ;
2023-05-03 21:38:32 +00:00
if ( static_cast < eConnectionType > ( packet - > data [ 1 ] ) = = eConnectionType : : MASTER ) {
switch ( static_cast < eMasterMessageType > ( packet - > data [ 3 ] ) ) {
case eMasterMessageType : : REQUEST_PERSISTENT_ID : {
2023-10-21 23:31:55 +00:00
LOG ( " A persistent ID req " ) ;
2021-12-05 17:54:36 +00:00
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
uint64_t requestID = 0 ;
inStream . Read ( requestID ) ;
2024-01-05 12:31:22 +00:00
uint32_t objID = PersistentIDManager : : GeneratePersistentID ( ) ;
2021-12-05 17:54:36 +00:00
MasterPackets : : SendPersistentIDResponse ( Game : : server , packet - > systemAddress , requestID , objID ) ;
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : REQUEST_ZONE_TRANSFER : {
2023-10-21 23:31:55 +00:00
LOG ( " Received zone transfer req " ) ;
2021-12-05 17:54:36 +00:00
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 ) ;
2022-12-16 04:39:29 +00:00
if ( shutdownSequenceStarted ) {
2023-10-21 23:31:55 +00:00
LOG ( " Shutdown sequence has been started. Not creating a new zone. " ) ;
2022-12-16 04:39:29 +00:00
break ;
}
2021-12-05 17:54:36 +00:00
Instance * in = Game : : im - > GetInstance ( zoneID , false , zoneClone ) ;
for ( auto * instance : Game : : im - > GetInstances ( ) ) {
2023-10-21 23:31:55 +00:00
LOG ( " Instance: %i/%i/%i -> %i " , instance - > GetMapID ( ) , instance - > GetCloneID ( ) , instance - > GetInstanceID ( ) , instance = = in ) ;
2021-12-05 17:54:36 +00:00
}
2022-12-16 04:39:29 +00:00
if ( in & & ! in - > GetIsReady ( ) ) //Instance not ready, make a pending request
2021-12-05 17:54:36 +00:00
{
in - > GetPendingRequests ( ) . push_back ( { requestID , static_cast < bool > ( mythranShift ) , packet - > systemAddress } ) ;
2023-10-21 23:31:55 +00:00
LOG ( " Server not ready, adding pending request %llu %i %i " , requestID , zoneID , zoneClone ) ;
2021-12-05 17:54:36 +00:00
break ;
}
//Instance is ready, transfer
2023-10-21 23:31:55 +00:00
LOG ( " Responding to transfer request %llu for zone %i %i " , requestID , zoneID , zoneClone ) ;
2021-12-05 17:54:36 +00:00
Game : : im - > RequestAffirmation ( in , { requestID , static_cast < bool > ( mythranShift ) , packet - > systemAddress } ) ;
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : SERVER_INFO : {
2021-12-05 17:54:36 +00:00
//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 ;
2024-01-12 20:23:44 +00:00
LUString theirIP ;
2021-12-05 17:54:36 +00:00
inStream . Read ( theirPort ) ;
inStream . Read ( theirZoneID ) ;
inStream . Read ( theirInstanceID ) ;
inStream . Read ( theirServerType ) ;
2024-01-07 08:02:27 +00:00
inStream . Read ( theirIP ) ;
2021-12-05 17:54:36 +00:00
2023-10-14 07:01:41 +00:00
if ( theirServerType = = ServerType : : World ) {
if ( ! Game : : im - > IsPortInUse ( theirPort ) ) {
2024-01-07 08:02:27 +00:00
Instance * in = new Instance ( theirIP . string , theirPort , theirZoneID , theirInstanceID , 0 , 12 , 12 ) ;
2023-10-14 07:01:41 +00:00
SystemAddress copy ;
copy . binaryAddress = packet - > systemAddress . binaryAddress ;
copy . port = packet - > systemAddress . port ;
in - > SetSysAddr ( copy ) ;
Game : : im - > AddInstance ( in ) ;
} else {
auto instance = Game : : im - > FindInstance (
theirZoneID , static_cast < uint16_t > ( theirInstanceID ) ) ;
if ( instance ) {
instance - > SetSysAddr ( packet - > systemAddress ) ;
}
2021-12-05 17:54:36 +00:00
}
}
2021-12-20 09:08:57 +00:00
if ( theirServerType = = ServerType : : Chat ) {
SystemAddress copy ;
copy . binaryAddress = packet - > systemAddress . binaryAddress ;
copy . port = packet - > systemAddress . port ;
chatServerMasterPeerSysAddr = copy ;
}
2022-12-15 13:46:03 +00:00
if ( theirServerType = = ServerType : : Auth ) {
SystemAddress copy ;
copy . binaryAddress = packet - > systemAddress . binaryAddress ;
copy . port = packet - > systemAddress . port ;
authServerMasterPeerSysAddr = copy ;
}
2023-10-21 23:31:55 +00:00
LOG ( " Received server info, instance: %i port: %i " , theirInstanceID , theirPort ) ;
2021-12-05 17:54:36 +00:00
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : SET_SESSION_KEY : {
2024-01-07 08:02:27 +00:00
CINSTREAM_SKIP_HEADER ;
2021-12-05 17:54:36 +00:00
uint32_t sessionKey = 0 ;
inStream . Read ( sessionKey ) ;
2024-01-12 20:23:44 +00:00
LUString username ;
2024-01-07 08:02:27 +00:00
inStream . Read ( username ) ;
2024-07-03 22:37:19 +00:00
2021-12-05 17:54:36 +00:00
for ( auto it : activeSessions ) {
2024-01-07 08:02:27 +00:00
if ( it . second = = username . string ) {
2021-12-05 17:54:36 +00:00
activeSessions . erase ( it . first ) ;
CBITSTREAM ;
2023-09-21 01:06:28 +00:00
BitStreamUtils : : WriteHeader ( bitStream , eConnectionType : : MASTER , eMasterMessageType : : NEW_SESSION_ALERT ) ;
2021-12-05 17:54:36 +00:00
bitStream . Write ( sessionKey ) ;
2024-01-07 08:02:27 +00:00
bitStream . Write ( username ) ;
2021-12-05 17:54:36 +00:00
SEND_PACKET_BROADCAST ;
break ;
}
}
2024-01-07 08:02:27 +00:00
activeSessions . insert ( std : : make_pair ( sessionKey , username . string ) ) ;
LOG ( " Got sessionKey %i for user %s " , sessionKey , username . string . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : REQUEST_SESSION_KEY : {
2024-01-07 08:02:27 +00:00
CINSTREAM_SKIP_HEADER ;
2024-01-12 20:23:44 +00:00
LUWString username ;
2024-01-07 08:02:27 +00:00
inStream . Read ( username ) ;
LOG ( " Requesting session key for %s " , username . GetAsString ( ) . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
for ( auto key : activeSessions ) {
2024-01-07 08:02:27 +00:00
if ( key . second = = username . GetAsString ( ) ) {
2021-12-05 17:54:36 +00:00
CBITSTREAM ;
2023-09-21 01:06:28 +00:00
BitStreamUtils : : WriteHeader ( bitStream , eConnectionType : : MASTER , eMasterMessageType : : SESSION_KEY_RESPONSE ) ;
2021-12-05 17:54:36 +00:00
bitStream . Write ( key . first ) ;
2024-01-07 08:02:27 +00:00
bitStream . Write ( username ) ;
2024-02-27 05:43:33 +00:00
Game : : server - > Send ( bitStream , packet - > systemAddress , false ) ;
2021-12-05 17:54:36 +00:00
break ;
}
}
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : PLAYER_ADDED : {
2021-12-05 17:54:36 +00:00
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 ( ) ) ;
} else {
2022-07-25 02:26:51 +00:00
printf ( " Instance missing? What? " ) ;
2021-12-05 17:54:36 +00:00
}
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : PLAYER_REMOVED : {
2021-12-05 17:54:36 +00:00
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 ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : CREATE_PRIVATE_ZONE : {
2021-12-05 17:54:36 +00:00
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 ;
2021-12-05 17:54:36 +00:00
inStream . Read ( mapId ) ;
inStream . Read ( cloneId ) ;
2022-07-17 03:40:46 +00:00
uint32_t len ;
inStream . Read < uint32_t > ( len ) ;
2022-12-16 10:24:02 +00:00
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 ( ) ) ;
2021-12-05 17:54:36 +00:00
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : REQUEST_PRIVATE_ZONE : {
2021-12-05 17:54:36 +00:00
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 ;
2021-12-05 17:54:36 +00:00
inStream . Read ( requestID ) ;
inStream . Read ( mythranShift ) ;
2022-07-25 02:26:51 +00:00
2022-07-17 03:40:46 +00:00
uint32_t len ;
inStream . Read < uint32_t > ( len ) ;
2022-12-16 10:24:02 +00:00
for ( uint32_t i = 0 ; i < len ; i + + ) {
2022-07-17 03:40:46 +00:00
char character ; inStream . Read < char > ( character ) ;
password + = character ;
}
2021-12-05 17:54:36 +00:00
2022-07-17 03:40:46 +00:00
auto * instance = Game : : im - > FindPrivateInstance ( password . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
2023-10-21 23:31:55 +00:00
LOG ( " Join private zone: %llu %d %s %p " , requestID , mythranShift , password . c_str ( ) , instance ) ;
2021-12-05 17:54:36 +00:00
if ( instance = = nullptr ) {
return ;
}
const auto & zone = instance - > GetZoneID ( ) ;
2024-03-18 01:48:09 +00:00
MasterPackets : : SendZoneTransferResponse ( Game : : server , packet - > systemAddress , requestID , static_cast < bool > ( mythranShift ) , zone . GetMapID ( ) , instance - > GetInstanceID ( ) , zone . GetCloneID ( ) , instance - > GetIP ( ) , instance - > GetPort ( ) ) ;
2021-12-05 17:54:36 +00:00
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : WORLD_READY : {
2021-12-05 17:54:36 +00:00
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
LWOMAPID zoneID ;
LWOINSTANCEID instanceID ;
inStream . Read ( zoneID ) ;
inStream . Read ( instanceID ) ;
2023-10-21 23:31:55 +00:00
LOG ( " Got world ready %i %i " , zoneID , instanceID ) ;
2021-12-05 17:54:36 +00:00
auto * instance = Game : : im - > FindInstance ( zoneID , instanceID ) ;
if ( instance = = nullptr ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to find zone to ready " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2023-10-21 23:31:55 +00:00
LOG ( " Ready zone %i " , zoneID ) ;
2021-12-05 17:54:36 +00:00
Game : : im - > ReadyInstance ( instance ) ;
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : PREP_ZONE : {
2021-12-05 17:54:36 +00:00
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
2022-12-16 10:24:02 +00:00
int32_t zoneID ;
2021-12-05 17:54:36 +00:00
inStream . Read ( zoneID ) ;
2022-12-16 04:39:29 +00:00
if ( shutdownSequenceStarted ) {
2023-10-21 23:31:55 +00:00
LOG ( " Shutdown sequence has been started. Not prepping a new zone. " ) ;
2022-12-16 04:39:29 +00:00
break ;
} else {
2023-10-21 23:31:55 +00:00
LOG ( " Prepping zone %i " , zoneID ) ;
2022-12-16 04:39:29 +00:00
Game : : im - > GetInstance ( zoneID , false , 0 ) ;
}
2021-12-05 17:54:36 +00:00
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : AFFIRM_TRANSFER_RESPONSE : {
2021-12-05 17:54:36 +00:00
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
uint64_t requestID ;
inStream . Read ( requestID ) ;
2023-10-21 23:31:55 +00:00
LOG ( " Got affirmation of transfer %llu " , requestID ) ;
2021-12-05 17:54:36 +00:00
auto * instance = Game : : im - > GetInstanceBySysAddr ( packet - > systemAddress ) ;
if ( instance = = nullptr )
return ;
Game : : im - > AffirmTransfer ( instance , requestID ) ;
2023-10-21 23:31:55 +00:00
LOG ( " Affirmation complete %llu " , requestID ) ;
2021-12-05 17:54:36 +00:00
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : SHUTDOWN_RESPONSE : {
2021-12-05 17:54:36 +00:00
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 ) ;
2021-12-05 17:54:36 +00:00
if ( instance = = nullptr ) {
return ;
}
2023-10-21 23:31:55 +00:00
LOG ( " Got shutdown response from zone %i clone %i instance %i port %i " , instance - > GetMapID ( ) , instance - > GetCloneID ( ) , instance - > GetInstanceID ( ) , instance - > GetPort ( ) ) ;
2022-07-18 23:08:33 +00:00
instance - > SetIsShuttingDown ( true ) ;
2021-12-05 17:54:36 +00:00
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : SHUTDOWN_UNIVERSE : {
2023-10-21 23:31:55 +00:00
LOG ( " Received shutdown universe command, shutting down in 10 minutes. " ) ;
2024-01-02 03:50:00 +00:00
Game : : universeShutdownRequested = true ;
2021-12-05 17:54:36 +00:00
break ;
}
default :
2023-10-21 23:31:55 +00:00
LOG ( " Unknown master packet ID from server: %i " , packet - > data [ 3 ] ) ;
2021-12-05 17:54:36 +00:00
}
}
2022-12-16 04:39:29 +00:00
}
2021-12-05 17:54:36 +00:00
2024-01-02 03:50:00 +00:00
int ShutdownSequence ( int32_t signal ) {
2024-01-03 00:28:17 +00:00
if ( ! Game : : logger ) return - 1 ;
2024-01-02 03:50:00 +00:00
LOG ( " Recieved Signal %d " , signal ) ;
2021-12-05 17:54:36 +00:00
if ( shutdownSequenceStarted ) {
2024-01-02 03:50:00 +00:00
LOG ( " Duplicate Shutdown Sequence " ) ;
return - 1 ;
2021-12-05 17:54:36 +00:00
}
2022-12-16 04:39:29 +00:00
if ( ! Game : : im ) {
FinalizeShutdown ( EXIT_FAILURE ) ;
}
Game : : im - > SetIsShuttingDown ( true ) ;
2021-12-05 17:54:36 +00:00
shutdownSequenceStarted = true ;
2024-01-02 03:50:00 +00:00
Game : : lastSignal = - 1 ;
2021-12-05 17:54:36 +00:00
2022-12-15 12:02:38 +00:00
{
CBITSTREAM ;
2023-09-21 01:06:28 +00:00
BitStreamUtils : : WriteHeader ( bitStream , eConnectionType : : MASTER , eMasterMessageType : : SHUTDOWN ) ;
2024-02-27 05:43:33 +00:00
Game : : server - > Send ( bitStream , UNASSIGNED_SYSTEM_ADDRESS , true ) ;
2023-10-21 23:31:55 +00:00
LOG ( " Triggered master shutdown " ) ;
2021-12-05 17:54:36 +00:00
}
2024-01-05 12:31:22 +00:00
PersistentIDManager : : SaveToDatabase ( ) ;
LOG ( " Saved ObjectIDTracker to DB " ) ;
2021-12-05 17:54:36 +00:00
2022-12-15 13:46:03 +00:00
// A server might not be finished spinning up yet, remove all of those here.
2022-12-16 04:39:29 +00:00
for ( auto * instance : Game : : im - > GetInstances ( ) ) {
2022-12-15 13:46:03 +00:00
if ( ! instance - > GetIsReady ( ) ) {
Game : : im - > RemoveInstance ( instance ) ;
}
2021-12-05 17:54:36 +00:00
}
2022-12-16 04:39:29 +00:00
for ( auto * instance : Game : : im - > GetInstances ( ) ) {
2022-12-15 12:02:38 +00:00
instance - > SetIsShuttingDown ( true ) ;
}
2023-10-21 23:31:55 +00:00
LOG ( " Attempting to shutdown instances, max 60 seconds... " ) ;
2021-12-13 01:31:39 +00:00
2022-12-16 04:39:29 +00:00
auto t = std : : chrono : : high_resolution_clock : : now ( ) ;
uint32_t framesSinceShutdownStart = 0 ;
constexpr uint32_t maxShutdownTime = 60 * mediumFramerate ;
bool allInstancesShutdown = false ;
Packet * packet = nullptr ;
2021-12-05 17:54:36 +00:00
while ( true ) {
2022-12-16 04:39:29 +00:00
packet = Game : : server - > Receive ( ) ;
2022-04-09 21:17:31 +00:00
if ( packet ) {
HandlePacket ( packet ) ;
Game : : server - > DeallocatePacket ( packet ) ;
packet = nullptr ;
}
2022-07-25 02:26:51 +00:00
2022-12-16 04:39:29 +00:00
allInstancesShutdown = true ;
2021-12-05 17:54:36 +00:00
for ( auto * instance : Game : : im - > GetInstances ( ) ) {
if ( instance = = nullptr ) {
continue ;
}
if ( ! instance - > GetShutdownComplete ( ) ) {
2022-12-16 04:39:29 +00:00
allInstancesShutdown = false ;
2021-12-05 17:54:36 +00:00
}
}
2022-12-16 04:39:29 +00:00
if ( allInstancesShutdown & & authServerMasterPeerSysAddr = = UNASSIGNED_SYSTEM_ADDRESS & & chatServerMasterPeerSysAddr = = UNASSIGNED_SYSTEM_ADDRESS ) {
2023-10-21 23:31:55 +00:00
LOG ( " Finished shutting down MasterServer! " ) ;
2021-12-05 17:54:36 +00:00
break ;
}
2022-12-16 04:39:29 +00:00
t + = std : : chrono : : milliseconds ( mediumFrameDelta ) ;
2021-12-05 17:54:36 +00:00
std : : this_thread : : sleep_until ( t ) ;
2022-12-16 04:39:29 +00:00
framesSinceShutdownStart + + ;
2021-12-05 17:54:36 +00:00
2022-12-16 04:39:29 +00:00
if ( framesSinceShutdownStart = = maxShutdownTime ) {
2023-10-21 23:31:55 +00:00
LOG ( " Finished shutting down by timeout! " ) ;
2021-12-05 17:54:36 +00:00
break ;
}
}
2024-01-02 03:50:00 +00:00
return FinalizeShutdown ( signal ) ;
2021-12-05 17:54:36 +00:00
}
2022-04-09 22:35:40 +00:00
2022-12-16 10:24:02 +00:00
int32_t FinalizeShutdown ( int32_t signal ) {
2022-04-09 22:35:40 +00:00
//Delete our objects here:
Database : : Destroy ( " MasterServer " ) ;
2022-12-04 22:25:58 +00:00
if ( Game : : config ) delete Game : : config ;
2024-01-03 00:28:17 +00:00
Game : : config = nullptr ;
2022-10-24 22:20:36 +00:00
if ( Game : : im ) delete Game : : im ;
2024-01-03 00:28:17 +00:00
Game : : im = nullptr ;
2022-10-24 22:20:36 +00:00
if ( Game : : server ) delete Game : : server ;
2024-01-03 00:28:17 +00:00
Game : : server = nullptr ;
2022-10-24 22:20:36 +00:00
if ( Game : : logger ) delete Game : : logger ;
2024-01-03 00:28:17 +00:00
Game : : logger = nullptr ;
2022-04-09 22:35:40 +00:00
2022-12-15 13:46:03 +00:00
if ( signal ! = EXIT_SUCCESS ) exit ( signal ) ;
return signal ;
2022-07-18 23:44:21 +00:00
}