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"
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"
# include "PacketUtils.h"
# include "dMessageIdentifiers.h"
namespace Game {
dLogger * logger ;
dServer * server ;
InstanceManager * im ;
dConfig * config ;
2022-11-01 18:21:26 +00:00
AssetManager * assetManager ;
2021-12-05 17:54:36 +00:00
} //namespace Game
bool shutdownSequenceStarted = false ;
void ShutdownSequence ( ) ;
2022-04-09 22:35:40 +00:00
int FinalizeShutdown ( ) ;
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 ;
bool shouldShutdown = false ;
2021-12-20 09:08:57 +00:00
SystemAddress chatServerMasterPeerSysAddr ;
2021-12-05 17:54:36 +00:00
int main ( int argc , char * * argv ) {
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
std : : atexit ( ShutdownSequence ) ;
signal ( SIGINT , [ ] ( int ) { ShutdownSequence ( ) ; } ) ;
2022-01-09 00:25:45 +00:00
signal ( SIGTERM , [ ] ( int ) { ShutdownSequence ( ) ; } ) ;
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-07-25 02:26:51 +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__ ) ;
2021-12-05 17:54:36 +00:00
//Read our config:
dConfig config ( " masterconfig.ini " ) ;
Game : : config = & config ;
Game : : logger - > SetLogToConsole ( bool ( std : : stoi ( config . GetValue ( " log_to_console " ) ) ) ) ;
2021-12-13 15:57:43 +00:00
Game : : logger - > SetLogDebugStatements ( config . GetValue ( " log_debug_statements " ) = = " 1 " ) ;
2022-07-25 02:26:51 +00:00
2022-10-24 22:20:36 +00:00
//Connect to the MySQL Database
std : : string mysql_host = config . GetValue ( " mysql_host " ) ;
std : : string mysql_database = config . GetValue ( " mysql_database " ) ;
std : : string mysql_username = config . GetValue ( " mysql_username " ) ;
std : : string mysql_password = config . GetValue ( " mysql_password " ) ;
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 {
std : : string client_path = config . GetValue ( " client_location " ) ;
if ( client_path . empty ( ) ) client_path = " ./res " ;
Game : : assetManager = new AssetManager ( config . GetValue ( " client_location " ) ) ;
} 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-11-01 18:21:26 +00:00
// Check CDClient exists
if ( ! std : : filesystem : : exists ( Game : : assetManager - > GetResPath ( ) / " CDServer.sqlite " ) ) {
Game : : logger - > Log ( " WorldServer " , " CDServer.sqlite could not be opened. Looking for cdclient.fdb to convert to sqlite. " ) ;
if ( ! std : : filesystem : : exists ( Game : : assetManager - > GetResPath ( ) / " cdclient.fdb " ) ) {
Game : : logger - > Log ( " WorldServer " , " cdclient.fdb could not be opened. Please move a cdclient.fdb or an already converted database to build/res. " ) ;
2022-10-30 07:38:43 +00:00
return EXIT_FAILURE ;
}
2022-11-01 18:21:26 +00:00
Game : : logger - > Log ( " WorldServer " , " Found cdclient.fdb. Clearing cdserver migration_history then copying and converting to sqlite. " ) ;
2022-10-30 07:38:43 +00:00
auto stmt = Database : : CreatePreppedStmt ( R " #(DELETE FROM migration_history WHERE name LIKE " % cdserver % " ;)# " ) ;
stmt - > executeUpdate ( ) ;
delete stmt ;
2022-11-01 18:21:26 +00:00
std : : string res = " python3 ../thirdparty/docker-utils/utils/fdb_to_sqlite.py " + ( Game : : assetManager - > GetResPath ( ) / " cdclient.fdb " ) . string ( ) ;
int result = system ( res . c_str ( ) ) ;
if ( result ! = 0 ) {
2022-10-30 07:38:43 +00:00
Game : : logger - > Log ( " MasterServer " , " Failed to convert fdb to sqlite " ) ;
return EXIT_FAILURE ;
}
2022-11-01 18:21:26 +00:00
if ( std : : rename ( " ./cdclient.sqlite " , ( Game : : assetManager - > GetResPath ( ) / " CDServer.sqlite " ) . string ( ) . c_str ( ) ) ! = 0 ) {
Game : : logger - > Log ( " MasterServer " , " Failed to move cdclient file. " ) ;
2022-10-30 07:38:43 +00:00
return EXIT_FAILURE ;
}
2022-10-24 22:20:36 +00:00
}
//Connect to CDClient
try {
2022-11-01 18:21:26 +00:00
CDClientDatabase : : Connect ( ( Game : : assetManager - > GetResPath ( ) / " 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 {
CDClientManager : : Instance ( ) - > Initialize ( ) ;
} 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 ;
//Read the password from the console without echoing it.
# ifdef __linux__
//This function is obsolete, but it only meant to be used by the
//sysadmin to create their first account.
password = getpass ( " Enter a password: " ) ;
# else
std : : cout < < " Enter a password: " ;
std : : cin > > password ;
# endif
//Generate new hash for bcrypt
char salt [ BCRYPT_HASHSIZE ] ;
char hash [ BCRYPT_HASHSIZE ] ;
int32_t bcryptState = : : bcrypt_gensalt ( 12 , salt ) ;
assert ( bcryptState = = 0 ) ;
bcryptState = : : bcrypt_hashpw ( password . c_str ( ) , salt , hash ) ;
assert ( bcryptState = = 0 ) ;
//Create account
auto * statement = Database : : CreatePreppedStmt ( " INSERT INTO accounts (name, password, " " gm_level) VALUES (?, ?, ?); " ) ;
statement - > setString ( 1 , username ) ;
statement - > setString ( 2 , std : : string ( hash , BCRYPT_HASHSIZE ) . c_str ( ) ) ;
statement - > setInt ( 3 , 9 ) ;
statement - > execute ( ) ;
delete statement ;
std : : cout < < " Account created successfully! \n " ;
2022-01-24 23:35:26 +00:00
2022-04-09 22:35:40 +00:00
Database : : Destroy ( " MasterServer " ) ;
2022-01-24 23:35:26 +00:00
delete Game : : logger ;
2022-01-24 23:41:35 +00:00
return EXIT_SUCCESS ;
2021-12-05 17:54:36 +00:00
}
int maxClients = 999 ;
int ourPort = 1000 ;
if ( config . GetValue ( " max_clients " ) ! = " " ) maxClients = std : : stoi ( config . GetValue ( " max_clients " ) ) ;
if ( config . GetValue ( " port " ) ! = " " ) ourPort = std : : stoi ( config . GetValue ( " port " ) ) ;
Game : : server = new dServer ( config . GetValue ( " external_ip " ) , ourPort , 0 , maxClients , true , false , Game : : logger , " " , 0 , ServerType : : Master ) ;
//Query for the database for a server labeled "master"
auto * masterLookupStatement = Database : : CreatePreppedStmt ( " SELECT id FROM `servers` WHERE `name` = 'master' " ) ;
auto * result = masterLookupStatement - > executeQuery ( ) ;
2021-12-08 13:57:16 +00:00
auto master_server_ip = config . GetValue ( " master_ip " ) ;
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` = ? " ) ;
2021-12-08 13:57:16 +00:00
updateStatement - > setString ( 1 , master_server_ip ) ;
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) " ) ;
2021-12-08 13:57:16 +00:00
insertStatement - > setString ( 1 , master_server_ip ) ;
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:
if ( config . GetValue ( " prestart_servers " ) ! = " " & & config . GetValue ( " prestart_servers " ) = = " 1 " ) {
2021-12-20 09:08:57 +00:00
StartChatServer ( ) ;
2021-12-05 17:54:36 +00:00
Game : : im - > GetInstance ( 0 , false , 0 ) - > SetIsReady ( true ) ;
Game : : im - > GetInstance ( 1000 , false , 0 ) - > SetIsReady ( true ) ;
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 ;
int framesSinceLastFlush = 0 ;
int framesSinceLastSQLPing = 0 ;
int framesSinceKillUniverseCommand = 0 ;
while ( true ) {
//In world we'd update our other systems here.
//Check for packets here:
packet = Game : : server - > Receive ( ) ;
if ( packet ) {
HandlePacket ( packet ) ;
Game : : server - > DeallocatePacket ( packet ) ;
packet = nullptr ;
}
//Push our log every 15s:
if ( framesSinceLastFlush > = 900 ) {
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:
if ( framesSinceLastSQLPing > = 40000 ) {
//Find out the master's IP for absolutely no reason:
std : : string masterIP ;
int masterPort ;
sql : : PreparedStatement * stmt = Database : : CreatePreppedStmt ( " SELECT ip, port FROM servers WHERE name='master'; " ) ;
auto res = stmt - > executeQuery ( ) ;
while ( res - > next ( ) ) {
masterIP = res - > getString ( 1 ) . c_str ( ) ;
masterPort = res - > getInt ( 2 ) ;
}
delete res ;
delete stmt ;
framesSinceLastSQLPing = 0 ;
2022-07-28 13:39:57 +00:00
} else
2021-12-05 17:54:36 +00:00
framesSinceLastSQLPing + + ;
//10m shutdown for universe kill command
if ( shouldShutdown ) {
if ( framesSinceKillUniverseCommand > = 40000 ) {
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 ) ;
if ( affirmTimeout = = 1000 ) {
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 ) ;
}
}
t + = std : : chrono : : milliseconds ( highFrameRate ) ;
std : : this_thread : : sleep_until ( t ) ;
}
2022-04-09 22:35:40 +00:00
FinalizeShutdown ( ) ;
2022-04-09 22:57:18 +00:00
exit ( EXIT_SUCCESS ) ;
return EXIT_SUCCESS ;
2021-12-05 17:54:36 +00:00
}
dLogger * SetupLogger ( ) {
std : : string logPath =
" ./logs/MasterServer_ " + std : : to_string ( time ( nullptr ) ) + " .log " ;
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
if ( packet - > systemAddress = = chatServerMasterPeerSysAddr & & ! shouldShutdown ) {
StartChatServer ( ) ;
}
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
//Game::im->GetInstance(zoneID.GetMapID(), false, 0); //Create the new
}
2021-12-20 09:08:57 +00:00
if ( packet - > systemAddress = = chatServerMasterPeerSysAddr & & ! shouldShutdown ) {
StartChatServer ( ) ;
}
2021-12-05 17:54:36 +00:00
}
if ( packet - > data [ 1 ] = = MASTER ) {
switch ( packet - > data [ 3 ] ) {
case MSG_MASTER_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 ;
}
case MSG_MASTER_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 ) ;
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
}
if ( ! in - > GetIsReady ( ) ) //Instance not ready, make a pending request
{
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 ;
}
case MSG_MASTER_SERVER_INFO : {
//MasterPackets::HandleServerInfo(packet);
//This is here because otherwise we'd have to include IM in
//non-master servers. This packet allows us to add World
//servers back if master crashed
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
uint32_t theirPort = 0 ;
uint32_t theirZoneID = 0 ;
uint32_t theirInstanceID = 0 ;
ServerType theirServerType ;
std : : string theirIP = " " ;
inStream . Read ( theirPort ) ;
inStream . Read ( theirZoneID ) ;
inStream . Read ( theirInstanceID ) ;
inStream . Read ( theirServerType ) ;
theirIP = PacketUtils : : ReadString ( 24 , packet , false ) ; //24 is the current offset
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-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 ;
}
case MSG_MASTER_SET_SESSION_KEY : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
uint32_t sessionKey = 0 ;
std : : string username ;
inStream . Read ( sessionKey ) ;
username = PacketUtils : : ReadString ( 12 , packet , false ) ;
for ( auto it : activeSessions ) {
if ( it . second = = username ) {
activeSessions . erase ( it . first ) ;
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , MASTER , MSG_MASTER_NEW_SESSION_ALERT ) ;
bitStream . Write ( sessionKey ) ;
2022-07-17 03:40:46 +00:00
bitStream . Write < uint32_t > ( username . size ( ) ) ;
for ( auto character : username ) {
bitStream . Write ( character ) ;
}
2021-12-05 17:54:36 +00:00
SEND_PACKET_BROADCAST ;
break ;
}
}
activeSessions . insert ( std : : make_pair ( sessionKey , username ) ) ;
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " Got sessionKey %i for user %s " , sessionKey , username . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
break ;
}
case MSG_MASTER_REQUEST_SESSION_KEY : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
std : : string username = PacketUtils : : ReadString ( 8 , packet , false ) ;
for ( auto key : activeSessions ) {
if ( key . second = = username ) {
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , MASTER , MSG_MASTER_SESSION_KEY_RESPONSE ) ;
bitStream . Write ( key . first ) ;
PacketUtils : : WriteString ( bitStream , key . second , 64 ) ;
Game : : server - > Send ( & bitStream , packet - > systemAddress , false ) ;
break ;
}
}
break ;
}
case MSG_MASTER_PLAYER_ADDED : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
LWOMAPID theirZoneID = 0 ;
LWOINSTANCEID theirInstanceID = 0 ;
inStream . Read ( theirZoneID ) ;
inStream . Read ( theirInstanceID ) ;
auto instance =
Game : : im - > FindInstance ( theirZoneID , theirInstanceID ) ;
if ( instance ) {
instance - > AddPlayer ( Player ( ) ) ;
2022-07-28 13:39:57 +00:00
} else {
2022-07-25 02:26:51 +00:00
printf ( " Instance missing? What? " ) ;
2021-12-05 17:54:36 +00:00
}
break ;
}
case MSG_MASTER_PLAYER_REMOVED : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
LWOMAPID theirZoneID = 0 ;
LWOINSTANCEID theirInstanceID = 0 ;
inStream . Read ( theirZoneID ) ;
inStream . Read ( theirInstanceID ) ;
auto instance =
Game : : im - > FindInstance ( theirZoneID , theirInstanceID ) ;
if ( instance ) {
instance - > RemovePlayer ( Player ( ) ) ;
}
break ;
}
case MSG_MASTER_CREATE_PRIVATE_ZONE : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
uint32_t mapId ;
LWOCLONEID cloneId ;
2022-07-17 03:40:46 +00:00
std : : string password ;
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 ) ;
for ( int i = 0 ; len > i ; i + + ) {
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 ;
}
case MSG_MASTER_REQUEST_PRIVATE_ZONE : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
uint64_t requestID = 0 ;
uint8_t mythranShift = false ;
2022-07-17 03:40:46 +00:00
std : : string password ;
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 ) ;
for ( int i = 0 ; i < len ; i + + ) {
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 ;
}
case MSG_MASTER_WORLD_READY : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
LWOMAPID zoneID ;
LWOINSTANCEID instanceID ;
inStream . Read ( zoneID ) ;
inStream . Read ( instanceID ) ;
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " Got world ready %i %i " , zoneID , instanceID ) ;
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 ;
}
case MSG_MASTER_PREP_ZONE : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
int zoneID ;
inStream . Read ( zoneID ) ;
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " MasterServer " , " Prepping zone %i " , zoneID ) ;
2021-12-05 17:54:36 +00:00
Game : : im - > GetInstance ( zoneID , false , 0 ) ;
break ;
}
case MSG_MASTER_AFFIRM_TRANSFER_RESPONSE : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
uint64_t requestID ;
inStream . Read ( requestID ) ;
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " Got affirmation of transfer %llu " , requestID ) ;
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 ;
}
case MSG_MASTER_SHUTDOWN_RESPONSE : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
2022-04-10 01:33:38 +00:00
auto * instance = Game : : im - > GetInstanceBySysAddr ( packet - > systemAddress ) ;
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 ;
}
case MSG_MASTER_SHUTDOWN_UNIVERSE : {
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " Received shutdown universe command, shutting down in 10 minutes. " ) ;
2021-12-05 17:54:36 +00:00
shouldShutdown = true ;
break ;
}
default :
2022-07-28 13:39:57 +00:00
Game : : logger - > Log ( " MasterServer " , " Unknown master packet ID from server: %i " , packet - > data [ 3 ] ) ;
2021-12-05 17:54:36 +00:00
}
}
}
2021-12-20 09:08:57 +00:00
void StartChatServer ( ) {
# ifdef __APPLE__
2022-07-28 13:39:57 +00:00
//macOS doesn't need sudo to run on ports < 1024
system ( " ./ChatServer& " ) ;
2021-12-20 09:08:57 +00:00
# elif _WIN32
2022-07-28 13:39:57 +00:00
system ( " start ./ChatServer.exe " ) ;
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 ( ) ) ) {
system ( " sudo ./ChatServer& " ) ;
} else {
system ( " ./ChatServer& " ) ;
}
2021-12-20 09:08:57 +00:00
# endif
}
void StartAuthServer ( ) {
# ifdef __APPLE__
2022-07-28 13:39:57 +00:00
system ( " ./AuthServer& " ) ;
2021-12-20 09:08:57 +00:00
# elif _WIN32
2022-07-28 13:39:57 +00:00
system ( " start ./AuthServer.exe " ) ;
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 ( ) ) ) {
system ( " sudo ./AuthServer& " ) ;
} else {
system ( " ./AuthServer& " ) ;
}
2021-12-20 09:08:57 +00:00
# endif
}
2021-12-05 17:54:36 +00:00
void ShutdownSequence ( ) {
if ( shutdownSequenceStarted ) {
return ;
}
shutdownSequenceStarted = true ;
if ( Game : : im ) {
for ( auto * instance : Game : : im - > GetInstances ( ) ) {
if ( instance = = nullptr ) {
continue ;
}
instance - > Shutdown ( ) ;
}
}
auto * objIdManager = ObjectIDManager : : TryInstance ( ) ;
if ( objIdManager ! = nullptr ) {
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
}
auto t = std : : chrono : : high_resolution_clock : : now ( ) ;
auto ticks = 0 ;
if ( ! Game : : im ) {
2022-04-09 22:57:18 +00:00
exit ( EXIT_SUCCESS ) ;
2021-12-05 17:54:36 +00:00
}
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
2021-12-05 17:54:36 +00:00
while ( true ) {
2022-04-09 21:17:31 +00:00
auto packet = Game : : server - > Receive ( ) ;
if ( packet ) {
HandlePacket ( packet ) ;
Game : : server - > DeallocatePacket ( packet ) ;
packet = nullptr ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
auto done = true ;
for ( auto * instance : Game : : im - > GetInstances ( ) ) {
if ( instance = = nullptr ) {
continue ;
}
if ( ! instance - > GetShutdownComplete ( ) ) {
done = false ;
}
}
if ( done ) {
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 ;
}
t + = std : : chrono : : milliseconds ( highFrameRate ) ;
std : : this_thread : : sleep_until ( t ) ;
ticks + + ;
if ( ticks = = 600 * 6 ) {
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-04-09 22:35:40 +00:00
FinalizeShutdown ( ) ;
2021-12-05 17:54:36 +00:00
}
2022-04-09 22:35:40 +00:00
int FinalizeShutdown ( ) {
//Delete our objects here:
Database : : Destroy ( " MasterServer " ) ;
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-04-09 22:57:18 +00:00
exit ( EXIT_SUCCESS ) ;
return EXIT_SUCCESS ;
2022-07-18 23:44:21 +00:00
}