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
# 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"
2021-12-05 17:54:36 +00:00
# include "Diagnostics.h"
# include "dCommonVars.h"
# include "dConfig.h"
# include "dLogger.h"
# 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"
# include "ObjectIDManager.h"
2023-08-05 02:28:07 +00:00
# include "BitstreamUtils.h"
2022-12-05 08:57:58 +00:00
# include "FdbToSqlite.h"
2021-12-05 17:54:36 +00:00
namespace Game {
2022-12-15 14:13:49 +00:00
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 ;
2023-03-20 13:10:52 +00:00
std : : mt19937 randomEngine ;
2021-12-05 17:54:36 +00:00
} //namespace Game
bool shutdownSequenceStarted = false ;
2022-12-16 10:24:02 +00:00
void ShutdownSequence ( int32_t signal = - 1 ) ;
int32_t FinalizeShutdown ( int32_t signal = - 1 ) ;
2021-12-05 17:54:36 +00:00
dLogger * SetupLogger ( ) ;
2021-12-20 09:08:57 +00:00
void StartAuthServer ( ) ;
void StartChatServer ( ) ;
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 ( ) ; } ) ;
2022-12-16 10:24:02 +00:00
signal ( SIGINT , [ ] ( int32_t signal ) { ShutdownSequence ( EXIT_FAILURE ) ; } ) ;
signal ( SIGTERM , [ ] ( int32_t signal ) { ShutdownSequence ( EXIT_FAILURE ) ; } ) ;
2021-12-05 17:54:36 +00:00
//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 ;
2021-12-05 17:54:36 +00:00
2022-12-15 14:13:49 +00:00
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 " ) ;
2023-08-04 02:38:33 +00:00
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 ( ) ) ;
2022-12-06 12:39:09 +00:00
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__ ) ;
2022-10-24 22:20:36 +00:00
//Connect to the MySQL Database
2022-12-04 22:25:58 +00:00
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 " ) ;
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 ;
}
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 ) {
Game : : logger - > Log ( " MasterServer " , " Got an error while setting up assets: %s " , ex . what ( ) ) ;
return EXIT_FAILURE ;
}
2022-10-24 22:20:36 +00:00
MigrationRunner : : RunMigrations ( ) ;
2022-07-18 23:44:21 +00:00
2022-12-22 06:34:11 +00:00
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 {
2023-01-07 05:04:20 +00:00
Game : : logger - > Log ( " MasterServer " ,
2022-12-22 06:34:11 +00:00
" %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 ( ) ) ;
2023-01-07 05:04:20 +00:00
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. " ) ;
2022-12-22 06:34:11 +00:00
}
2023-01-07 05:04:20 +00:00
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 ) {
2022-12-22 06:34:11 +00:00
Game : : logger - > Log ( " MasterServer " , " 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
}
2023-01-07 05:04:20 +00:00
cdClientBuffer . close ( ) ;
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 ) {
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 ;
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 ( ) ;
2022-10-24 22:20:36 +00:00
//Get CDClient initial information
try {
2023-03-17 14:36:21 +00:00
CDClientManager : : Instance ( ) ;
2022-10-24 22:20:36 +00:00
} catch ( CppSQLite3Exception & e ) {
Game : : logger - > Log ( " WorldServer " , " Failed to initialize CDServer SQLite Database " ) ;
2022-11-01 18:21:26 +00:00
Game : : logger - > Log ( " WorldServer " , " May be caused by corrupted file: %s " , ( Game : : assetManager - > GetResPath ( ) / " CDServer.sqlite " ) . string ( ) . c_str ( ) ) ;
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
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 ;
2023-03-05 19:11:32 +00:00
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 ;
}
2021-12-05 17:54:36 +00:00
//Read the password from the console without echoing it.
2023-03-05 19:11:32 +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 {
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 ;
}
2021-12-05 17:54:36 +00:00
2023-03-05 19:11:32 +00:00
Game : : logger - > Log ( " MasterServer " , " Account created successfully! " ) ;
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 ;
uint32_t ourPort = 1000 ;
2022-12-04 22:25:58 +00:00
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 " ) ) ;
2021-12-05 17:54:36 +00:00
2022-12-15 13:46:03 +00:00
Game : : server = new dServer ( Game : : config - > GetValue ( " external_ip " ) , ourPort , 0 , maxClients , true , false , Game : : logger , " " , 0 , ServerType : : Master , Game : : config , & Game : : shouldShutdown ) ;
2021-12-05 17:54:36 +00:00
//Query for the database for a server labeled "master"
auto * masterLookupStatement = Database : : CreatePreppedStmt ( " SELECT id FROM `servers` WHERE `name` = 'master' " ) ;
auto * result = masterLookupStatement - > executeQuery ( ) ;
2022-12-04 22:25:58 +00:00
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
2021-12-05 17:54:36 +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` = ? " ) ;
2022-11-03 03:53:45 +00:00
updateStatement - > setString ( 1 , master_server_ip . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
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 {
2021-12-05 17:54:36 +00:00
//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) " ) ;
2022-11-03 03:53:45 +00:00
insertStatement - > setString ( 1 , master_server_ip . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
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:
2022-12-04 22:25:58 +00:00
if ( Game : : config - > GetValue ( " prestart_servers " ) ! = " " & & Game : : config - > GetValue ( " prestart_servers " ) = = " 1 " ) {
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
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:
2022-12-16 04:39:29 +00:00
if ( framesSinceLastFlush > = logFlushTime ) {
2021-12-05 17:54:36 +00:00
Game : : logger - > Flush ( ) ;
framesSinceLastFlush = 0 ;
2022-07-28 13:39:57 +00:00
} else
2021-12-05 17:54:36 +00:00
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 ;
2021-12-05 17:54:36 +00:00
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
2021-12-05 17:54:36 +00:00
framesSinceLastSQLPing + + ;
//10m shutdown for universe kill command
2022-12-15 12:02:38 +00:00
if ( Game : : shouldShutdown ) {
2022-12-16 04:39:29 +00:00
if ( framesSinceKillUniverseCommand > = shutdownUniverseTime ) {
2022-01-24 23:35:26 +00:00
//Break main loop and exit
break ;
2022-07-28 13:39:57 +00:00
} else
2021-12-05 17:54:36 +00:00
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 {
2021-12-05 17:54:36 +00:00
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 ) ;
}
2022-12-15 13:46:03 +00:00
return FinalizeShutdown ( EXIT_SUCCESS ) ;
2021-12-05 17:54:36 +00:00
}
dLogger * SetupLogger ( ) {
std : : string logPath =
2022-11-27 11:59:59 +00:00
( BinaryPathFinder : : GetBinaryDir ( ) / ( " logs/MasterServer_ " + std : : to_string ( time ( nullptr ) ) + " .log " ) ) . string ( ) ;
2021-12-05 17:54:36 +00:00
bool logToConsole = false ;
2021-12-11 12:29:34 +00:00
bool logDebugStatements = false ;
2021-12-05 17:54:36 +00:00
# ifdef _DEBUG
logToConsole = true ;
2021-12-11 12:29:34 +00:00
logDebugStatements = true ;
2021-12-05 17:54:36 +00:00
# endif
2021-12-11 12:29:34 +00:00
return new dLogger ( logPath , logToConsole , logDebugStatements ) ;
2021-12-05 17:54:36 +00:00
}
void HandlePacket ( Packet * packet ) {
if ( packet - > data [ 0 ] = = ID_DISCONNECTION_NOTIFICATION ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 : {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) ;
uint32_t objID = ObjectIDManager : : Instance ( ) - > GeneratePersistentID ( ) ;
MasterPackets : : SendPersistentIDResponse ( Game : : server , packet - > systemAddress , requestID , objID ) ;
break ;
}
2023-05-03 21:38:32 +00:00
case eMasterMessageType : : REQUEST_ZONE_TRANSFER : {
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) {
Game : : logger - > Log ( " MasterServer " , " Shutdown sequence has been started. Not creating a new zone. " ) ;
break ;
}
2021-12-05 17:54:36 +00:00
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 ) ;
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 } ) ;
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ;
2023-08-05 02:28:07 +00:00
uint32_t theirIPSize ;
2021-12-05 17:54:36 +00:00
std : : string theirIP = " " ;
inStream . Read ( theirPort ) ;
inStream . Read ( theirZoneID ) ;
inStream . Read ( theirInstanceID ) ;
inStream . Read ( theirServerType ) ;
2023-08-05 02:28:07 +00:00
inStream . Read ( theirIPSize ) ;
char character ;
while ( theirIPSize - - > 0 ) {
inStream . Read ( character ) ;
theirIP . push_back ( character ) ;
}
2021-12-05 17:54:36 +00:00
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 {
2021-12-05 17:54:36 +00:00
auto instance = Game : : im - > FindInstance (
theirZoneID , static_cast < uint16_t > ( theirInstanceID ) ) ;
if ( instance ) {
instance - > SetSysAddr ( packet - > systemAddress ) ;
}
}
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 ;
}
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 : {
2023-08-05 02:28:07 +00:00
CINSTREAM_SKIP_HEADER ;
2021-12-05 17:54:36 +00:00
uint32_t sessionKey = 0 ;
inStream . Read ( sessionKey ) ;
2023-08-05 02:28:07 +00:00
LUString username ( 33 ) ;
inStream . Read ( username ) ;
2021-12-05 17:54:36 +00:00
for ( auto it : activeSessions ) {
2023-08-05 02:28:07 +00:00
if ( it . second = = username . string ) {
2021-12-05 17:54:36 +00:00
activeSessions . erase ( it . first ) ;
CBITSTREAM ;
2023-08-05 02:28:07 +00:00
BitstreamUtils : : WriteHeader ( bitStream , eConnectionType : : MASTER , eMasterMessageType : : NEW_SESSION_ALERT ) ;
2021-12-05 17:54:36 +00:00
bitStream . Write ( sessionKey ) ;
2023-08-05 02:28:07 +00:00
bitStream . Write < uint32_t > ( username . string . size ( ) ) ;
for ( auto character : username . string ) {
2022-07-17 03:40:46 +00:00
bitStream . Write ( character ) ;
}
2021-12-05 17:54:36 +00:00
SEND_PACKET_BROADCAST ;
break ;
}
}
2023-08-05 02:28:07 +00:00
activeSessions . insert ( std : : make_pair ( sessionKey , username . string ) ) ;
Game : : logger - > Log ( " MasterServer " , " 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 : {
2023-08-05 02:28:07 +00:00
CINSTREAM_SKIP_HEADER
LUWString username ( 33 ) ;
inStream . Read ( username ) ;
2021-12-05 17:54:36 +00:00
for ( auto key : activeSessions ) {
2023-08-05 02:28:07 +00:00
if ( key . second = = username . GetAsString ( ) ) {
2021-12-05 17:54:36 +00:00
CBITSTREAM ;
2023-08-05 02:28:07 +00:00
BitstreamUtils : : WriteHeader ( bitStream , eConnectionType : : MASTER , eMasterMessageType : : SESSION_KEY_RESPONSE ) ;
2021-12-05 17:54:36 +00:00
bitStream . Write ( key . first ) ;
2023-08-05 02:28:07 +00:00
bitStream . Write ( LUString ( key . second , 64 ) ) ;
2021-12-05 17:54:36 +00:00
Game : : server - > Send ( & bitStream , packet - > systemAddress , false ) ;
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 ( ) ) ;
2022-07-28 13:39:57 +00:00
} 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
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 ) ;
2021-12-05 17:54:36 +00:00
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 ( ) ) ;
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 ) ;
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) {
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " Failed to find zone to ready " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) {
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 ) ;
}
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 ) ;
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " Got affirmation of transfer %llu " , requestID ) ;
2021-12-05 17:54:36 +00:00
2022-07-28 13:39:57 +00:00
auto * instance = Game : : im - > GetInstanceBySysAddr ( packet - > systemAddress ) ;
2021-12-05 17:54:36 +00:00
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 ) ;
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 ;
}
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 : {
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 ;
2021-12-05 17:54:36 +00:00
break ;
}
default :
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " 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
2021-12-20 09:08:57 +00:00
void StartChatServer ( ) {
2022-12-15 13:46:03 +00:00
if ( Game : : shouldShutdown ) {
Game : : logger - > Log ( " MasterServer " , " Currently shutting down. Chat will not be restarted. " ) ;
return ;
}
2021-12-20 09:08:57 +00:00
# ifdef __APPLE__
2022-07-28 13:39:57 +00:00
//macOS doesn't need sudo to run on ports < 1024
2022-12-15 14:13:49 +00:00
auto result = system ( ( ( BinaryPathFinder : : GetBinaryDir ( ) / " ChatServer " ) . string ( ) + " & " ) . c_str ( ) ) ;
2021-12-20 09:08:57 +00:00
# elif _WIN32
2022-12-15 14:13:49 +00:00
auto result = system ( ( " start " + ( BinaryPathFinder : : GetBinaryDir ( ) / " ChatServer.exe " ) . string ( ) ) . c_str ( ) ) ;
2021-12-20 09:08:57 +00:00
# else
2022-07-28 13:39:57 +00:00
if ( std : : atoi ( Game : : config - > GetValue ( " use_sudo_chat " ) . c_str ( ) ) ) {
2022-12-15 14:13:49 +00:00
auto result = system ( ( " sudo " + ( BinaryPathFinder : : GetBinaryDir ( ) / " ChatServer " ) . string ( ) + " & " ) . c_str ( ) ) ;
2022-07-28 13:39:57 +00:00
} else {
2022-12-15 14:13:49 +00:00
auto result = system ( ( ( BinaryPathFinder : : GetBinaryDir ( ) / " ChatServer " ) . string ( ) + " & " ) . c_str ( ) ) ;
2022-12-16 04:39:29 +00:00
}
2021-12-20 09:08:57 +00:00
# endif
2022-12-16 04:39:29 +00:00
}
2021-12-20 09:08:57 +00:00
void StartAuthServer ( ) {
2022-12-15 13:46:03 +00:00
if ( Game : : shouldShutdown ) {
Game : : logger - > Log ( " MasterServer " , " Currently shutting down. Auth will not be restarted. " ) ;
return ;
}
2021-12-20 09:08:57 +00:00
# ifdef __APPLE__
2022-12-15 14:13:49 +00:00
auto result = system ( ( ( BinaryPathFinder : : GetBinaryDir ( ) / " AuthServer " ) . string ( ) + " & " ) . c_str ( ) ) ;
2021-12-20 09:08:57 +00:00
# elif _WIN32
2022-12-15 14:13:49 +00:00
auto result = system ( ( " start " + ( BinaryPathFinder : : GetBinaryDir ( ) / " AuthServer.exe " ) . string ( ) ) . c_str ( ) ) ;
2021-12-20 09:08:57 +00:00
# else
2022-07-28 13:39:57 +00:00
if ( std : : atoi ( Game : : config - > GetValue ( " use_sudo_auth " ) . c_str ( ) ) ) {
2022-12-15 14:13:49 +00:00
auto result = system ( ( " sudo " + ( BinaryPathFinder : : GetBinaryDir ( ) / " AuthServer " ) . string ( ) + " & " ) . c_str ( ) ) ;
2022-07-28 13:39:57 +00:00
} else {
2022-12-15 14:13:49 +00:00
auto result = system ( ( ( BinaryPathFinder : : GetBinaryDir ( ) / " AuthServer " ) . string ( ) + " & " ) . c_str ( ) ) ;
2022-12-16 04:39:29 +00:00
}
2021-12-20 09:08:57 +00:00
# endif
}
2022-12-16 10:24:02 +00:00
void ShutdownSequence ( int32_t signal ) {
2021-12-05 17:54:36 +00:00
if ( shutdownSequenceStarted ) {
return ;
}
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 ;
2022-12-15 13:46:03 +00:00
Game : : shouldShutdown = true ;
2021-12-05 17:54:36 +00:00
2022-12-15 12:02:38 +00:00
{
CBITSTREAM ;
2023-08-05 02:28:07 +00:00
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 " ) ;
2021-12-05 17:54:36 +00:00
}
auto * objIdManager = ObjectIDManager : : TryInstance ( ) ;
2022-12-16 04:39:29 +00:00
if ( objIdManager ) {
2021-12-05 17:54:36 +00:00
objIdManager - > SaveToDatabase ( ) ;
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) ;
}
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " 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 ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " Finished shutting down by timeout! " ) ;
2021-12-05 17:54:36 +00:00
break ;
}
}
2022-12-15 13:46:03 +00:00
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 ;
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
2022-12-15 13:46:03 +00:00
if ( signal ! = EXIT_SUCCESS ) exit ( signal ) ;
return signal ;
2022-07-18 23:44:21 +00:00
}