2021-12-05 17:54:36 +00:00
# include <iostream>
# include <string>
# include <ctime>
# include <chrono>
# include <thread>
# include "MD5.h"
//DLU Includes:
# include "dCommonVars.h"
# include "dServer.h"
# include "dLogger.h"
# include "Database.h"
# include "dConfig.h"
# include "dpWorld.h"
# include "dZoneManager.h"
# include "Metrics.hpp"
# include "PerformanceManager.h"
# include "Diagnostics.h"
//RakNet includes:
# include "RakNetDefines.h"
# include "RakNetworkFactory.h"
# include "RakString.h"
//World includes:
# include <csignal>
# include "AuthPackets.h"
# include "PacketUtils.h"
# include "WorldPackets.h"
# include "UserManager.h"
# include "dMessageIdentifiers.h"
# include "CDClientManager.h"
# include "CDClientDatabase.h"
# include "GeneralUtils.h"
# include "ObjectIDManager.h"
# include "ZoneInstanceManager.h"
# include "dChatFilter.h"
# include "ClientPackets.h"
# include "CharacterComponent.h"
# include "EntityManager.h"
# include "Entity.h"
# include "Character.h"
# include "ChatPackets.h"
# include "GameMessageHandler.h"
# include "GameMessages.h"
# include "Mail.h"
# include "dLocale.h"
# include "TeamManager.h"
# include "SkillComponent.h"
# include "DestroyableComponent.h"
# include "Game.h"
# include "MasterPackets.h"
# include "Player.h"
# include "PropertyManagementComponent.h"
# include "ZCompression.h"
namespace Game {
dLogger * logger ;
dServer * server ;
dZoneManager * zoneManager ;
dpWorld * physicsWorld ;
dChatFilter * chatFilter ;
dConfig * config ;
dLocale * locale ;
std : : mt19937 randomEngine ;
RakPeerInterface * chatServer ;
SystemAddress chatSysAddr ;
}
bool chatDisabled = false ;
2021-12-20 12:50:53 +00:00
bool chatConnected = false ;
2021-12-05 17:54:36 +00:00
bool worldShutdownSequenceStarted = false ;
bool worldShutdownSequenceComplete = false ;
void WorldShutdownSequence ( ) ;
2022-04-11 00:00:03 +00:00
void WorldShutdownProcess ( uint32_t zoneId ) ;
2022-04-10 01:35:41 +00:00
void FinalizeShutdown ( ) ;
void SendShutdownMessageToMaster ( ) ;
2021-12-05 17:54:36 +00:00
dLogger * SetupLogger ( int zoneID , int instanceID ) ;
void HandlePacketChat ( Packet * packet ) ;
void HandlePacket ( Packet * packet ) ;
struct tempSessionInfo {
SystemAddress sysAddr ;
std : : string hash ;
} ;
std : : map < std : : string , tempSessionInfo > m_PendingUsers ;
int instanceID = 0 ;
int g_CloneID = 0 ;
2021-12-16 22:11:51 +00:00
std : : string databaseChecksum = " " ;
2021-12-05 17:54:36 +00:00
int main ( int argc , char * * argv ) {
Diagnostics : : SetProcessName ( " World " ) ;
Diagnostics : : SetProcessFileName ( argv [ 0 ] ) ;
Diagnostics : : Initialize ( ) ;
// Triggers the shutdown sequence at application exit
std : : atexit ( WorldShutdownSequence ) ;
2022-05-09 22:33:43 +00:00
2022-01-11 21:47:29 +00:00
signal ( SIGINT , [ ] ( int ) { WorldShutdownSequence ( ) ; } ) ;
signal ( SIGTERM , [ ] ( int ) { WorldShutdownSequence ( ) ; } ) ;
2021-12-05 17:54:36 +00:00
int zoneID = 1000 ;
int cloneID = 0 ;
int maxClients = 8 ;
int ourPort = 2007 ;
//Check our arguments:
for ( int i = 0 ; i < argc ; + + i ) {
std : : string argument ( argv [ i ] ) ;
if ( argument = = " -zone " ) zoneID = atoi ( argv [ i + 1 ] ) ;
if ( argument = = " -instance " ) instanceID = atoi ( argv [ i + 1 ] ) ;
if ( argument = = " -clone " ) cloneID = atoi ( argv [ i + 1 ] ) ;
if ( argument = = " -maxclients " ) maxClients = atoi ( argv [ i + 1 ] ) ;
if ( argument = = " -port " ) ourPort = atoi ( argv [ i + 1 ] ) ;
}
//Create all the objects we need to run our service:
Game : : logger = SetupLogger ( zoneID , instanceID ) ;
if ( ! Game : : logger ) return 0 ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
Game : : logger - > SetLogToConsole ( true ) ; //We want this info to always be logged.
Game : : logger - > Log ( " WorldServer " , " Starting World server... \n " ) ;
Game : : logger - > Log ( " WorldServer " , " Version: %i.%i \n " , PROJECT_VERSION_MAJOR , PROJECT_VERSION_MINOR ) ;
Game : : logger - > Log ( " WorldServer " , " Compiled on: %s \n " , __TIMESTAMP__ ) ;
# ifndef _DEBUG
Game : : logger - > SetLogToConsole ( false ) ; //By default, turn it back off if not in debug.
# endif
//Read our config:
dConfig config ( " worldconfig.ini " ) ;
Game : : config = & config ;
Game : : logger - > SetLogToConsole ( bool ( std : : stoi ( config . GetValue ( " log_to_console " ) ) ) ) ;
2021-12-13 15:57:43 +00:00
Game : : logger - > SetLogDebugStatements ( config . GetValue ( " log_debug_statements " ) = = " 1 " ) ;
2021-12-05 17:54:36 +00:00
if ( config . GetValue ( " disable_chat " ) = = " 1 " ) chatDisabled = true ;
// Connect to CDClient
try {
CDClientDatabase : : Connect ( " ./res/CDServer.sqlite " ) ;
} catch ( CppSQLite3Exception & e ) {
Game : : logger - > Log ( " WorldServer " , " Unable to connect to CDServer SQLite Database \n " ) ;
Game : : logger - > Log ( " WorldServer " , " Error: %s \n " , e . errorMessage ( ) ) ;
Game : : logger - > Log ( " WorldServer " , " Error Code: %i \n " , e . errorCode ( ) ) ;
return - 1 ;
}
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
CDClientManager : : Instance ( ) - > Initialize ( ) ;
//Connect to the MySQL Database
std : : string mysql_host = config . GetValue ( " mysql_host " ) ;
std : : string mysql_database = config . GetValue ( " mysql_database " ) ;
std : : string mysql_username = config . GetValue ( " mysql_username " ) ;
std : : string mysql_password = config . GetValue ( " mysql_password " ) ;
Diagnostics : : SetProduceMemoryDump ( config . GetValue ( " generate_dump " ) = = " 1 " ) ;
if ( ! config . GetValue ( " dump_folder " ) . empty ( ) )
{
Diagnostics : : SetOutDirectory ( config . GetValue ( " dump_folder " ) ) ;
}
try {
Database : : Connect ( mysql_host , mysql_database , mysql_username , mysql_password ) ;
} catch ( sql : : SQLException & ex ) {
Game : : logger - > Log ( " WorldServer " , " Got an error while connecting to the database: %s \n " , ex . what ( ) ) ;
return 0 ;
}
//Find out the master's IP:
std : : string masterIP = " localhost " ;
int masterPort = 1000 ;
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 ;
ObjectIDManager : : Instance ( ) - > Initialize ( ) ;
UserManager : : Instance ( ) - > Initialize ( ) ;
2021-12-20 10:25:45 +00:00
LootGenerator : : Instance ( ) ;
2021-12-05 17:54:36 +00:00
Game : : chatFilter = new dChatFilter ( " ./res/chatplus_en_us " , bool ( std : : stoi ( config . GetValue ( " dont_generate_dcf " ) ) ) ) ;
Game : : server = new dServer ( masterIP , ourPort , instanceID , maxClients , false , true , Game : : logger , masterIP , masterPort , ServerType : : World , zoneID ) ;
//Connect to the chat server:
int chatPort = 1501 ;
if ( config . GetValue ( " chat_server_port " ) ! = " " ) chatPort = std : : atoi ( config . GetValue ( " chat_server_port " ) . c_str ( ) ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
auto chatSock = SocketDescriptor ( uint16_t ( ourPort + 2 ) , 0 ) ;
Game : : chatServer = RakNetworkFactory : : GetRakPeerInterface ( ) ;
Game : : chatServer - > Startup ( 1 , 30 , & chatSock , 1 ) ;
Game : : chatServer - > Connect ( masterIP . c_str ( ) , chatPort , " 3.25 ND1 " , 8 ) ;
//Set up other things:
Game : : randomEngine = std : : mt19937 ( time ( 0 ) ) ;
Game : : locale = new dLocale ( ) ;
//Run it until server gets a kill message from Master:
auto lastTime = std : : chrono : : high_resolution_clock : : now ( ) ;
auto t = std : : chrono : : high_resolution_clock : : now ( ) ;
Packet * packet = nullptr ;
int framesSinceLastFlush = 0 ;
int framesSinceMasterDisconnect = 0 ;
2021-12-20 12:50:53 +00:00
int framesSinceChatDisconnect = 0 ;
2021-12-05 17:54:36 +00:00
int framesSinceLastUsersSave = 0 ;
int framesSinceLastSQLPing = 0 ;
int framesSinceLastUser = 0 ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
const float maxPacketProcessingTime = 1.5f ; //0.015f;
const int maxPacketsToProcess = 1024 ;
bool ready = false ;
int framesSinceMasterStatus = 0 ;
int framesSinceShutdownSequence = 0 ;
int currentFramerate = highFrameRate ;
2022-02-09 09:38:09 +00:00
2021-12-05 17:54:36 +00:00
int ghostingStepCount = 0 ;
auto ghostingLastTime = std : : chrono : : high_resolution_clock : : now ( ) ;
PerformanceManager : : SelectProfile ( zoneID ) ;
//Load our level:
if ( zoneID ! = 0 ) {
dpWorld : : Instance ( ) . Initialize ( zoneID ) ;
Game : : physicsWorld = & dpWorld : : Instance ( ) ; //just in case some old code references it
dZoneManager : : Instance ( ) - > Initialize ( LWOZONEID ( zoneID , instanceID , cloneID ) ) ;
g_CloneID = cloneID ;
2021-12-12 15:14:04 +00:00
// pre calculate the FDB checksum
2021-12-14 18:12:44 +00:00
if ( Game : : config - > GetValue ( " check_fdb " ) = = " 1 " ) {
2021-12-12 15:14:04 +00:00
std : : ifstream fileStream ;
2021-12-16 22:11:51 +00:00
2021-12-16 22:51:37 +00:00
static const std : : vector < std : : string > aliases = {
2021-12-16 22:11:51 +00:00
" res/CDServers.fdb " ,
" res/cdserver.fdb " ,
" res/CDClient.fdb " ,
" res/cdclient.fdb " ,
} ;
2022-05-09 22:33:43 +00:00
2021-12-16 22:51:37 +00:00
for ( const auto & file : aliases ) {
fileStream . open ( file , std : : ios : : binary | std : : ios : : in ) ;
2021-12-16 22:11:51 +00:00
if ( fileStream . is_open ( ) ) {
break ;
}
}
2021-12-12 15:14:04 +00:00
const int bufferSize = 1024 ;
MD5 * md5 = new MD5 ( ) ;
2022-05-09 22:33:43 +00:00
2021-12-16 22:11:51 +00:00
char fileStreamBuffer [ 1024 ] = { } ;
2021-12-12 15:53:19 +00:00
2021-12-12 15:14:04 +00:00
while ( ! fileStream . eof ( ) ) {
2021-12-16 22:11:51 +00:00
memset ( fileStreamBuffer , 0 , bufferSize ) ;
fileStream . read ( fileStreamBuffer , bufferSize ) ;
md5 - > update ( fileStreamBuffer , fileStream . gcount ( ) ) ;
2021-12-12 15:14:04 +00:00
}
2021-12-16 22:11:51 +00:00
fileStream . close ( ) ;
2021-12-12 15:53:19 +00:00
2021-12-12 15:14:04 +00:00
const char * nullTerminateBuffer = " \0 " ;
md5 - > update ( nullTerminateBuffer , 1 ) ; // null terminate the data
md5 - > finalize ( ) ;
2021-12-16 22:11:51 +00:00
databaseChecksum = md5 - > hexdigest ( ) ;
2022-05-09 22:33:43 +00:00
2021-12-12 15:14:04 +00:00
delete md5 ;
2021-12-16 22:11:51 +00:00
Game : : logger - > Log ( " WorldServer " , " FDB Checksum calculated as: %s \n " , databaseChecksum . c_str ( ) ) ;
2021-12-12 15:14:04 +00:00
}
2021-12-05 17:54:36 +00:00
}
while ( true ) {
Metrics : : StartMeasurement ( MetricVariable : : Frame ) ;
Metrics : : StartMeasurement ( MetricVariable : : GameLoop ) ;
std : : clock_t metricCPUTimeStart = std : : clock ( ) ;
const auto currentTime = std : : chrono : : high_resolution_clock : : now ( ) ;
float deltaTime = std : : chrono : : duration < float > ( currentTime - lastTime ) . count ( ) ;
lastTime = currentTime ;
const auto occupied = UserManager : : Instance ( ) - > GetUserCount ( ) ! = 0 ;
if ( ! ready )
{
currentFramerate = highFrameRate ;
}
else
{
currentFramerate = PerformanceManager : : GetServerFramerate ( ) ;
}
//Warning if we ran slow
if ( deltaTime > currentFramerate ) {
Game : : logger - > Log ( " WorldServer " , " We're running behind, dT: %f > %f (framerate) \n " , deltaTime , currentFramerate ) ;
}
//Check if we're still connected to master:
if ( ! Game : : server - > GetIsConnectedToMaster ( ) ) {
framesSinceMasterDisconnect + + ;
2022-02-08 16:25:14 +00:00
int framesToWaitForMaster = ready ? 10 : 200 ;
if ( framesSinceMasterDisconnect > = framesToWaitForMaster & & ! worldShutdownSequenceStarted ) {
Game : : logger - > Log ( " WorldServer " , " Game loop running but no connection to master for %d frames, shutting down \n " , framesToWaitForMaster ) ;
2021-12-05 17:54:36 +00:00
worldShutdownSequenceStarted = true ;
}
}
else framesSinceMasterDisconnect = 0 ;
2021-12-20 12:50:53 +00:00
// Check if we're still connected to chat:
if ( ! chatConnected ) {
framesSinceChatDisconnect + + ;
// Attempt to reconnect every 30 seconds.
if ( framesSinceChatDisconnect > = 2000 ) {
framesSinceChatDisconnect = 0 ;
Game : : chatServer - > Connect ( masterIP . c_str ( ) , chatPort , " 3.25 ND1 " , 8 ) ;
}
}
else framesSinceChatDisconnect = 0 ;
2021-12-05 17:54:36 +00:00
//In world we'd update our other systems here.
if ( zoneID ! = 0 & & deltaTime > 0.0f ) {
Metrics : : StartMeasurement ( MetricVariable : : Physics ) ;
2022-02-09 09:38:09 +00:00
dpWorld : : Instance ( ) . StepWorld ( deltaTime ) ;
2021-12-05 17:54:36 +00:00
Metrics : : EndMeasurement ( MetricVariable : : Physics ) ;
Metrics : : StartMeasurement ( MetricVariable : : UpdateEntities ) ;
EntityManager : : Instance ( ) - > UpdateEntities ( deltaTime ) ;
Metrics : : EndMeasurement ( MetricVariable : : UpdateEntities ) ;
Metrics : : StartMeasurement ( MetricVariable : : Ghosting ) ;
if ( std : : chrono : : duration < float > ( currentTime - ghostingLastTime ) . count ( ) > = 1.0f ) {
EntityManager : : Instance ( ) - > UpdateGhosting ( ) ;
ghostingLastTime = currentTime ;
}
Metrics : : EndMeasurement ( MetricVariable : : Ghosting ) ;
Metrics : : StartMeasurement ( MetricVariable : : UpdateSpawners ) ;
dZoneManager : : Instance ( ) - > Update ( deltaTime ) ;
Metrics : : EndMeasurement ( MetricVariable : : UpdateSpawners ) ;
}
Metrics : : StartMeasurement ( MetricVariable : : PacketHandling ) ;
//Check for packets here:
packet = Game : : server - > ReceiveFromMaster ( ) ;
if ( packet ) { //We can get messages not handle-able by the dServer class, so handle them if we returned anything.
2022-05-09 22:33:43 +00:00
HandlePacket ( packet ) ;
2021-12-05 17:54:36 +00:00
Game : : server - > DeallocateMasterPacket ( packet ) ;
}
//Handle our chat packets:
packet = Game : : chatServer - > Receive ( ) ;
if ( packet ) {
HandlePacketChat ( packet ) ;
Game : : chatServer - > DeallocatePacket ( packet ) ;
}
//Handle world-specific packets:
float timeSpent = 0.0f ;
UserManager : : Instance ( ) - > DeletePendingRemovals ( ) ;
auto t1 = std : : chrono : : high_resolution_clock : : now ( ) ;
for ( int curPacket = 0 ; curPacket < maxPacketsToProcess & & timeSpent < maxPacketProcessingTime ; curPacket + + ) {
packet = Game : : server - > Receive ( ) ;
if ( packet ) {
auto t1 = std : : chrono : : high_resolution_clock : : now ( ) ;
HandlePacket ( packet ) ;
auto t2 = std : : chrono : : high_resolution_clock : : now ( ) ;
timeSpent + = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( t2 - t1 ) . count ( ) ;
Game : : server - > DeallocatePacket ( packet ) ;
packet = nullptr ;
}
else {
break ;
}
}
Metrics : : EndMeasurement ( MetricVariable : : PacketHandling ) ;
Metrics : : StartMeasurement ( MetricVariable : : UpdateReplica ) ;
//Update our replica objects:
Game : : server - > UpdateReplica ( ) ;
Metrics : : EndMeasurement ( MetricVariable : : UpdateReplica ) ;
//Push our log every 15s:
if ( framesSinceLastFlush > = 1000 ) {
Game : : logger - > Flush ( ) ;
framesSinceLastFlush = 0 ;
} else framesSinceLastFlush + + ;
if ( zoneID ! = 0 & & ! occupied )
{
framesSinceLastUser + + ;
//If we haven't had any players for a while, time out and shut down:
if ( framesSinceLastUser = = ( cloneID ! = 0 ? 4000 : 40000 ) )
{
worldShutdownSequenceStarted = true ;
}
}
else
{
framesSinceLastUser = 0 ;
}
//Save all connected users every 10 minutes:
if ( framesSinceLastUsersSave > = 40000 & & zoneID ! = 0 ) {
UserManager : : Instance ( ) - > SaveAllActiveCharacters ( ) ;
framesSinceLastUsersSave = 0 ;
if ( PropertyManagementComponent : : Instance ( ) ! = nullptr ) {
PropertyManagementComponent : : Instance ( ) - > Save ( ) ;
}
}
else framesSinceLastUsersSave + + ;
//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 ;
}
else framesSinceLastSQLPing + + ;
Metrics : : EndMeasurement ( MetricVariable : : GameLoop ) ;
Metrics : : StartMeasurement ( MetricVariable : : Sleep ) ;
t + = std : : chrono : : milliseconds ( currentFramerate ) ;
std : : this_thread : : sleep_until ( t ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
Metrics : : EndMeasurement ( MetricVariable : : Sleep ) ;
if ( ! ready & & Game : : server - > GetIsConnectedToMaster ( ) )
{
// Some delay is required here or else we crash the client?
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
framesSinceMasterStatus + + ;
if ( framesSinceMasterStatus > = 200 )
{
2021-12-13 01:37:09 +00:00
Game : : logger - > Log ( " WorldServer " , " Finished loading world with zone (%i), ready up! \n " , Game : : server - > GetZoneID ( ) ) ;
2021-12-05 17:54:36 +00:00
MasterPackets : : SendWorldReady ( Game : : server , Game : : server - > GetZoneID ( ) , Game : : server - > GetInstanceID ( ) ) ;
ready = true ;
}
}
2022-04-10 01:35:41 +00:00
if ( worldShutdownSequenceStarted & & ! worldShutdownSequenceComplete ) {
WorldShutdownProcess ( zoneID ) ;
2022-04-09 23:02:27 +00:00
break ;
2021-12-05 17:54:36 +00:00
}
Metrics : : AddMeasurement ( MetricVariable : : CPUTime , ( 1e6 * ( 1000.0 * ( std : : clock ( ) - metricCPUTimeStart ) ) ) / CLOCKS_PER_SEC ) ;
Metrics : : EndMeasurement ( MetricVariable : : Frame ) ;
}
2022-04-10 01:35:41 +00:00
FinalizeShutdown ( ) ;
2022-04-09 23:02:27 +00:00
return EXIT_SUCCESS ;
2021-12-05 17:54:36 +00:00
}
dLogger * SetupLogger ( int zoneID , int instanceID ) {
std : : string logPath = " ./logs/WorldServer_ " + std : : to_string ( zoneID ) + " _ " + std : : to_string ( instanceID ) + " _ " + 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 HandlePacketChat ( Packet * packet ) {
if ( packet - > data [ 0 ] = = ID_DISCONNECTION_NOTIFICATION | | packet - > data [ 0 ] = = ID_CONNECTION_LOST ) {
2021-12-13 01:37:09 +00:00
Game : : logger - > Log ( " WorldServer " , " Lost our connection to chat, zone(%i), instance(%i) \n " , Game : : server - > GetZoneID ( ) , Game : : server - > GetInstanceID ( ) ) ;
2022-05-09 22:33:43 +00:00
2021-12-20 12:50:53 +00:00
chatConnected = false ;
2021-12-05 17:54:36 +00:00
}
if ( packet - > data [ 0 ] = = ID_CONNECTION_REQUEST_ACCEPTED ) {
2021-12-13 01:37:09 +00:00
Game : : logger - > Log ( " WorldServer " , " Established connection to chat, zone(%i), instance (%i) \n " , Game : : server - > GetZoneID ( ) , Game : : server - > GetInstanceID ( ) ) ;
2021-12-05 17:54:36 +00:00
Game : : chatSysAddr = packet - > systemAddress ;
2021-12-20 12:50:53 +00:00
chatConnected = true ;
2021-12-05 17:54:36 +00:00
}
if ( packet - > data [ 0 ] = = ID_USER_PACKET_ENUM ) {
if ( packet - > data [ 1 ] = = CHAT_INTERNAL ) {
switch ( packet - > data [ 3 ] ) {
case MSG_CHAT_INTERNAL_ROUTE_TO_PLAYER : {
CINSTREAM ;
LWOOBJID playerID ;
inStream . Read ( playerID ) ;
inStream . Read ( playerID ) ;
auto player = EntityManager : : Instance ( ) - > GetEntity ( playerID ) ;
if ( ! player ) return ;
auto sysAddr = player - > GetSystemAddress ( ) ;
//Write our stream outwards:
CBITSTREAM ;
for ( int i = 0 ; i < inStream . GetNumberOfBytesUsed ( ) ; i + + ) {
bitStream . Write ( packet - > data [ i + 16 ] ) ; //16 bytes == header + playerID to skip
}
SEND_PACKET ; //send routed packet to player
break ;
}
case MSG_CHAT_INTERNAL_ANNOUNCEMENT : {
CINSTREAM ;
LWOOBJID header ;
inStream . Read ( header ) ;
2022-07-17 03:40:46 +00:00
std : : string title ;
std : : string msg ;
uint32_t len ;
inStream . Read < uint32_t > ( len ) ;
for ( int i = 0 ; len > i ; i + + ) {
char character ;
inStream . Read < char > ( character ) ;
title + = character ;
}
len = 0 ;
inStream . Read < uint32_t > ( len ) ;
for ( int i = 0 ; len > i ; i + + ) {
char character ;
inStream . Read < char > ( character ) ;
msg + = character ;
}
2021-12-05 17:54:36 +00:00
//Send to our clients:
AMFArrayValue args ;
auto * titleValue = new AMFStringValue ( ) ;
2022-07-17 03:40:46 +00:00
titleValue - > SetStringValue ( title . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
auto * messageValue = new AMFStringValue ( ) ;
2022-07-17 03:40:46 +00:00
messageValue - > SetStringValue ( msg . c_str ( ) ) ;
2021-12-05 17:54:36 +00:00
args . InsertValue ( " title " , titleValue ) ;
args . InsertValue ( " message " , messageValue ) ;
GameMessages : : SendUIMessageServerToAllClients ( " ToggleAnnounce " , & args ) ;
delete titleValue ;
delete messageValue ;
titleValue = nullptr ;
messageValue = nullptr ;
break ;
}
case MSG_CHAT_INTERNAL_MUTE_UPDATE : {
CINSTREAM ;
LWOOBJID playerId ;
time_t expire = 0 ;
inStream . Read ( playerId ) ;
inStream . Read ( playerId ) ;
inStream . Read ( expire ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
auto * entity = EntityManager : : Instance ( ) - > GetEntity ( playerId ) ;
if ( entity ! = nullptr )
{
entity - > GetParentUser ( ) - > SetMuteExpire ( expire ) ;
entity - > GetCharacter ( ) - > SendMuteNotice ( ) ;
}
break ;
}
case MSG_CHAT_INTERNAL_TEAM_UPDATE : {
CINSTREAM ;
LWOOBJID header ;
inStream . Read ( header ) ;
LWOOBJID teamID = 0 ;
char lootOption = 0 ;
char memberCount = 0 ;
std : : vector < LWOOBJID > members ;
inStream . Read ( teamID ) ;
bool deleteTeam = inStream . ReadBit ( ) ;
if ( deleteTeam )
{
TeamManager : : Instance ( ) - > DeleteTeam ( teamID ) ;
Game : : logger - > Log ( " WorldServer " , " Deleting team (%llu) \n " , teamID ) ;
break ;
}
inStream . Read ( lootOption ) ;
inStream . Read ( memberCount ) ;
Game : : logger - > Log ( " WorldServer " , " Updating team (%llu), (%i), (%i) \n " , teamID , lootOption , memberCount ) ;
for ( char i = 0 ; i < memberCount ; i + + )
{
LWOOBJID member = LWOOBJID_EMPTY ;
inStream . Read ( member ) ;
members . push_back ( member ) ;
Game : : logger - > Log ( " WorldServer " , " Updating team member (%llu) \n " , member ) ;
}
TeamManager : : Instance ( ) - > UpdateTeam ( teamID , lootOption , members ) ;
break ;
}
default :
Game : : logger - > Log ( " WorldServer " , " Received an unknown chat internal: %i \n " , int ( packet - > data [ 3 ] ) ) ;
}
}
}
}
void HandlePacket ( Packet * packet ) {
if ( packet - > data [ 0 ] = = ID_DISCONNECTION_NOTIFICATION | | packet - > data [ 0 ] = = ID_CONNECTION_LOST ) {
auto user = UserManager : : Instance ( ) - > GetUser ( packet - > systemAddress ) ;
if ( ! user ) return ;
auto c = user - > GetLastUsedChar ( ) ;
if ( ! c ) {
UserManager : : Instance ( ) - > DeleteUser ( packet - > systemAddress ) ;
return ;
}
auto * entity = EntityManager : : Instance ( ) - > GetEntity ( c - > GetObjectID ( ) ) ;
if ( ! entity )
{
entity = Player : : GetPlayer ( packet - > systemAddress ) ;
}
if ( entity ) {
auto * skillComponent = entity - > GetComponent < SkillComponent > ( ) ;
if ( skillComponent ! = nullptr )
{
skillComponent - > Reset ( ) ;
}
entity - > GetCharacter ( ) - > SaveXMLToDatabase ( ) ;
Game : : logger - > Log ( " WorldServer " , " Deleting player %llu \n " , entity - > GetObjectID ( ) ) ;
EntityManager : : Instance ( ) - > DestroyEntity ( entity ) ;
2022-07-13 03:36:06 +00:00
}
2021-12-05 17:54:36 +00:00
2022-07-13 03:36:06 +00:00
{
2021-12-05 17:54:36 +00:00
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , CHAT_INTERNAL , MSG_CHAT_INTERNAL_PLAYER_REMOVED_NOTIFICATION ) ;
2022-07-13 03:36:06 +00:00
bitStream . Write ( user - > GetLoggedInChar ( ) ) ;
2021-12-05 17:54:36 +00:00
Game : : chatServer - > Send ( & bitStream , SYSTEM_PRIORITY , RELIABLE , 0 , Game : : chatSysAddr , false ) ;
}
UserManager : : Instance ( ) - > DeleteUser ( packet - > systemAddress ) ;
if ( PropertyManagementComponent : : Instance ( ) ! = nullptr ) {
PropertyManagementComponent : : Instance ( ) - > Save ( ) ;
}
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , MASTER , MSG_MASTER_PLAYER_REMOVED ) ;
bitStream . Write ( ( LWOMAPID ) Game : : server - > GetZoneID ( ) ) ;
bitStream . Write ( ( LWOINSTANCEID ) instanceID ) ;
Game : : server - > SendToMaster ( & bitStream ) ;
}
if ( packet - > data [ 0 ] ! = ID_USER_PACKET_ENUM ) return ;
if ( packet - > data [ 1 ] = = SERVER ) {
if ( packet - > data [ 3 ] = = MSG_SERVER_VERSION_CONFIRM ) {
AuthPackets : : HandleHandshake ( Game : : server , packet ) ;
}
}
if ( packet - > data [ 1 ] = = MASTER ) {
switch ( packet - > data [ 3 ] ) {
case MSG_MASTER_REQUEST_PERSISTENT_ID_RESPONSE : {
uint64_t requestID = PacketUtils : : ReadPacketU64 ( 8 , packet ) ;
uint32_t objectID = PacketUtils : : ReadPacketU32 ( 16 , packet ) ;
ObjectIDManager : : Instance ( ) - > HandleRequestPersistentIDResponse ( requestID , objectID ) ;
break ;
}
case MSG_MASTER_REQUEST_ZONE_TRANSFER_RESPONSE : {
uint64_t requestID = PacketUtils : : ReadPacketU64 ( 8 , packet ) ;
ZoneInstanceManager : : Instance ( ) - > HandleRequestZoneTransferResponse ( requestID , packet ) ;
break ;
}
case MSG_MASTER_SESSION_KEY_RESPONSE : {
//Read our session key and to which user it belongs:
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 ) ;
//Find them:
auto it = m_PendingUsers . find ( username ) ;
if ( it = = m_PendingUsers . end ( ) ) return ;
//Convert our key:
std : : string userHash = std : : to_string ( sessionKey ) ;
userHash = md5 ( userHash ) ;
//Verify it:
if ( userHash ! = it - > second . hash ) {
Game : : logger - > Log ( " WorldServer " , " SOMEONE IS TRYING TO HACK? SESSION KEY MISMATCH: ours: %s != master: %s \n " , userHash . c_str ( ) , it - > second . hash . c_str ( ) ) ;
Game : : server - > Disconnect ( it - > second . sysAddr , SERVER_DISCON_INVALID_SESSION_KEY ) ;
return ;
}
else {
Game : : logger - > Log ( " WorldServer " , " User %s authenticated with correct key. \n " , username . c_str ( ) ) ;
UserManager : : Instance ( ) - > DeleteUser ( packet - > systemAddress ) ;
//Create our user and send them in:
UserManager : : Instance ( ) - > CreateUser ( it - > second . sysAddr , username , userHash ) ;
auto zone = dZoneManager : : Instance ( ) - > GetZone ( ) ;
if ( zone ) {
float x = 0.0f ;
float y = 0.0f ;
float z = 0.0f ;
if ( zone - > GetZoneID ( ) . GetMapID ( ) = = 1100 ) {
auto pos = zone - > GetSpawnPos ( ) ;
x = pos . x ;
y = pos . y ;
z = pos . z ;
}
WorldPackets : : SendLoadStaticZone ( it - > second . sysAddr , x , y , z , zone - > GetChecksum ( ) ) ;
}
if ( Game : : server - > GetZoneID ( ) = = 0 ) {
//Since doing this reroute breaks the client's request, we have to call this manually.
UserManager : : Instance ( ) - > RequestCharacterList ( it - > second . sysAddr ) ;
}
m_PendingUsers . erase ( username ) ;
//Notify master:
{
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , MASTER , MSG_MASTER_PLAYER_ADDED ) ;
bitStream . Write ( ( LWOMAPID ) Game : : server - > GetZoneID ( ) ) ;
bitStream . Write ( ( LWOINSTANCEID ) instanceID ) ;
Game : : server - > SendToMaster ( & bitStream ) ;
}
}
break ;
}
case MSG_MASTER_AFFIRM_TRANSFER_REQUEST : {
const uint64_t requestID = PacketUtils : : ReadPacketU64 ( 8 , packet ) ;
Game : : logger - > Log ( " MasterServer " , " Got affirmation request of transfer %llu \n " , requestID ) ;
CBITSTREAM
PacketUtils : : WriteHeader ( bitStream , MASTER , MSG_MASTER_AFFIRM_TRANSFER_RESPONSE ) ;
bitStream . Write ( requestID ) ;
Game : : server - > SendToMaster ( & bitStream ) ;
break ;
}
case MSG_MASTER_SHUTDOWN : {
worldShutdownSequenceStarted = true ;
2021-12-13 01:37:09 +00:00
Game : : logger - > Log ( " WorldServer " , " Got shutdown request from master, zone (%i), instance (%i) \n " , Game : : server - > GetZoneID ( ) , Game : : server - > GetInstanceID ( ) ) ;
2021-12-05 17:54:36 +00:00
break ;
}
case MSG_MASTER_NEW_SESSION_ALERT : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
uint32_t sessionKey = inStream . Read ( sessionKey ) ;
RakNet : : RakString username ;
inStream . Read ( username ) ;
//Find them:
User * user = UserManager : : Instance ( ) - > GetUser ( username . C_String ( ) ) ;
if ( ! user ) {
Game : : logger - > Log ( " WorldServer " , " Got new session alert for user %s, but they're not logged in. \n " , username . C_String ( ) ) ;
return ;
}
//Check the key:
if ( sessionKey ! = std : : atoi ( user - > GetSessionKey ( ) . c_str ( ) ) ) {
Game : : logger - > Log ( " WorldServer " , " Got new session alert for user %s, but the session key is invalid. \n " , username . C_String ( ) ) ;
Game : : server - > Disconnect ( user - > GetSystemAddress ( ) , SERVER_DISCON_INVALID_SESSION_KEY ) ;
return ;
}
break ;
}
default :
Game : : logger - > Log ( " WorldServer " , " Unknown packet ID from master %i \n " , int ( packet - > data [ 3 ] ) ) ;
}
return ;
}
if ( packet - > data [ 1 ] ! = WORLD ) return ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
switch ( packet - > data [ 3 ] ) {
case MSG_WORLD_CLIENT_VALIDATION : {
std : : string username = PacketUtils : : ReadString ( 0x08 , packet , true ) ;
std : : string sessionKey = PacketUtils : : ReadString ( 74 , packet , true ) ;
2021-12-16 22:11:51 +00:00
std : : string clientDatabaseChecksum = PacketUtils : : ReadString ( packet - > length - 33 , packet , false ) ;
2021-12-05 17:54:36 +00:00
2021-12-16 22:11:51 +00:00
// sometimes client puts a null terminator at the end of the checksum and sometimes doesn't, weird
clientDatabaseChecksum = clientDatabaseChecksum . substr ( 0 , 32 ) ;
2021-12-12 03:41:11 +00:00
2021-12-16 22:11:51 +00:00
// If the check is turned on, validate the client's database checksum.
if ( Game : : config - > GetValue ( " check_fdb " ) = = " 1 " & & ! databaseChecksum . empty ( ) ) {
2021-12-14 19:25:30 +00:00
uint32_t gmLevel = 0 ;
2021-12-16 22:11:51 +00:00
auto * stmt = Database : : CreatePreppedStmt ( " SELECT gm_level FROM accounts WHERE name=? LIMIT 1; " ) ;
2021-12-14 19:25:30 +00:00
stmt - > setString ( 1 , username . c_str ( ) ) ;
2022-05-09 22:33:43 +00:00
2021-12-16 22:11:51 +00:00
auto * res = stmt - > executeQuery ( ) ;
2021-12-14 19:25:30 +00:00
while ( res - > next ( ) ) {
gmLevel = res - > getInt ( 1 ) ;
}
2021-12-05 17:54:36 +00:00
2021-12-14 19:25:30 +00:00
delete stmt ;
delete res ;
2021-12-16 22:11:51 +00:00
// Developers may skip this check
if ( gmLevel < 8 & & clientDatabaseChecksum ! = databaseChecksum ) {
Game : : logger - > Log ( " WorldServer " , " Client's database checksum does not match the server's, aborting connection. \n " ) ;
Game : : server - > Disconnect ( packet - > systemAddress , SERVER_DISCON_KICK ) ;
return ;
2021-12-12 03:41:11 +00:00
}
}
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
//Request the session info from Master:
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , MASTER , MSG_MASTER_REQUEST_SESSION_KEY ) ;
PacketUtils : : WriteString ( bitStream , username , 64 ) ;
Game : : server - > SendToMaster ( & bitStream ) ;
//Insert info into our pending list
tempSessionInfo info ;
info . sysAddr = SystemAddress ( packet - > systemAddress ) ;
info . hash = sessionKey ;
m_PendingUsers . insert ( std : : make_pair ( username , info ) ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
break ;
}
case MSG_WORLD_CLIENT_CHARACTER_LIST_REQUEST : {
//We need to delete the entity first, otherwise the char list could delete it while it exists in the world!
if ( Game : : server - > GetZoneID ( ) ! = 0 ) {
auto user = UserManager : : Instance ( ) - > GetUser ( packet - > systemAddress ) ;
if ( ! user ) return ;
EntityManager : : Instance ( ) - > DestroyEntity ( user - > GetLastUsedChar ( ) - > GetEntity ( ) ) ;
}
//This loops prevents users who aren't authenticated to double-request the char list, which
2022-05-09 22:33:43 +00:00
//would make the login screen freeze sometimes.
2021-12-05 17:54:36 +00:00
if ( m_PendingUsers . size ( ) > 0 ) {
for ( auto it : m_PendingUsers ) {
if ( it . second . sysAddr = = packet - > systemAddress ) {
return ;
}
}
}
UserManager : : Instance ( ) - > RequestCharacterList ( packet - > systemAddress ) ;
break ;
}
case MSG_WORLD_CLIENT_GAME_MSG : {
RakNet : : BitStream bitStream ( packet - > data , packet - > length , false ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
uint64_t header ;
LWOOBJID objectID ;
uint16_t messageID ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
bitStream . Read ( header ) ;
bitStream . Read ( objectID ) ;
bitStream . Read ( messageID ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
RakNet : : BitStream dataStream ;
bitStream . Read ( dataStream , bitStream . GetNumberOfUnreadBits ( ) ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
GameMessageHandler : : HandleMessage ( & dataStream , packet - > systemAddress , objectID , GAME_MSG ( messageID ) ) ;
break ;
}
case MSG_WORLD_CLIENT_CHARACTER_CREATE_REQUEST : {
UserManager : : Instance ( ) - > CreateCharacter ( packet - > systemAddress , packet ) ;
break ;
}
case MSG_WORLD_CLIENT_LOGIN_REQUEST : {
RakNet : : BitStream inStream ( packet - > data , packet - > length , false ) ;
uint64_t header = inStream . Read ( header ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
LWOOBJID playerID = 0 ;
inStream . Read ( playerID ) ;
playerID = GeneralUtils : : ClearBit ( playerID , OBJECT_BIT_CHARACTER ) ;
playerID = GeneralUtils : : ClearBit ( playerID , OBJECT_BIT_PERSISTENT ) ;
2022-07-13 03:36:06 +00:00
auto user = UserManager : : Instance ( ) - > GetUser ( packet - > systemAddress ) ;
if ( user ) {
auto lastCharacter = user - > GetLoggedInChar ( ) ;
// This means we swapped characters and we need to remove the previous player from the container.
if ( static_cast < uint32_t > ( lastCharacter ) ! = playerID ) {
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , CHAT_INTERNAL , MSG_CHAT_INTERNAL_PLAYER_REMOVED_NOTIFICATION ) ;
bitStream . Write ( lastCharacter ) ;
Game : : chatServer - > Send ( & bitStream , SYSTEM_PRIORITY , RELIABLE , 0 , Game : : chatSysAddr , false ) ;
}
}
2021-12-05 17:54:36 +00:00
UserManager : : Instance ( ) - > LoginCharacter ( packet - > systemAddress , static_cast < uint32_t > ( playerID ) ) ;
break ;
}
case MSG_WORLD_CLIENT_CHARACTER_DELETE_REQUEST : {
UserManager : : Instance ( ) - > DeleteCharacter ( packet - > systemAddress , packet ) ;
UserManager : : Instance ( ) - > RequestCharacterList ( packet - > systemAddress ) ;
break ;
}
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
case MSG_WORLD_CLIENT_CHARACTER_RENAME_REQUEST : {
UserManager : : Instance ( ) - > RenameCharacter ( packet - > systemAddress , packet ) ;
break ;
}
case MSG_WORLD_CLIENT_LEVEL_LOAD_COMPLETE : {
Game : : logger - > Log ( " WorldServer " , " Received level load complete from user. \n " ) ;
User * user = UserManager : : Instance ( ) - > GetUser ( packet - > systemAddress ) ;
if ( user ) {
Character * c = user - > GetLastUsedChar ( ) ;
2022-05-09 22:33:43 +00:00
if ( c ! = nullptr ) {
2021-12-05 17:54:36 +00:00
std : : u16string username = GeneralUtils : : ASCIIToUTF16 ( c - > GetName ( ) ) ;
Game : : server - > GetReplicaManager ( ) - > AddParticipant ( packet - > systemAddress ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
EntityInfo info { } ;
info . lot = 1 ;
Entity * player = EntityManager : : Instance ( ) - > CreateEntity ( info , UserManager : : Instance ( ) - > GetUser ( packet - > systemAddress ) ) ;
2022-04-01 01:17:43 +00:00
WorldPackets : : SendCreateCharacter ( packet - > systemAddress , player , c - > GetXMLData ( ) , username , c - > GetGMLevel ( ) ) ;
WorldPackets : : SendServerState ( packet - > systemAddress ) ;
2021-12-05 17:54:36 +00:00
const auto respawnPoint = player - > GetCharacter ( ) - > GetRespawnPoint ( dZoneManager : : Instance ( ) - > GetZone ( ) - > GetWorldID ( ) ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
EntityManager : : Instance ( ) - > ConstructEntity ( player , UNASSIGNED_SYSTEM_ADDRESS , true ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
if ( respawnPoint ! = NiPoint3 : : ZERO )
{
GameMessages : : SendPlayerReachedRespawnCheckpoint ( player , respawnPoint , NiQuaternion : : IDENTITY ) ;
}
2022-05-09 00:57:36 +00:00
EntityManager : : Instance ( ) - > ConstructAllEntities ( packet - > systemAddress ) ;
auto * characterComponent = player - > GetComponent < CharacterComponent > ( ) ;
if ( characterComponent ) {
player - > GetComponent < CharacterComponent > ( ) - > RocketUnEquip ( player ) ;
}
2022-01-20 01:34:53 +00:00
c - > SetRetroactiveFlags ( ) ;
2022-01-19 01:51:12 +00:00
2022-04-13 08:49:55 +00:00
player - > RetroactiveVaultSize ( ) ;
2022-01-19 01:51:12 +00:00
2021-12-05 17:54:36 +00:00
player - > GetCharacter ( ) - > SetTargetScene ( " " ) ;
// Fix the destroyable component
auto * destroyableComponent = player - > GetComponent < DestroyableComponent > ( ) ;
if ( destroyableComponent ! = nullptr )
{
destroyableComponent - > FixStats ( ) ;
}
//Tell the player to generate BBB models, if any:
if ( g_CloneID ! = 0 ) {
const auto & worldId = dZoneManager : : Instance ( ) - > GetZone ( ) - > GetZoneID ( ) ;
const auto zoneId = Game : : server - > GetZoneID ( ) ;
const auto cloneId = g_CloneID ;
2022-01-13 03:48:27 +00:00
auto query = CDClientDatabase : : CreatePreppedStmt (
" SELECT id FROM PropertyTemplate WHERE mapID = ?; " ) ;
query . bind ( 1 , ( int ) zoneId ) ;
auto result = query . execQuery ( ) ;
2021-12-05 17:54:36 +00:00
if ( result . eof ( ) | | result . fieldIsNull ( 0 ) ) {
Game : : logger - > Log ( " WorldServer " , " No property templates found for zone %d, not sending BBB \n " , zoneId ) ;
goto noBBB ;
}
//Check for BBB models:
auto stmt = Database : : CreatePreppedStmt ( " SELECT ugc_id FROM properties_contents WHERE lot=14 AND property_id=? " ) ;
int templateId = result . getIntField ( 0 ) ;
result . finalize ( ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
auto * propertyLookup = Database : : CreatePreppedStmt ( " SELECT * FROM properties WHERE template_id = ? AND clone_id = ?; " ) ;
propertyLookup - > setInt ( 1 , templateId ) ;
propertyLookup - > setInt64 ( 2 , g_CloneID ) ;
auto * propertyEntry = propertyLookup - > executeQuery ( ) ;
uint64_t propertyId = 0 ;
if ( propertyEntry - > next ( ) ) {
propertyId = propertyEntry - > getUInt64 ( 1 ) ;
}
delete propertyLookup ;
stmt - > setUInt64 ( 1 , propertyId ) ;
auto res = stmt - > executeQuery ( ) ;
while ( res - > next ( ) ) {
Game : : logger - > Log ( " UGC " , " Getting lxfml ugcID: " + std : : to_string ( res - > getUInt ( 1 ) ) + " \n " ) ;
//Get lxfml:
auto stmtL = Database : : CreatePreppedStmt ( " SELECT lxfml from ugc where id=? " ) ;
stmtL - > setUInt ( 1 , res - > getUInt ( 1 ) ) ;
auto lxres = stmtL - > executeQuery ( ) ;
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
while ( lxres - > next ( ) ) {
auto lxfml = lxres - > getBlob ( 1 ) ;
lxfml - > seekg ( 0 , std : : ios : : end ) ;
size_t lxfmlSize = lxfml - > tellg ( ) ;
lxfml - > seekg ( 0 ) ;
//Send message:
{
LWOOBJID blueprintID = res - > getUInt ( 1 ) ;
blueprintID = GeneralUtils : : SetBit ( blueprintID , OBJECT_BIT_CHARACTER ) ;
blueprintID = GeneralUtils : : SetBit ( blueprintID , OBJECT_BIT_PERSISTENT ) ;
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , CLIENT , MSG_CLIENT_BLUEPRINT_SAVE_RESPONSE ) ;
bitStream . Write < LWOOBJID > ( 0 ) ; //always zero so that a check on the client passes
bitStream . Write < unsigned int > ( 0 ) ;
bitStream . Write < unsigned int > ( 1 ) ;
bitStream . Write ( blueprintID ) ;
bitStream . Write < uint32_t > ( lxfmlSize + 9 ) ;
//Write a fake sd0 header:
bitStream . Write < unsigned char > ( 0x73 ) ; //s
bitStream . Write < unsigned char > ( 0x64 ) ; //d
bitStream . Write < unsigned char > ( 0x30 ) ; //0
bitStream . Write < unsigned char > ( 0x01 ) ; //1
bitStream . Write < unsigned char > ( 0xFF ) ; //end magic
bitStream . Write < uint32_t > ( lxfmlSize ) ;
for ( size_t i = 0 ; i < lxfmlSize ; + + i )
bitStream . Write < uint8_t > ( lxfml - > get ( ) ) ;
SystemAddress sysAddr = packet - > systemAddress ;
SEND_PACKET ;
PacketUtils : : SavePacket ( " lxfml packet " + std : : to_string ( res - > getUInt ( 1 ) ) + " .bin " , ( char * ) bitStream . GetData ( ) , bitStream . GetNumberOfBytesUsed ( ) ) ;
}
}
delete stmtL ;
delete lxres ;
}
delete stmt ;
delete res ;
}
noBBB :
// Tell the client it's done loading:
2022-05-09 22:33:43 +00:00
GameMessages : : SendInvalidZoneTransferList ( player , packet - > systemAddress , GeneralUtils : : ASCIIToUTF16 ( Game : : config - > GetValue ( " source " ) ) , u " " , false , false ) ;
2021-12-05 17:54:36 +00:00
GameMessages : : SendServerDoneLoadingAllObjects ( player , packet - > systemAddress ) ;
//Send the player it's mail count:
//update: this might not be needed so im going to try disabling this here.
//Mail::HandleNotificationRequest(packet->systemAddress, player->GetObjectID());
//Notify chat that a player has loaded:
{
const auto & playerName = player - > GetCharacter ( ) - > GetName ( ) ;
//RakNet::RakString playerName(player->GetCharacter()->GetName().c_str());
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , CHAT_INTERNAL , MSG_CHAT_INTERNAL_PLAYER_ADDED_NOTIFICATION ) ;
bitStream . Write ( player - > GetObjectID ( ) ) ;
2022-07-17 03:40:46 +00:00
bitStream . Write < uint32_t > ( playerName . size ( ) ) ;
2021-12-05 17:54:36 +00:00
for ( size_t i = 0 ; i < playerName . size ( ) ; i + + )
{
bitStream . Write ( playerName [ i ] ) ;
}
2022-05-09 22:33:43 +00:00
2021-12-05 17:54:36 +00:00
auto zone = dZoneManager : : Instance ( ) - > GetZone ( ) - > GetZoneID ( ) ;
bitStream . Write ( zone . GetMapID ( ) ) ;
bitStream . Write ( zone . GetInstanceID ( ) ) ;
bitStream . Write ( zone . GetCloneID ( ) ) ;
bitStream . Write ( player - > GetParentUser ( ) - > GetMuteExpire ( ) ) ;
Game : : chatServer - > Send ( & bitStream , SYSTEM_PRIORITY , RELIABLE , 0 , Game : : chatSysAddr , false ) ;
}
}
else {
2021-12-13 01:37:09 +00:00
Game : : logger - > Log ( " WorldServer " , " Couldn't find character to log in with for user %s (%i)! \n " , user - > GetUsername ( ) . c_str ( ) , user - > GetAccountID ( ) ) ;
2021-12-05 17:54:36 +00:00
Game : : server - > Disconnect ( packet - > systemAddress , SERVER_DISCON_CHARACTER_NOT_FOUND ) ;
}
} else {
2021-12-13 01:37:09 +00:00
Game : : logger - > Log ( " WorldServer " , " Couldn't get user for level load complete! \n " ) ;
2021-12-05 17:54:36 +00:00
}
break ;
}
case MSG_WORLD_CLIENT_POSITION_UPDATE : {
ClientPackets : : HandleClientPositionUpdate ( packet - > systemAddress , packet ) ;
break ;
}
case MSG_WORLD_CLIENT_MAIL : {
RakNet : : BitStream bitStream ( packet - > data , packet - > length , false ) ;
LWOOBJID space ;
bitStream . Read ( space ) ;
Mail : : HandleMailStuff ( & bitStream , packet - > systemAddress , UserManager : : Instance ( ) - > GetUser ( packet - > systemAddress ) - > GetLastUsedChar ( ) - > GetEntity ( ) ) ;
break ;
}
case MSG_WORLD_CLIENT_ROUTE_PACKET : {
//Yeet to chat
CINSTREAM ;
uint64_t header = 0 ;
uint32_t size = 0 ;
inStream . Read ( header ) ;
inStream . Read ( size ) ;
if ( size > 20000 ) {
Game : : logger - > Log ( " WorldServer " , " Tried to route a packet with a read size > 20000, so likely a false packet. \n " ) ;
return ;
}
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , CHAT , packet - > data [ 14 ] ) ;
//We need to insert the player's objectID so the chat server can find who originated this request:
LWOOBJID objectID = 0 ;
auto user = UserManager : : Instance ( ) - > GetUser ( packet - > systemAddress ) ;
if ( user ) {
objectID = user - > GetLastUsedChar ( ) - > GetObjectID ( ) ;
}
bitStream . Write ( objectID ) ;
//Now write the rest of the data:
auto data = inStream . GetData ( ) ;
for ( uint32_t i = 0 ; i < size ; + + i ) {
bitStream . Write ( data [ i + 23 ] ) ;
}
Game : : chatServer - > Send ( & bitStream , SYSTEM_PRIORITY , RELIABLE_ORDERED , 0 , Game : : chatSysAddr , false ) ;
break ;
}
case MSG_WORLD_CLIENT_STRING_CHECK : {
ClientPackets : : HandleChatModerationRequest ( packet - > systemAddress , packet ) ;
break ;
}
case MSG_WORLD_CLIENT_GENERAL_CHAT_MESSAGE : {
if ( chatDisabled ) {
ChatPackets : : SendMessageFail ( packet - > systemAddress ) ;
}
else {
ClientPackets : : HandleChatMessage ( packet - > systemAddress , packet ) ;
}
break ;
}
case MSG_WORLD_CLIENT_HANDLE_FUNNESS : {
//This means the client is running slower or faster than it should.
//Could be insane lag, but I'mma just YEET them as it's usually speedhacking.
//This is updated to now count the amount of times we've been caught "speedhacking" to kick with a delay
//This is hopefully going to fix the random disconnects people face sometimes.
if ( Game : : config - > GetValue ( " disable_anti_speedhack " ) = = " 1 " ) {
return ;
}
User * user = UserManager : : Instance ( ) - > GetUser ( packet - > systemAddress ) ;
if ( user ) {
user - > UserOutOfSync ( ) ;
}
else {
Game : : server - > Disconnect ( packet - > systemAddress , SERVER_DISCON_KICK ) ;
}
break ;
}
default :
Game : : server - > GetLogger ( ) - > Log ( " HandlePacket " , " Unknown world packet received: %i \n " , int ( packet - > data [ 3 ] ) ) ;
}
}
2022-04-10 01:35:41 +00:00
void WorldShutdownProcess ( uint32_t zoneId ) {
Game : : logger - > Log ( " WorldServer " , " Saving map %i instance %i \n " , zoneId , instanceID ) ;
for ( auto i = 0 ; i < Game : : server - > GetReplicaManager ( ) - > GetParticipantCount ( ) ; + + i ) {
const auto & player = Game : : server - > GetReplicaManager ( ) - > GetParticipantAtIndex ( i ) ;
2021-12-05 17:54:36 +00:00
2022-04-10 01:35:41 +00:00
auto * entity = Player : : GetPlayer ( player ) ;
Game : : logger - > Log ( " WorldServer " , " Saving data! \n " ) ;
if ( entity ! = nullptr & & entity - > GetCharacter ( ) ! = nullptr ) {
auto * skillComponent = entity - > GetComponent < SkillComponent > ( ) ;
2021-12-05 17:54:36 +00:00
2022-04-10 01:35:41 +00:00
if ( skillComponent ! = nullptr ) {
skillComponent - > Reset ( ) ;
}
std : : string message = " Saving character " + entity - > GetCharacter ( ) - > GetName ( ) + " ... \n " ;
Game : : logger - > Log ( " WorldServer " , message ) ;
entity - > GetCharacter ( ) - > SaveXMLToDatabase ( ) ;
message = " Character data for " + entity - > GetCharacter ( ) - > GetName ( ) + " was saved! \n " ;
Game : : logger - > Log ( " WorldServer " , message ) ;
}
}
2021-12-05 17:54:36 +00:00
2022-04-10 01:35:41 +00:00
if ( PropertyManagementComponent : : Instance ( ) ! = nullptr ) {
2022-04-10 01:48:13 +00:00
Game : : logger - > Log ( " WorldServer " , " Saving ALL property data for zone %i clone %i! \n " , zoneId , PropertyManagementComponent : : Instance ( ) - > GetCloneId ( ) ) ;
2022-04-10 01:35:41 +00:00
PropertyManagementComponent : : Instance ( ) - > Save ( ) ;
2022-04-10 01:48:13 +00:00
Game : : logger - > Log ( " WorldServer " , " ALL property data saved for zone %i clone %i! \n " , zoneId , PropertyManagementComponent : : Instance ( ) - > GetCloneId ( ) ) ;
2022-04-10 01:35:41 +00:00
}
2021-12-05 17:54:36 +00:00
2022-04-10 01:48:13 +00:00
Game : : logger - > Log ( " WorldServer " , " ALL DATA HAS BEEN SAVED FOR ZONE %i INSTANCE %i! \n " , zoneId , instanceID ) ;
2021-12-05 17:54:36 +00:00
2022-04-10 01:35:41 +00:00
while ( Game : : server - > GetReplicaManager ( ) - > GetParticipantCount ( ) > 0 ) {
const auto & player = Game : : server - > GetReplicaManager ( ) - > GetParticipantAtIndex ( 0 ) ;
2021-12-05 17:54:36 +00:00
2022-04-10 01:35:41 +00:00
Game : : server - > Disconnect ( player , SERVER_DISCON_KICK ) ;
}
SendShutdownMessageToMaster ( ) ;
}
2021-12-05 17:54:36 +00:00
2022-04-10 01:35:41 +00:00
void WorldShutdownSequence ( ) {
if ( worldShutdownSequenceStarted | | worldShutdownSequenceComplete ) {
return ;
}
worldShutdownSequenceStarted = true ;
2022-04-11 00:08:29 +00:00
Game : : logger - > Log ( " WorldServer " , " Zone (%i) instance (%i) shutting down outside of main loop! \n " , Game : : server - > GetZoneID ( ) , instanceID ) ;
2022-04-10 01:35:41 +00:00
WorldShutdownProcess ( Game : : server - > GetZoneID ( ) ) ;
FinalizeShutdown ( ) ;
2021-12-05 17:54:36 +00:00
}
2022-04-10 01:35:41 +00:00
void FinalizeShutdown ( ) {
//Delete our objects here:
if ( Game : : physicsWorld ) Game : : physicsWorld = nullptr ;
if ( Game : : zoneManager ) delete Game : : zoneManager ;
Game : : logger - > Log ( " WorldServer " , " Shutdown complete, zone (%i), instance (%i) \n " , Game : : server - > GetZoneID ( ) , instanceID ) ;
Metrics : : Clear ( ) ;
Database : : Destroy ( " WorldServer " ) ;
delete Game : : chatFilter ;
delete Game : : server ;
delete Game : : logger ;
worldShutdownSequenceComplete = true ;
exit ( EXIT_SUCCESS ) ;
}
void SendShutdownMessageToMaster ( ) {
CBITSTREAM ;
PacketUtils : : WriteHeader ( bitStream , MASTER , MSG_MASTER_SHUTDOWN_RESPONSE ) ;
Game : : server - > SendToMaster ( & bitStream ) ;
}