bool isPlayerHasGuildhouse(Player *player, Creature *_creature, bool whisper = false) { QueryResult *result; result = SD2Database.PQuery("SELECT `comment` FROM `guildhouses` WHERE `guildId` = %u", player->GetGuildId()); if (result) { if (whisper) { //whisper to player "already have etc..." Field *fields = result->Fetch(); char msg[100]; sprintf(msg, MSG_ALREADYHAVEGH, fields[0].GetString()); _creature->MonsterWhisper(msg, player->GetGUID()); } delete result; return true; } return false; }
bool showBuyList(Player *player, Creature *_creature, uint32 showFromId = 0) { //show not occupied guildhouses QueryResult *result; result = SD2Database.PQuery("SELECT `id`, `comment` FROM `guildhouses` WHERE `guildId` = 0 AND `id` > %u ORDER BY `id` ASC LIMIT %u", showFromId, GOSSIP_COUNT_MAX); if (result) { uint32 guildhouseId = 0; std::string comment = ""; do { Field *fields = result->Fetch(); guildhouseId = fields[0].GetInt32(); comment = fields[1].GetString(); //send comment as a gossip item //transmit guildhouseId in Action variable player->ADD_GOSSIP_ITEM(ICON_GOSSIP_TABARD, comment, GOSSIP_SENDER_MAIN, guildhouseId + OFFSET_GH_ID_TO_ACTION); } while (result->NextRow()); if (result->GetRowCount() == GOSSIP_COUNT_MAX) { //assume that we have additional page //add link to next GOSSIP_COUNT_MAX items player->ADD_GOSSIP_ITEM(ICON_GOSSIP_BALOONDOTS, MSG_GOSSIP_NEXTPAGE, GOSSIP_SENDER_MAIN, guildhouseId + OFFSET_SHOWBUY_FROM); } delete result; player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, _creature->GetGUID()); return true; } else { if (showFromId = 0) { //all guildhouses are occupied _creature->MonsterWhisper(MSG_NOFREEGH, player->GetGUID()); player->CLOSE_GOSSIP_MENU(); } else { //this condition occurs when COUNT(guildhouses) % GOSSIP_COUNT_MAX == 0 //just show GHs from beginning showBuyList(player, _creature, 0); } } return false; }
void buyGuildhouse(Player *player, Creature *_creature, uint32 guildhouseId) { if (player->GetMoney() < COST_GH_BUY) { //show how much money player need to buy GH (in gold) char msg[100]; sprintf(msg, MSG_NOTENOUGHMONEY, COST_GH_BUY / 10000); _creature->MonsterWhisper(msg, player->GetGUID()); return; } if (isPlayerHasGuildhouse(player, _creature, true)) { //player already have GH return; } QueryResult *result; //check if somebody already occupied this GH result = SD2Database.PQuery("SELECT `id` FROM `guildhouses` WHERE `id` = %u AND `guildId` <> 0", guildhouseId); if (result) { delete result; _creature->MonsterWhisper(MSG_GHOCCUPIED, player->GetGUID()); return; } //update DB result = SD2Database.PQuery("UPDATE `guildhouses` SET `guildId` = %u WHERE `id` = %u", player->GetGuildId(), guildhouseId); if (result) delete result; player->ModifyMoney(-COST_GH_BUY); _creature->MonsterSay(MSG_CONGRATULATIONS, LANG_UNIVERSAL, player->GetGUID()); }
/// Initialize connection to the database bool StartDB(std::string &dbstring) { if(!sConfig.GetString("LoginDatabaseInfo", &dbstring)) { sLog.outError("Database not specified"); return false; } sLog.outString("Database: %s", dbstring.c_str() ); if(!dbRealmServer.Initialize(dbstring.c_str())) { sLog.outError("Cannot connect to database"); return false; } return true; }
void sellGuildhouse(Player *player, Creature *_creature) { if (isPlayerHasGuildhouse(player, _creature)) { QueryResult *result; result = SD2Database.PQuery("UPDATE `guildhouses` SET `guildId` = 0 WHERE `guildId` = %u", player->GetGuildId()); if (result) delete result; player->ModifyMoney(COST_GH_SELL); //display message e.g. "here your money etc." char msg[100]; sprintf(msg, MSG_SOLD, COST_GH_SELL / 10000); _creature->MonsterWhisper(msg, player->GetGUID()); } }
bool getGuildHouseCoords(uint32 guildId, float &x, float &y, float &z, uint32 &map) { if (guildId == 0) { //if player has no guild return false; } QueryResult *result; result = SD2Database.PQuery("SELECT `x`, `y`, `z`, `map` FROM `guildhouses` WHERE `guildId` = %u", guildId); if(result) { Field *fields = result->Fetch(); x = fields[0].GetFloat(); y = fields[1].GetFloat(); z = fields[2].GetFloat(); map = fields[3].GetUInt32(); delete result; return true; } return false; }
/// Make the SRP6 calculation from hash in dB void AuthSocket::_SetVSFields(std::string rI) { BigNumber I; I.SetHexStr(rI.c_str()); //In case of leading zeroes in the rI hash, restore them uint8 mDigest[SHA_DIGEST_LENGTH]; memset(mDigest,0,SHA_DIGEST_LENGTH); if (I.GetNumBytes() <= SHA_DIGEST_LENGTH) memcpy(mDigest,I.AsByteArray(),I.GetNumBytes()); std::reverse(mDigest,mDigest+SHA_DIGEST_LENGTH); Sha1Hash sha; sha.UpdateData(s.AsByteArray(), s.GetNumBytes()); sha.UpdateData(mDigest, SHA_DIGEST_LENGTH); sha.Finalize(); BigNumber x; x.SetBinary(sha.GetDigest(), sha.GetLength()); v = g.ModExp(x, N); // No SQL injection (username escaped) dbRealmServer.PExecute("UPDATE `account` SET `v` = '%s', `s` = '%s' WHERE UPPER(`username`)= UPPER('%s')",v.AsHexStr(),s.AsHexStr(), _safelogin.c_str() ); }
void SQLStorage::Load () { uint32 maxi; Field *fields; QueryResult *result = sDatabase.PQuery("SELECT MAX(`entry`) FROM `%s`",table); if(!result) { sLog.outError("Error loading `%s` table (not exist?)\n",table); exit(1); // Stop server at loading non exited table or not accessable table } maxi= (*result)[0].GetUInt32()+1; delete result; result = sDatabase.PQuery("SELECT COUNT(*) FROM `%s`",table); fields = result->Fetch(); RecordCount=fields[0].GetUInt32(); delete result; result = sDatabase.PQuery("SELECT * FROM `%s`",table); if(!result) { sLog.outError("`%s` table is empty!\n",table); RecordCount = 0; return; } uint32 recordsize=0; uint32 offset=0; if(iNumFields!=result->GetFieldCount()) { RecordCount = 0; sLog.outError("Error in `%s` table, probably sql file format was updated (there should be %d fields in sql).\n",table,iNumFields); delete result; exit(1); // Stop server at loading broken or non-compatiable table. } if(sizeof(char*)==sizeof(uint32)) recordsize=4*iNumFields; else { //get struct size uint32 sc=0; for(uint32 x=0;x<iNumFields;x++) if(format[x]==FT_STRING) sc++; recordsize=(iNumFields-sc)*4+sc*sizeof(char*); } char** newIndex=new char*[maxi]; memset(newIndex,0,maxi*sizeof(char*)); char * _data= new char[RecordCount *recordsize]; uint32 count=0; barGoLink bar( RecordCount ); do { fields = result->Fetch(); bar.step(); char *p=(char*)&_data[recordsize*count]; newIndex[fields[0].GetUInt32()]=p; offset=0; for(uint32 x=0;x<iNumFields;x++) switch(format[x]) { case FT_INT: *((uint32*)(&p[offset]))=fields[x].GetUInt32(); offset+=sizeof(uint32); break; case FT_FLOAT: *((float*)(&p[offset]))=fields[x].GetFloat(); offset+=sizeof(float); break; case FT_STRING: char * tmp=(char*)fields[x].GetString(); char* st; if(!tmp) { st=new char[1]; *st=0; } else { uint32 l=strlen(tmp)+1; st=new char[l]; memcpy(st,tmp,l); } *((char**)(&p[offset]))=st; offset+=sizeof(char*); break; } count++; }while( result->NextRow() ); delete result; if(data) //Reload table AddEvent((EventHandler)(&FreeStorage),this,20000); pOldData=data; pOldIndex=pIndex; iOldNumRecords=MaxEntry; pIndex =newIndex; MaxEntry=maxi; data=_data; }
/// %Realm List command handler bool AuthSocket::_HandleRealmList() { DEBUG_LOG("Entering _HandleRealmList"); if (ibuf.GetLength() < 5) return false; ibuf.Remove(5); ///- Get the user id (else close the connection) // No SQL injection (escaped user name) QueryResult *result = dbRealmServer.PQuery("SELECT `id`,`I` FROM `account` WHERE `username` = '%s'",_safelogin.c_str()); if(!result) { sLog.outError("[ERROR] user %s tried to login and we cannot find him in the database.",_login.c_str()); this->Close(); return false; } uint32 id = (*result)[0].GetUInt32(); std::string rI = (*result)[1].GetCppString(); delete result; ///- Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm) uint8 AmountOfCharacters = 0; ByteBuffer pkt; pkt << (uint32) 0; pkt << (uint16) m_realmList.size(); RealmList::RealmMap::const_iterator i; for( i = m_realmList.begin(); i != m_realmList.end(); i++ ) { pkt << i->second->icon; // realm type pkt << (uint8) 0; // if 1, then realm locked pkt << i->second->color; // if 2, then realm is offline pkt << i->first; pkt << i->second->address; /// \todo Fix realm population pkt << (float) 0.0; //this is population 0.5 = low 1.0 = medium 2.0 high (float)(maxplayers / players)*2 // No SQL injection. id of realm is controlled by the database. result = dbRealmServer.PQuery( "SELECT `numchars` FROM `realmcharacters` WHERE `realmid` = '%d' AND `acctid`='%u'",i->second->m_ID,id); if( result ) { Field *fields = result->Fetch(); AmountOfCharacters = fields[0].GetUInt8(); delete result; } pkt << AmountOfCharacters; pkt << i->second->timezone; pkt << (uint8) 0x2C; // unk, may be realm number/id? } pkt << (uint8) 0x10; pkt << (uint8) 0x00; ByteBuffer hdr; hdr << (uint8) REALM_LIST; hdr << (uint16)pkt.size(); hdr.append(pkt); SendBuf((char *)hdr.contents(), hdr.size()); // Set check field before possable relogin to realm _SetVSFields(rI); return true; }
/// Logon Proof command handler bool AuthSocket::_HandleLogonProof() { DEBUG_LOG("Entering _HandleLogonProof"); ///- Read the packet if (ibuf.GetLength() < sizeof(sAuthLogonProof_C)) return false; sAuthLogonProof_C lp; ibuf.Read((char *)&lp, sizeof(sAuthLogonProof_C)); ///- Continue the SRP6 calculation based on data received from the client BigNumber A; A.SetBinary(lp.A, 32); Sha1Hash sha; sha.UpdateBigNumbers(&A, &B, NULL); sha.Finalize(); BigNumber u; u.SetBinary(sha.GetDigest(), 20); BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N); uint8 t[32]; uint8 t1[16]; uint8 vK[40]; memcpy(t, S.AsByteArray(), 32); for (int i = 0; i < 16; i++) { t1[i] = t[i*2]; } sha.Initialize(); sha.UpdateData(t1, 16); sha.Finalize(); for (int i = 0; i < 20; i++) { vK[i*2] = sha.GetDigest()[i]; } for (int i = 0; i < 16; i++) { t1[i] = t[i*2+1]; } sha.Initialize(); sha.UpdateData(t1, 16); sha.Finalize(); for (int i = 0; i < 20; i++) { vK[i*2+1] = sha.GetDigest()[i]; } K.SetBinary(vK, 40); uint8 hash[20]; sha.Initialize(); sha.UpdateBigNumbers(&N, NULL); sha.Finalize(); memcpy(hash, sha.GetDigest(), 20); sha.Initialize(); sha.UpdateBigNumbers(&g, NULL); sha.Finalize(); for (int i = 0; i < 20; i++) { hash[i] ^= sha.GetDigest()[i]; } BigNumber t3; t3.SetBinary(hash, 20); sha.Initialize(); sha.UpdateData(_login); sha.Finalize(); BigNumber t4; t4.SetBinary(sha.GetDigest(), 20); sha.Initialize(); sha.UpdateBigNumbers(&t3, &t4, &s, &A, &B, &K, NULL); sha.Finalize(); BigNumber M; M.SetBinary(sha.GetDigest(), 20); ///- Check if SRP6 results match (password is correct), else send an error if (!memcmp(M.AsByteArray(), lp.M1, 20)) { sLog.outBasic("User '%s' successfully authenticated", _login.c_str()); ///- Update the sessionkey, last_ip and last login time in the account table for this account // No SQL injection (escaped user name) and IP address as received by socket dbRealmServer.PExecute("UPDATE `account` SET `sessionkey` = '%s', `last_ip` = '%s', `last_login` = NOW() WHERE `username` = '%s'",K.AsHexStr(), GetRemoteAddress().c_str(), _safelogin.c_str() ); ///- Finish SRP6 and send the final result to the client sha.Initialize(); sha.UpdateBigNumbers(&A, &M, &K, NULL); sha.Finalize(); sAuthLogonProof_S proof; memcpy(proof.M2, sha.GetDigest(), 20); proof.cmd = AUTH_LOGON_PROOF; proof.error = 0; proof.unk2 = 0; proof.unk3 = 0; SendBuf((char *)&proof, sizeof(proof)); ///- Set _authed to true! _authed = true; } else { char data[4]={AUTH_LOGON_PROOF,REALM_AUTH_NO_MATCH,3,0}; SendBuf(data,sizeof(data)); } return true; }
/// Logon Challenge command handler bool AuthSocket::_HandleLogonChallenge() { DEBUG_LOG("Entering _HandleLogonChallenge"); if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C)) return false; ///- Read the first 4 bytes (header) to get the length of the remaining of the packet std::vector<uint8> buf; buf.resize(4); ibuf.Read((char *)&buf[0], 4); uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining); if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining)) return false; //No big fear of memory outage (size is int16, i.e. < 65536) buf.resize(remaining + buf.size() + 1); buf[buf.size() - 1] = 0; sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; ///- Read the remaining of the packet ibuf.Read((char *)&buf[4], remaining); DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size); DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I); ByteBuffer pkt; _login = (const char*)ch->I; //Escape the user login to avoid further SQL injection //Memory will be freed on AuthSocket object destruction _safelogin=_login; dbRealmServer.escape_string(_safelogin); ///- Check if the client has one of the expected version numbers bool valid_version=false; int accepted_versions[]=EXPECTED_MANGOS_CLIENT_BUILD; for(int i=0;accepted_versions[i];i++) if(ch->build==accepted_versions[i]) { valid_version=true; break; } /// <ul><li> if this is a valid version if(valid_version) { pkt << (uint8) AUTH_LOGON_CHALLENGE; pkt << (uint8) 0x00; ///- Verify that this IP is not in the ip_banned table // No SQL injection possible (paste the IP address as passed by the socket) dbRealmServer.Execute("DELETE FROM `ip_banned` WHERE `unbandate`<=UNIX_TIMESTAMP() AND `unbandate`<>`bandate`"); QueryResult *result = dbRealmServer.PQuery( "SELECT * FROM `ip_banned` WHERE `ip` = '%s'",GetRemoteAddress().c_str()); if(result) { pkt << (uint8)REALM_AUTH_ACCOUNT_BANNED; sLog.outBasic("[AuthChallenge] Banned ip %s tries to login!",GetRemoteAddress().c_str ()); delete result; } else { ///- Get the account details from the account table // No SQL injection (escaped user name) QueryResult *result = dbRealmServer.PQuery("SELECT `I`,`id`,`locked`,`last_ip`,`online` FROM `account` WHERE `username` = '%s'",_safelogin.c_str ()); if( result ) { ///- If the IP is 'locked', check that the player comes indeed from the correct IP address bool locked = false; if((*result)[2].GetUInt8() == 1) // if ip is locked { DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), (*result)[3].GetString()); DEBUG_LOG("[AuthChallenge] Player address is '%s'", GetRemoteAddress().c_str()); if ( strcmp((*result)[3].GetString(),GetRemoteAddress().c_str()) ) { DEBUG_LOG("[AuthChallenge] Account IP differs"); pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED; locked=true; } else { DEBUG_LOG("[AuthChallenge] Account IP matches"); } } else { DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login.c_str()); } if (!locked) { //set expired bans to inactive dbRealmServer.Execute("UPDATE `account_banned` SET `active` = 0 WHERE `unbandate`<=UNIX_TIMESTAMP() AND `unbandate`<>`bandate`"); ///- If the account is banned, reject the logon attempt QueryResult *banresult = dbRealmServer.PQuery("SELECT `bandate`,`unbandate` FROM `account_banned` WHERE `id` = %u AND `active` = 1", (*result)[1].GetUInt32()); if(banresult) { if((*banresult)[0].GetUInt64() == (*banresult)[1].GetUInt64()) { pkt << (uint8) REALM_AUTH_ACCOUNT_BANNED; sLog.outBasic("[AuthChallenge] Banned account %s tries to login!",_login.c_str ()); } else { pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED; sLog.outBasic("[AuthChallenge] Temporarily banned account %s tries to login!",_login.c_str ()); } delete banresult; } else { ///- Get the password from the account table, upper it, and make the SRP6 calculation std::string rI = (*result)[0].GetCppString(); _SetVSFields(rI); b.SetRand(19 * 8); BigNumber gmod=g.ModExp(b, N); B = ((v * 3) + gmod) % N; ASSERT(gmod.GetNumBytes() <= 32); BigNumber unk3; unk3.SetRand(16*8); ///- Fill the response packet with the result pkt << (uint8)REALM_AUTH_SUCCESS; pkt.append(B.AsByteArray(), 32); pkt << (uint8)1; pkt.append(g.AsByteArray(), 1); pkt << (uint8)32; pkt.append(N.AsByteArray(), 32); pkt.append(s.AsByteArray(), s.GetNumBytes()); pkt.append(unk3.AsByteArray(), 16); pkt << (uint8)0; // Added in 1.12.x client branch } } delete result; } else //no account { pkt<< (uint8) REALM_AUTH_NO_MATCH; } } } //valid version else ///<li> else { ///- Check if we have the apropriate patch on the disk char tmp[64]; // No buffer overflow (fixed length of arguments) sprintf(tmp,"./patches/%d%c%c%c%c.mpq",ch->build,ch->country[3], ch->country[2],ch->country[1],ch->country[0]); // This will be closed at the destruction of the AuthSocket (client deconnection) FILE *pFile=fopen(tmp,"rb"); if(!pFile) { pkt << (uint8) AUTH_LOGON_CHALLENGE; pkt << (uint8) 0x00; pkt << (uint8) REALM_AUTH_WRONG_BUILD_NUMBER; DEBUG_LOG("[AuthChallenge] %u is not a valid client version!", ch->build); DEBUG_LOG("[AuthChallenge] Patch %s not found",tmp); }else { //have patch pPatch=pFile; XFER_INIT xferh; ///- Get the MD5 hash of the patch file (get it from preloaded Patcher cache or calculate it) if(PatchesCache.GetHash(tmp,(uint8*)&xferh.md5)) { DEBUG_LOG("\n[AuthChallenge] Found precached patch info for patch %s",tmp); } else { //calculate patch md5 printf("\n[AuthChallenge] Patch info for %s was not cached.",tmp); PatchesCache.LoadPatchMD5(tmp); PatchesCache.GetHash(tmp,(uint8*)&xferh.md5); } ///- Send a packet to the client with the file length and MD5 hash uint8 data[2]={AUTH_LOGON_PROOF,REALM_AUTH_UPDATE_CLIENT}; SendBuf((const char*)data,sizeof(data)); memcpy(&xferh,"0\x05Patch",7); xferh.cmd=XFER_INITIATE; fseek(pPatch,0,SEEK_END); xferh.file_size=ftell(pPatch); SendBuf((const char*)&xferh,sizeof(xferh)); return true; } } /// </ul> SendBuf((char *)pkt.contents(), pkt.size()); return true; }
/// Launch the realm server int main(int argc, char **argv) { ///- Command line parsing to get the configuration file name char const* cfg_file = _REALMD_CONFIG; int c=1; while( c < argc ) { if( strcmp(argv[c],"-c") == 0) { if( ++c >= argc ) { sLog.outError("Runtime-Error: -c option requires an input argument"); usage(argv[0]); return 1; } else cfg_file = argv[c]; } else { sLog.outError("Runtime-Error: unsupported option %s",argv[c]); usage(argv[0]); return 1; } ++c; } if (!sConfig.SetSource(cfg_file)) { sLog.outError("Could not find configuration file %s.", cfg_file); return 1; } sLog.outString("Using configuration file %s.", cfg_file); ///- Check the version of the configuration file uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0); if (confVersion < _REALMDCONFVERSION) { sLog.outError("*****************************************************************************"); sLog.outError(" WARNING: Your realmd.conf version indicates your conf file is out of date!"); sLog.outError(" Please check for updates, as your current default values may cause"); sLog.outError(" strange behavior."); sLog.outError("*****************************************************************************"); clock_t pause = 3000 + clock(); while (pause > clock()); } sLog.outString( "MaNGOS realm daemon %s", _FULLVERSION ); sLog.outString( "<Ctrl-C> to stop.\n" ); ///- Initialise the database connection std::string dbstring; if(!StartDB(dbstring)) return 1; ///- Get the list of realms for the server m_realmList.GetAndAddRealms(dbstring); if (m_realmList.size() == 0) { sLog.outError("No valid realms specified."); return 1; } ///- Launch the listening network socket port_t rmport = sConfig.GetIntDefault( "RealmServerPort", DEFAULT_REALMSERVER_PORT ); SocketHandler h; ListenSocket<AuthSocket> authListenSocket(h); if ( authListenSocket.Bind(rmport)) { sLog.outError( "MaNGOS realmd can not bind to port %d", rmport ); return 1; } h.Add(&authListenSocket); ///- Catch termination signals HookSignals(); ///- Handle affinity for multiple processors and process priority on Windows #ifdef WIN32 { HANDLE hProcess = GetCurrentProcess(); uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0); if(Aff > 0) { DWORD appAff; DWORD sysAff; if(GetProcessAffinityMask(hProcess,&appAff,&sysAff)) { DWORD curAff = Aff & appAff; // remove non accessible processors if(!curAff ) { sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for realmd. Accessible processors bitmask (hex): %x",Aff,appAff); } else { if(SetProcessAffinityMask(hProcess,curAff)) sLog.outString("Using processors (bitmask, hex): %x", curAff); else sLog.outError("Can't set used processors (hex): %x", curAff); } } sLog.outString(""); } uint32 Prio = sConfig.GetIntDefault("ProcessPriority", 0); if(Prio) { if(SetPriorityClass(hProcess,HIGH_PRIORITY_CLASS)) sLog.outString("realmd process priority class set to HIGH"); else sLog.outError("ERROR: Can't set realmd process priority class."); sLog.outString(""); } } #endif // maximum counter for next ping uint32 numLoops = (sConfig.GetIntDefault( "MaxPingTime", 30 ) * (MINUTE * 1000000 / 100000)); uint32 loopCounter = 0; ///- Wait for termination signal while (!stopEvent) { h.Select(0, 100000); if( (++loopCounter) == numLoops ) { loopCounter = 0; sLog.outDetail("Ping MySQL to keep connection alive"); delete dbRealmServer.Query("SELECT 1 FROM `realmlist` LIMIT 1"); } } ///- Remove signal handling before leaving UnhookSignals(); sLog.outString( "Halting process..." ); return 0; }