diff options
author | Kargatum <dowlandtop@yandex.com> | 2021-05-27 05:12:46 +0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-27 00:12:46 +0200 |
commit | ea5f5f20727bb80f92c7dd085c19b805af5e4c7c (patch) | |
tree | e098abf514dd4e901ce47bc98646c57f5173fad0 | |
parent | 78e1719c80e8c3174461dcee3a25bcd77438b450 (diff) |
feat(Core/RealmList): port TrinityCore realm api (#5626)
* feat(Core/RealmList): port TrinityCore realm api
* 1
* whitespace cleanup
* Update data/sql/updates/pending_db_auth/rev_1620114805872279900.sql
Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com>
* 1
* 2
* Update data/sql/updates/pending_db_auth/rev_1620114805872279900.sql
Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com>
* `
* 1
* small corrects
* finish maybe
* realm.Id.Realm
* ws
* 1
Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com>
23 files changed, 526 insertions, 295 deletions
diff --git a/data/sql/updates/pending_db_auth/rev_1620114805872279900.sql b/data/sql/updates/pending_db_auth/rev_1620114805872279900.sql new file mode 100644 index 0000000000..56ef2198e1 --- /dev/null +++ b/data/sql/updates/pending_db_auth/rev_1620114805872279900.sql @@ -0,0 +1,35 @@ +INSERT INTO `version_db_auth` (`sql_rev`) VALUES ('1620114805872279900'); + +-- +-- Table structure for table `build_info` +-- +DROP TABLE IF EXISTS `build_info`; +CREATE TABLE `build_info` ( + `build` INT NOT NULL, + `majorVersion` INT DEFAULT NULL, + `minorVersion` INT DEFAULT NULL, + `bugfixVersion` INT DEFAULT NULL, + `hotfixVersion` CHAR(3) DEFAULT NULL, + `winAuthSeed` VARCHAR(32) DEFAULT NULL, + `win64AuthSeed` VARCHAR(32) DEFAULT NULL, + `mac64AuthSeed` VARCHAR(32) DEFAULT NULL, + `winChecksumSeed` VARCHAR(40) DEFAULT NULL, + `macChecksumSeed` VARCHAR(40) DEFAULT NULL, + PRIMARY KEY (`build`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- +-- Dumping data for table `build_info` +-- +INSERT INTO `build_info` VALUES +(5875,1,12,1,NULL,NULL,NULL,NULL,'95EDB27C7823B363CBDDAB56A392E7CB73FCCA20','8D173CC381961EEBABF336F5E6675B101BB513E5'), +(6005,1,12,2,NULL,NULL,NULL,NULL,NULL,NULL), +(6141,1,12,3,NULL,NULL,NULL,NULL,NULL,NULL), +(8606,2,4,3,NULL,NULL,NULL,NULL,'319AFAA3F2559682F9FF658BE01456255F456FB1','D8B0ECFE534BC1131E19BAD1D4C0E813EEE4994F'), +(9947,3,1,3,NULL,NULL,NULL,NULL,NULL,NULL), +(10505,3,2,2,'a',NULL,NULL,NULL,NULL,NULL), +(11159,3,3,0,'a',NULL,NULL,NULL,NULL,NULL), +(11403,3,3,2,NULL,NULL,NULL,NULL,NULL,NULL), +(11723,3,3,3,'a',NULL,NULL,NULL,NULL,NULL), +(12340,3,3,5,'a',NULL,NULL,NULL,'CDCBBD5188315E6B4D19449D492DBCFAF156A347','B706D13FF2F4018839729461E3F8A0E2B5FDC034'), +(13930,3,3,5,'a',NULL,NULL,NULL,NULL,NULL); diff --git a/src/common/Common.h b/src/common/Common.h index bc25cc24c5..6acc2c7295 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -14,6 +14,7 @@ #include <utility> #if AC_PLATFORM == AC_PLATFORM_WINDOWS +#include <ace/config-all.h> #include <ws2tcpip.h> #if AC_COMPILER == AC_COMPILER_INTEL # if !defined(BOOST_ASIO_HAS_MOVE) diff --git a/src/server/authserver/Authentication/AuthCodes.cpp b/src/server/authserver/Authentication/AuthCodes.cpp index f5002b64b1..9777de4836 100644 --- a/src/server/authserver/Authentication/AuthCodes.cpp +++ b/src/server/authserver/Authentication/AuthCodes.cpp @@ -5,66 +5,24 @@ */ #include "AuthCodes.h" -#include <cstddef> +#include "RealmList.h" namespace AuthHelper { - static RealmBuildInfo const PostBcAcceptedClientBuilds[] = - { - {15595, 4, 3, 4, ' '}, - {14545, 4, 2, 2, ' '}, - {13623, 4, 0, 6, 'a'}, - {12340, 3, 3, 5, 'a'}, - {11723, 3, 3, 3, 'a'}, - {11403, 3, 3, 2, ' '}, - {11159, 3, 3, 0, 'a'}, - {10505, 3, 2, 2, 'a'}, - {9947, 3, 1, 3, ' '}, - {8606, 2, 4, 3, ' '}, - {0, 0, 0, 0, ' '} // terminator - }; - - static RealmBuildInfo const PreBcAcceptedClientBuilds[] = - { - {6141, 1, 12, 3, ' '}, - {6005, 1, 12, 2, ' '}, - {5875, 1, 12, 1, ' '}, - {0, 0, 0, 0, ' '} // terminator - }; - - bool IsPreBCAcceptedClientBuild(int build) - { - for (int i = 0; PreBcAcceptedClientBuilds[i].Build; ++i) - if (PreBcAcceptedClientBuilds[i].Build == build) - return true; + constexpr static uint32 MAX_PRE_BC_CLIENT_BUILD = 6141; - return false; - } - - bool IsPostBCAcceptedClientBuild(int build) + bool IsPreBCAcceptedClientBuild(uint32 build) { - for (int i = 0; PostBcAcceptedClientBuilds[i].Build; ++i) - if (PostBcAcceptedClientBuilds[i].Build == build) - return true; - - return false; + return build <= MAX_PRE_BC_CLIENT_BUILD && sRealmList->GetBuildInfo(build); } - bool IsAcceptedClientBuild(int build) + bool IsPostBCAcceptedClientBuild(uint32 build) { - return (IsPostBCAcceptedClientBuild(build) || IsPreBCAcceptedClientBuild(build)); + return build > MAX_PRE_BC_CLIENT_BUILD && sRealmList->GetBuildInfo(build); } - RealmBuildInfo const* GetBuildInfo(int build) + bool IsAcceptedClientBuild(uint32 build) { - for (int i = 0; PostBcAcceptedClientBuilds[i].Build; ++i) - if (PostBcAcceptedClientBuilds[i].Build == build) - return &PostBcAcceptedClientBuilds[i]; - - for (int i = 0; PreBcAcceptedClientBuilds[i].Build; ++i) - if (PreBcAcceptedClientBuilds[i].Build == build) - return &PreBcAcceptedClientBuilds[i]; - - return nullptr; + return sRealmList->GetBuildInfo(build) != nullptr; } }; diff --git a/src/server/authserver/Authentication/AuthCodes.h b/src/server/authserver/Authentication/AuthCodes.h index 6bb1a7f742..be079b7367 100644 --- a/src/server/authserver/Authentication/AuthCodes.h +++ b/src/server/authserver/Authentication/AuthCodes.h @@ -7,6 +7,9 @@ #ifndef _AUTHCODES_H #define _AUTHCODES_H +#include "Define.h" +#include <array> + enum AuthResult { WOW_SUCCESS = 0x00, @@ -65,21 +68,13 @@ enum ExpansionFlags NO_VALID_EXP_FLAG = 0x0 }; -struct RealmBuildInfo -{ - int Build; - int MajorVersion; - int MinorVersion; - int BugfixVersion; - int HotfixVersion; -}; +struct RealmBuildInfo; namespace AuthHelper { - RealmBuildInfo const* GetBuildInfo(int build); - bool IsAcceptedClientBuild(int build); - bool IsPostBCAcceptedClientBuild(int build); - bool IsPreBCAcceptedClientBuild(int build); + bool IsAcceptedClientBuild(uint32 build); + bool IsPostBCAcceptedClientBuild(uint32 build); + bool IsPreBCAcceptedClientBuild(uint32 build); }; #endif diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 772832937b..1552362888 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -135,7 +135,7 @@ extern int main(int argc, char** argv) // Get the list of realms for the server sRealmList->Initialize(sConfigMgr->GetOption<int32>("RealmsStateUpdateDelay", 20)); - if (sRealmList->size() == 0) + if (sRealmList->GetRealms().empty()) { LOG_ERROR("server.authserver", "No valid realms specified."); return 1; diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp index 3a17dd7c21..c8c6eb0d7c 100644 --- a/src/server/authserver/Server/AuthSocket.cpp +++ b/src/server/authserver/Server/AuthSocket.cpp @@ -165,6 +165,8 @@ private: Patches _patches; }; +std::array<uint8, 16> VersionChallenge = { { 0xBA, 0xA3, 0x1E, 0x99, 0xA0, 0x0B, 0x21, 0x57, 0xFC, 0x37, 0x3F, 0xB3, 0x69, 0xCD, 0xD2, 0xF1 } }; + const AuthHandler table[] = { { AUTH_LOGON_CHALLENGE, STATUS_CHALLENGE, &AuthSocket::_HandleLogonChallenge }, @@ -366,6 +368,10 @@ bool AuthSocket::_HandleLogonChallenge() // Restore string order as its byte order is reversed std::reverse(_os.begin(), _os.end()); + _localizationName.resize(4); + for (int i = 0; i < 4; ++i) + _localizationName[i] = ch->country[4 - i - 1]; + ByteBuffer pkt; pkt << uint8(AUTH_LOGON_CHALLENGE); pkt << uint8(0x00); @@ -487,9 +493,6 @@ bool AuthSocket::_HandleLogonChallenge() fields[10].GetBinary<acore::Crypto::SRP6::SALT_LENGTH>(), fields[11].GetBinary<acore::Crypto::SRP6::VERIFIER_LENGTH>()); - BigNumber unk3; - unk3.SetRand(16 * 8); - // Fill the response packet with the result if (!AuthHelper::IsAcceptedClientBuild(_build)) { @@ -507,8 +510,7 @@ bool AuthSocket::_HandleLogonChallenge() pkt << uint8(32); pkt.append(_srp6->N); pkt.append(_srp6->s); - pkt.append(unk3.ToByteArray<16>()); - + pkt.append(VersionChallenge.data(), VersionChallenge.size()); pkt << uint8(securityFlags); // security flags (0x0...0x04) if (securityFlags & 0x01) // PIN input @@ -529,11 +531,7 @@ bool AuthSocket::_HandleLogonChallenge() if (securityFlags & 0x04) // Security token input pkt << uint8(1); - _localizationName.resize(4); - for (int i = 0; i < 4; ++i) - _localizationName[i] = ch->country[4 - i - 1]; - - LOG_DEBUG("network", "'%s:%d' [AuthChallenge] account %s is using locale (%u)", + LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using locale (%u)", ipAddress.c_str(), port, _accountInfo.Login.c_str(), GetLocaleByName(_localizationName)); ///- All good, await client's proof @@ -546,9 +544,8 @@ bool AuthSocket::_HandleLogonChallenge() // Logon Proof command handler bool AuthSocket::_HandleLogonProof() { -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - LOG_DEBUG("network", "Entering _HandleLogonProof"); -#endif + LOG_TRACE("server.authserver", "Entering _HandleLogonProof"); + // Read the packet sAuthLogonProof_C lp; @@ -571,20 +568,6 @@ bool AuthSocket::_HandleLogonProof() if (std::optional<SessionKey> K = _srp6->VerifyChallengeResponse(lp.A, lp.clientM)) { _sessionKey = *K; - LOG_DEBUG("network", "'%s:%d' User '%s' successfully authenticated", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _accountInfo.Login.c_str()); - - // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account - // No SQL injection (escaped user name) and IP address as received by socket - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); - stmt->setBinary(0, _sessionKey); - stmt->setString(1, socket().getRemoteAddress().c_str()); - stmt->setUInt32(2, GetLocaleByName(_localizationName)); - stmt->setString(3, _os); - stmt->setString(4, _accountInfo.Login); - LoginDatabase.DirectExecute(stmt); - - // Finish SRP6 and send the final result to the client - acore::Crypto::SHA1::Digest M2 = acore::Crypto::SRP6::GetSessionVerifier(lp.A, lp.clientM, _sessionKey); // Check auth token bool tokenSuccess = false; @@ -613,6 +596,21 @@ bool AuthSocket::_HandleLogonProof() socket().send(data, sizeof(data)); } + LOG_DEBUG("network", "'%s:%d' User '%s' successfully authenticated", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _accountInfo.Login.c_str()); + + // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account + // No SQL injection (escaped user name) and IP address as received by socket + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); + stmt->setBinary(0, _sessionKey); + stmt->setString(1, socket().getRemoteAddress().c_str()); + stmt->setUInt32(2, GetLocaleByName(_localizationName)); + stmt->setString(3, _os); + stmt->setString(4, _accountInfo.Login); + LoginDatabase.DirectExecute(stmt); + + // Finish SRP6 and send the final result to the client + acore::Crypto::SHA1::Digest M2 = acore::Crypto::SRP6::GetSessionVerifier(lp.A, lp.clientM, _sessionKey); + if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients { sAuthLogonProof_S proof; @@ -621,7 +619,7 @@ bool AuthSocket::_HandleLogonProof() proof.error = 0; proof.unk1 = 0x00800000; // Accountflags. 0x01 = GM, 0x08 = Trial, 0x00800000 = Pro pass (arena tournament) proof.unk2 = 0x00; // SurveyId - proof.unk3 = 0x00; + proof.unk3 = 0x00; // 0x1 = has account message socket().send((char*)&proof, sizeof(proof)); } else @@ -642,9 +640,8 @@ bool AuthSocket::_HandleLogonProof() char data[4] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 }; socket().send(data, sizeof(data)); -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - LOG_DEBUG("network", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _accountInfo.Login.c_str()); -#endif + LOG_INFO("server.authserver.hack", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", + socket().getRemoteAddress().c_str(), socket().getRemotePort(), _accountInfo.Login.c_str()); uint32 MaxWrongPassCount = sConfigMgr->GetOption<int32>("WrongPass.MaxCount", 0); @@ -666,42 +663,30 @@ bool AuthSocket::_HandleLogonProof() stmt->setString(0, _accountInfo.Login); LoginDatabase.Execute(stmt); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_FAILEDLOGINS); - stmt->setString(0, _accountInfo.Login); - - if (PreparedQueryResult loginfail = LoginDatabase.Query(stmt)) + if (++_accountInfo.FailedLogins >= MaxWrongPassCount) { - uint32 failed_logins = (*loginfail)[1].GetUInt32(); + uint32 WrongPassBanTime = sConfigMgr->GetOption<int32>("WrongPass.BanTime", 600); + bool WrongPassBanType = sConfigMgr->GetOption<bool>("WrongPass.BanType", false); - if (failed_logins >= MaxWrongPassCount) + if (WrongPassBanType) { - uint32 WrongPassBanTime = sConfigMgr->GetOption<int32>("WrongPass.BanTime", 600); - bool WrongPassBanType = sConfigMgr->GetOption<bool>("WrongPass.BanType", false); - - if (WrongPassBanType) - { - uint32 acc_id = (*loginfail)[0].GetUInt32(); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED); - stmt->setUInt32(0, acc_id); - stmt->setUInt32(1, WrongPassBanTime); - LoginDatabase.Execute(stmt); + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED); + stmt->setUInt32(0, _accountInfo.Id); + stmt->setUInt32(1, WrongPassBanTime); + LoginDatabase.Execute(stmt); -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - LOG_DEBUG("network", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _accountInfo.Login.c_str(), WrongPassBanTime, failed_logins); -#endif - } - else - { - stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED); - stmt->setString(0, socket().getRemoteAddress()); - stmt->setUInt32(1, WrongPassBanTime); - LoginDatabase.Execute(stmt); + LOG_DEBUG("network", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", + socket().getRemoteAddress().c_str(), socket().getRemotePort(), _accountInfo.Login.c_str(), WrongPassBanTime, _accountInfo.FailedLogins); + } + else + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED); + stmt->setString(0, socket().getRemoteAddress()); + stmt->setUInt32(1, WrongPassBanTime); + LoginDatabase.Execute(stmt); -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - LOG_DEBUG("network", "'%s:%d' [AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times", - socket().getRemoteAddress().c_str(), socket().getRemotePort(), socket().getRemoteAddress().c_str(), WrongPassBanTime, _accountInfo.Login.c_str(), failed_logins); -#endif - } + LOG_DEBUG("network", "'%s:%d' [AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times", + socket().getRemoteAddress().c_str(), socket().getRemotePort(), socket().getRemoteAddress().c_str(), WrongPassBanTime, _accountInfo.Login.c_str(), _accountInfo.FailedLogins); } } } @@ -713,9 +698,8 @@ bool AuthSocket::_HandleLogonProof() // Reconnect Challenge command handler bool AuthSocket::_HandleReconnectChallenge() { -#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - LOG_DEBUG("network", "Entering _HandleReconnectChallenge"); -#endif + LOG_TRACE("network", "Entering _HandleReconnectChallenge"); + if (socket().recv_len() < sizeof(sAuthLogonChallenge_C)) return false; @@ -751,6 +735,15 @@ bool AuthSocket::_HandleReconnectChallenge() #endif std::string login((char const*)ch->I, ch->I_len); + LOG_DEBUG("server.authserver", "[ReconnectChallenge] '%s'", login.c_str()); + + // Reinitialize build, expansion and the account securitylevel + _build = ch->build; + _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); + _os = (const char*)ch->os; + + if (_os.size() > 4) + return false; auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE); stmt->setString(0, login); @@ -768,14 +761,6 @@ bool AuthSocket::_HandleReconnectChallenge() Field* fields = result->Fetch(); _accountInfo.LoadResult(fields); - // Reinitialize build, expansion and the account securitylevel - _build = ch->build; - _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); - _os = (const char*)ch->os; - - if (_os.size() > 4) - return false; - // Restore string order as its byte order is reversed std::reverse(_os.begin(), _os.end()); @@ -788,9 +773,9 @@ bool AuthSocket::_HandleReconnectChallenge() // Sending response ByteBuffer pkt; pkt << uint8(AUTH_RECONNECT_CHALLENGE); - pkt << uint8(0x00); + pkt << uint8(WOW_SUCCESS); pkt.append(_reconnectProof); // 16 bytes random - pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros + pkt.append(VersionChallenge.data(), VersionChallenge.size()); socket().send((char const*)pkt.contents(), pkt.size()); return true; } @@ -837,7 +822,8 @@ bool AuthSocket::_HandleReconnectProof() } else { - LOG_ERROR("server", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _accountInfo.Login.c_str()); + LOG_ERROR("server.authserver.hack", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", + socket().getRemoteAddress().c_str(), socket().getRemotePort(), _accountInfo.Login.c_str()); socket().shutdown(); return false; } @@ -849,20 +835,22 @@ ACE_INET_Addr const& AuthSocket::GetAddressForClient(Realm const& realm, ACE_INE if (clientAddr.is_loopback()) { // Try guessing if realm is also connected locally - if (realm.LocalAddress.is_loopback() || realm.ExternalAddress.is_loopback()) + if (realm.LocalAddress->is_loopback() || realm.ExternalAddress->is_loopback()) return clientAddr; // Assume that user connecting from the machine that authserver is located on // has all realms available in his local network - return realm.LocalAddress; + return *realm.LocalAddress; } // Check if connecting client is in the same network - if (IsIPAddrInNetwork(realm.LocalAddress, clientAddr, realm.LocalSubnetMask)) - return realm.LocalAddress; + if (IsIPAddrInNetwork(*realm.LocalAddress, clientAddr, *realm.LocalSubnetMask)) + { + return *realm.LocalAddress; + } // Return external IP - return realm.ExternalAddress; + return *realm.ExternalAddress; } // Realm List command handler @@ -900,17 +888,17 @@ bool AuthSocket::_HandleRealmList() // Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm) ByteBuffer pkt; - size_t RealmListSize = 0; - for (RealmList::RealmMap::const_iterator i = sRealmList->begin(); i != sRealmList->end(); ++i) + + for (auto& [realmHandle, realm] : sRealmList->GetRealms()) { - const Realm& realm = i->second; // don't work with realms which not compatible with the client - bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.gamebuild == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.gamebuild)); + bool okBuild = ((_expversion & POST_BC_EXP_FLAG) && realm.Build == _build) || ((_expversion & PRE_BC_EXP_FLAG) && !AuthHelper::IsPreBCAcceptedClientBuild(realm.Build)); // No SQL injection. id of realm is controlled by the database. - uint32 flag = realm.flag; - RealmBuildInfo const* buildInfo = AuthHelper::GetBuildInfo(realm.gamebuild); + uint32 flag = realm.Flags; + + RealmBuildInfo const* buildInfo = sRealmList->GetBuildInfo(realm.Build); if (!okBuild) { if (!buildInfo) @@ -922,7 +910,7 @@ bool AuthSocket::_HandleRealmList() if (!buildInfo) flag &= ~REALM_FLAG_SPECIFYBUILD; - std::string name = i->first; + std::string name = realm.Name; if (_expversion & PRE_BC_EXP_FLAG && flag & REALM_FLAG_SPECIFYBUILD) { std::ostringstream ss; @@ -931,29 +919,29 @@ bool AuthSocket::_HandleRealmList() } // We don't need the port number from which client connects with but the realm's port - clientAddr.set_port_number(realm.ExternalAddress.get_port_number()); + clientAddr.set_port_number(realm.ExternalAddress->get_port_number()); - uint8 lock = (realm.allowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0; + uint8 lock = (realm.AllowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0; uint8 AmountOfCharacters = 0; stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_NUM_CHARS_ON_REALM); - stmt->setUInt32(0, realm.m_ID); + stmt->setUInt32(0, realm.Id.Realm); stmt->setUInt32(1, id); result = LoginDatabase.Query(stmt); if (result) AmountOfCharacters = (*result)[0].GetUInt8(); - pkt << realm.icon; // realm type + pkt << realm.Type; // realm type if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients pkt << lock; // if 1, then realm locked pkt << uint8(flag); // RealmFlags pkt << name; pkt << GetAddressString(GetAddressForClient(realm, clientAddr)); - pkt << realm.populationLevel; + pkt << realm.PopulationLevel; pkt << AmountOfCharacters; - pkt << realm.timezone; // realm category + pkt << realm.Timezone; // realm category if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients - pkt << uint8(realm.m_ID); + pkt << uint8(realm.Id.Realm); else pkt << uint8(0x0); // 1.12.1 and 1.12.2 clients diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 9137f4423c..aa1eb2ee35 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -16,6 +16,7 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "Realm.h" #include "ScriptMgr.h" #include "SpellMgr.h" #include "UpdateMask.h" @@ -114,7 +115,7 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac if (target) target_sec = target->GetSecurity(); else if (target_account) - target_sec = AccountMgr::GetSecurity(target_account, realmID); + target_sec = AccountMgr::GetSecurity(target_account, realm.Id.Realm); else return true; // caller must report error for (target == nullptr && target_account == 0) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 58111d16e9..c018207ddb 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -57,6 +57,7 @@ #include "QuestDef.h" #include "ReputationMgr.h" #include "revision.h" +#include "Realm.h" #include "SavingSystem.h" #include "ScriptMgr.h" #include "SkillDiscovery.h" diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 25d8a50e21..c3744c6dae 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -26,6 +26,7 @@ #include "Pet.h" #include "Player.h" #include "PlayerDump.h" +#include "Realm.h" #include "ReputationMgr.h" #include "ScriptMgr.h" #include "ServerMotd.h" @@ -647,13 +648,13 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM); stmt->setUInt32(0, GetAccountId()); - stmt->setUInt32(1, realmID); + stmt->setUInt32(1, realm.Id.Realm); trans->Append(stmt); stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS); stmt->setUInt32(0, createInfo->CharCount); stmt->setUInt32(1, GetAccountId()); - stmt->setUInt32(2, realmID); + stmt->setUInt32(2, realm.Id.Realm); trans->Append(stmt); LoginDatabase.CommitTransaction(trans); @@ -996,7 +997,7 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) CharacterDatabase.Execute(stmt); stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_ONLINE); - stmt->setUInt32(0, realmID); + stmt->setUInt32(0, realm.Id.Realm); stmt->setUInt32(1, GetAccountId()); LoginDatabase.Execute(stmt); diff --git a/src/server/game/Handlers/Socialhandler.cpp b/src/server/game/Handlers/Socialhandler.cpp index 07453f6162..9869545023 100644 --- a/src/server/game/Handlers/Socialhandler.cpp +++ b/src/server/game/Handlers/Socialhandler.cpp @@ -10,6 +10,7 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" +#include "Realm.h" #include "SocialMgr.h" #include "World.h" #include "WorldSession.h" @@ -52,7 +53,7 @@ void WorldSession::HandleAddFriendOpcode(WorldPacket& recv_data) TeamId teamId = Player::TeamIdForRace(playerData->race); FriendsResult friendResult = FRIEND_NOT_FOUND; - if (!AccountMgr::IsPlayerAccount(GetSecurity()) || sWorld->getBoolConfig(CONFIG_ALLOW_GM_FRIEND)|| AccountMgr::IsPlayerAccount(AccountMgr::GetSecurity(friendAccountId, realmID))) + if (!AccountMgr::IsPlayerAccount(GetSecurity()) || sWorld->getBoolConfig(CONFIG_ALLOW_GM_FRIEND)|| AccountMgr::IsPlayerAccount(AccountMgr::GetSecurity(friendAccountId, realm.Id.Realm))) { if (friendGuid) { diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index d7383e92c1..f7e6d1f124 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -15,6 +15,7 @@ #include "Opcodes.h" #include "PacketLog.h" #include "Player.h" +#include "Realm.h" #include "ScriptMgr.h" #include "SharedDefines.h" #include "Util.h" @@ -791,7 +792,7 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) { // NOTE: ATM the socket is singlethread, have this in mind ... - uint32 loginServerID, loginServerType, regionID, battlegroupID, realm; + uint32 loginServerID, loginServerType, regionID, battlegroupID, realmid; uint64 DosResponse; uint32 BuiltNumberClient; std::string accountName; @@ -817,7 +818,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) recvPacket.read(clientSeed); recvPacket >> regionID; recvPacket >> battlegroupID; - recvPacket >> realm; + recvPacket >> realmid; recvPacket >> DosResponse; recvPacket.read(digest); @@ -829,7 +830,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // 0 1 2 3 4 5 6 7 8 9 10 // SELECT id, sessionkey, last_ip, locked, lock_country, expansion, mutetime, locale, recruiter, os, totaltime FROM account WHERE username = ? auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); - stmt->setInt32(0, int32(realmID)); + stmt->setInt32(0, int32(realm.Id.Realm)); stmt->setString(1, accountName); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -856,11 +857,10 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); stmt->setString(0, address); stmt->setString(1, accountName); - LoginDatabase.Execute(stmt); // This also allows to check for possible "hack" attempts on account - // even if auth credentials are bad, try using the session key we have - client cannot read auth response error without it + // even if auth credentials are bad, try using the session key we have - client cannot read auth response error without it m_Crypt.Init(account.SessionKey); // First reject the connection if packet contains invalid data or realm state doesn't allow logging in @@ -875,14 +875,14 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) return -1; } - if (realm != realmID) + if (realmid != realm.Id.Realm) { packet.Initialize(SMSG_AUTH_RESPONSE, 1); packet << uint8(REALM_LIST_REALM_NOT_FOUND); SendPacket(packet); LOG_ERROR("server", "WorldSocket::HandleAuthSession: Client %s requested connecting with realm id %u but this realm has id %u set in config.", - address.c_str(), realm, realmID); + address.c_str(), realmid, realm.Id.Realm); sScriptMgr->OnFailedAccountLogin(account.Id); return -1; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 7ebb804b3f..705b4136bf 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -54,6 +54,7 @@ #include "PetitionMgr.h" #include "Player.h" #include "PoolMgr.h" +#include "Realm.h" #include "SavingSystem.h" #include "ScriptMgr.h" #include "ServerMotd.h" @@ -91,6 +92,8 @@ float World::m_MaxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE; float World::m_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE; float World::m_MaxVisibleDistanceInBGArenas = DEFAULT_VISIBILITY_BGARENAS; +Realm realm; + /// World constructor World::World() { @@ -428,7 +431,7 @@ void World::LoadConfigSettings(bool reload) } // Set realm id and enable db logging - sLog->SetRealmId(realmID); + sLog->SetRealmId(realm.Id.Realm); #ifdef ELUNA ///- Initialize Lua Engine @@ -1488,7 +1491,7 @@ void World::SetInitialWorldSettings() uint32 realm_zone = getIntConfig(CONFIG_REALM_ZONE); - LoginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID); // One-time query + LoginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realm.Id.Realm); // One-time query ///- Custom Hook for loading DB items sScriptMgr->OnLoadCustomDatabaseTable(); @@ -1938,7 +1941,7 @@ void World::SetInitialWorldSettings() m_startTime = m_gameTime; LoginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, uptime, revision) VALUES(%u, %u, 0, '%s')", - realmID, uint32(m_startTime), GitRevision::GetFullVersion()); // One-time query + realm.Id.Realm, uint32(m_startTime), GitRevision::GetFullVersion()); // One-time query m_timers[WUPDATE_WEATHERS].SetInterval(1 * IN_MILLISECONDS); m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE * IN_MILLISECONDS); @@ -2316,7 +2319,7 @@ void World::Update(uint32 diff) stmt->setUInt32(0, tmpDiff); stmt->setUInt16(1, uint16(maxOnlinePlayers)); - stmt->setUInt32(2, realmID); + stmt->setUInt32(2, realm.Id.Realm); stmt->setUInt32(3, uint32(m_startTime)); LoginDatabase.Execute(stmt); @@ -2854,13 +2857,13 @@ void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount) PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM); stmt->setUInt32(0, accountId); - stmt->setUInt32(1, realmID); + stmt->setUInt32(1, realm.Id.Realm); trans->Append(stmt); stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS); stmt->setUInt8(0, charCount); stmt->setUInt32(1, accountId); - stmt->setUInt32(2, realmID); + stmt->setUInt32(2, realm.Id.Realm); trans->Append(stmt); LoginDatabase.CommitTransaction(trans); @@ -2988,7 +2991,7 @@ void World::ResetDailyQuests() void World::LoadDBAllowedSecurityLevel() { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL); - stmt->setInt32(0, int32(realmID)); + stmt->setInt32(0, int32(realm.Id.Realm)); PreparedQueryResult result = LoginDatabase.Query(stmt); if (result) @@ -3451,3 +3454,13 @@ void World::RemoveOldCorpses() { m_timers[WUPDATE_CORPSES].SetCurrent(m_timers[WUPDATE_CORPSES].GetInterval()); } + +bool World::IsPvPRealm() const +{ + return getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; +} + +bool World::IsFFAPvPRealm() const +{ + return getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; +} diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 8134a58171..b2005d2073 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -29,7 +29,9 @@ class WorldPacket; class WorldSocket; class SystemMgr; -extern uint32 realmID; +struct Realm; + +AC_GAME_API extern Realm realm; enum ShutdownMask { @@ -74,18 +76,6 @@ enum BillingPlanFlags SESSION_ENABLE_CAIS = 0x80, }; -/// Type of server, this is values from second column of Cfg_Configs.dbc -enum RealmType -{ - REALM_TYPE_NORMAL = 0, - REALM_TYPE_PVP = 1, - REALM_TYPE_NORMAL2 = 4, - REALM_TYPE_RP = 6, - REALM_TYPE_RPPVP = 8, - REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries - // replaced by REALM_PVP in realm list -}; - enum RealmZone { REALM_ZONE_UNKNOWN = 0, // any language @@ -335,8 +325,8 @@ public: void LoadWorldStates(); /// Are we on a "Player versus Player" server? - bool IsPvPRealm() const { return (getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP); } - bool IsFFAPvPRealm() const { return getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; } + [[nodiscard]] bool IsPvPRealm() const; + [[nodiscard]] bool IsFFAPvPRealm() const; void KickAll(); void KickAllLess(AccountTypes sec); diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index be57b08554..f5385b417e 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -18,6 +18,7 @@ EndScriptData */ #include "CryptoGenerics.h" #include "Language.h" #include "Player.h" +#include "Realm.h" #include "ScriptMgr.h" #include "SecretMgr.h" #include "StringConvert.h" @@ -796,7 +797,7 @@ public: } } - // Check if provided realmID has a negative value other than -1 + // Check if provided realm.Id.Realm has a negative value other than -1 if (gmRealmID < -1) { handler->SendSysMessage(LANG_INVALID_REALMID); @@ -804,7 +805,7 @@ public: return false; } - // If gmRealmID is -1, delete all values for the account id, else, insert values for the specific realmID + // If gmRealmID is -1, delete all values for the account id, else, insert values for the specific realm.Id.Realm PreparedStatement* stmt; if (gmRealmID == -1) @@ -818,7 +819,7 @@ public: stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM); stmt->setUInt32(0, targetAccountId); - stmt->setUInt32(1, realmID); + stmt->setUInt32(1, realm.Id.Realm); } LoginDatabase.Execute(stmt); diff --git a/src/server/scripts/Commands/cs_gm.cpp b/src/server/scripts/Commands/cs_gm.cpp index 718c924126..899d6cfad1 100644 --- a/src/server/scripts/Commands/cs_gm.cpp +++ b/src/server/scripts/Commands/cs_gm.cpp @@ -17,6 +17,7 @@ EndScriptData */ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "Realm.h" #include "ScriptMgr.h" #include "World.h" @@ -156,7 +157,7 @@ public: ///- Get the accounts with GM Level >0 PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_GM_ACCOUNTS); stmt->setUInt8(0, uint8(SEC_MODERATOR)); - stmt->setInt32(1, int32(realmID)); + stmt->setInt32(1, int32(realm.Id.Realm)); PreparedQueryResult result = LoginDatabase.Query(stmt); if (result) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 95f45e4632..bf1b93be03 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -24,6 +24,7 @@ #include "Opcodes.h" #include "Pet.h" #include "Player.h" +#include "Realm.h" #include "ScriptMgr.h" #include "SpellAuras.h" #include "TargetedMovementGenerator.h" @@ -1909,7 +1910,7 @@ public: // Query the prepared statement for login data stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_PINFO); - stmt->setInt32(0, int32(realmID)); + stmt->setInt32(0, int32(realm.Id.Realm)); stmt->setUInt32(1, accId); PreparedQueryResult accInfoResult = LoginDatabase.Query(stmt); diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp index 178b8a396a..cefc7758cd 100644 --- a/src/server/scripts/Commands/cs_ticket.cpp +++ b/src/server/scripts/Commands/cs_ticket.cpp @@ -17,6 +17,7 @@ EndScriptData */ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "Realm.h" #include "ScriptMgr.h" #include "TicketMgr.h" @@ -84,7 +85,7 @@ public: // Get target information ObjectGuid targetGuid = sObjectMgr->GetPlayerGUIDByName(target.c_str()); uint32 targetAccountId = sObjectMgr->GetPlayerAccountIdByGUID(targetGuid.GetCounter()); - uint32 targetGmLevel = AccountMgr::GetSecurity(targetAccountId, realmID); + uint32 targetGmLevel = AccountMgr::GetSecurity(targetAccountId, realm.Id.Realm); // Target must exist and have administrative rights if (!targetGuid || AccountMgr::IsPlayerAccount(targetGmLevel)) @@ -382,7 +383,7 @@ public: { ObjectGuid guid = ticket->GetAssignedToGUID(); uint32 accountId = sObjectMgr->GetPlayerAccountIdByGUID(guid.GetCounter()); - security = AccountMgr::GetSecurity(accountId, realmID); + security = AccountMgr::GetSecurity(accountId, realm.Id.Realm); } // Check security diff --git a/src/server/shared/Realms/Realm.h b/src/server/shared/Realms/Realm.h new file mode 100644 index 0000000000..e95e197c5a --- /dev/null +++ b/src/server/shared/Realms/Realm.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore> + */ + +#ifndef Realm_h__ +#define Realm_h__ + +#include "Common.h" +// #include "AsioHacksFwd.h" +#include <ace/INET_Addr.h> + +enum RealmFlags +{ + REALM_FLAG_NONE = 0x00, + REALM_FLAG_VERSION_MISMATCH = 0x01, + REALM_FLAG_OFFLINE = 0x02, + REALM_FLAG_SPECIFYBUILD = 0x04, + REALM_FLAG_UNK1 = 0x08, + REALM_FLAG_UNK2 = 0x10, + REALM_FLAG_RECOMMENDED = 0x20, + REALM_FLAG_NEW = 0x40, + REALM_FLAG_FULL = 0x80 +}; + +struct AC_SHARED_API RealmHandle +{ + RealmHandle() : Realm(0) { } + RealmHandle(uint32 index) : Realm(index) { } + + uint32 Realm; // primary key in `realmlist` table + + bool operator<(RealmHandle const& r) const + { + return Realm < r.Realm; + } +}; + +/// Type of server, this is values from second column of Cfg_Configs.dbc +enum RealmType +{ + REALM_TYPE_NORMAL = 0, + REALM_TYPE_PVP = 1, + REALM_TYPE_NORMAL2 = 4, + REALM_TYPE_RP = 6, + REALM_TYPE_RPPVP = 8, + + MAX_CLIENT_REALM_TYPE = 14, + + REALM_TYPE_FFA_PVP = 16 // custom, free for all pvp mode like arena PvP in all zones except rest activated places and sanctuaries + // replaced by REALM_PVP in realm list +}; + +// Storage object for a realm +struct AC_SHARED_API Realm +{ + RealmHandle Id; + uint32 Build; + std::unique_ptr<ACE_INET_Addr> ExternalAddress; + std::unique_ptr<ACE_INET_Addr> LocalAddress; + std::unique_ptr<ACE_INET_Addr> LocalSubnetMask; + uint16 Port; + std::string Name; + uint8 Type; + RealmFlags Flags; + uint8 Timezone; + AccountTypes AllowedSecurityLevel; + float PopulationLevel; + + // boost::asio::ip::tcp_endpoint GetAddressForClient(boost::asio::ip::address const& clientAddr) const; +}; + +#endif // Realm_h__ diff --git a/src/server/shared/Realms/RealmList.cpp b/src/server/shared/Realms/RealmList.cpp index 3e550d44ad..1bdeaee1ff 100644 --- a/src/server/shared/Realms/RealmList.cpp +++ b/src/server/shared/Realms/RealmList.cpp @@ -4,11 +4,14 @@ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> */ -#include "Common.h" -#include "DatabaseEnv.h" #include "RealmList.h" +#include "DatabaseEnv.h" +#include "Log.h" +#include "Optional.h" +#include "Util.h" -RealmList::RealmList() : m_NextUpdateTime(time(nullptr)) { } +RealmList::RealmList() : + _updateInterval(0) { } RealmList* RealmList::instance() { @@ -19,81 +22,209 @@ RealmList* RealmList::instance() // Load the realm list from the database void RealmList::Initialize(uint32 updateInterval) { - m_UpdateInterval = updateInterval; + _updateInterval = updateInterval; + + LoadBuildInfo(); // Get the content of the realmlist table in the database - UpdateRealms(true); + UpdateRealms(); } -void RealmList::UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build) +void RealmList::LoadBuildInfo() +{ + // 0 1 2 3 4 5 6 + if (QueryResult result = LoginDatabase.Query("SELECT majorVersion, minorVersion, bugfixVersion, hotfixVersion, build, winChecksumSeed, macChecksumSeed FROM build_info ORDER BY build ASC")) + { + do + { + Field* fields = result->Fetch(); + RealmBuildInfo& build = _builds.emplace_back(); + build.MajorVersion = fields[0].GetUInt32(); + build.MinorVersion = fields[1].GetUInt32(); + build.BugfixVersion = fields[2].GetUInt32(); + std::string hotfixVersion = fields[3].GetString(); + + if (hotfixVersion.length() < build.HotfixVersion.size()) + { + std::copy(hotfixVersion.begin(), hotfixVersion.end(), build.HotfixVersion.begin()); + } + else + { + std::fill(hotfixVersion.begin(), hotfixVersion.end(), '\0'); + } + + build.Build = fields[4].GetUInt32(); + std::string windowsHash = fields[5].GetString(); + + if (windowsHash.length() == build.WindowsHash.size() * 2) + { + HexStrToByteArray(windowsHash, build.WindowsHash); + } + + std::string macHash = fields[6].GetString(); + + if (macHash.length() == build.MacHash.size() * 2) + { + HexStrToByteArray(macHash, build.MacHash); + } + } while (result->NextRow()); + } +} + +void RealmList::UpdateRealm(RealmHandle const& id, uint32 build, std::string const& name, + ACE_INET_Addr&& address, ACE_INET_Addr&& localAddr, ACE_INET_Addr&& localSubmask, + uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population) { // Create new if not exist or update existed - Realm& realm = m_realms[name]; - - realm.m_ID = id; - realm.name = name; - realm.icon = icon; - realm.flag = flag; - realm.timezone = timezone; - realm.allowedSecurityLevel = allowedSecurityLevel; - realm.populationLevel = popu; - - // Append port to IP address. - realm.ExternalAddress = address; - realm.LocalAddress = localAddr; - realm.LocalSubnetMask = localSubmask; - realm.gamebuild = build; + Realm& realm = _realms[id]; + + realm.Id = id; + realm.Build = build; + realm.Name = name; + realm.Type = icon; + realm.Flags = flag; + realm.Timezone = timezone; + realm.AllowedSecurityLevel = allowedSecurityLevel; + realm.PopulationLevel = population; + + if (!realm.ExternalAddress || *realm.ExternalAddress != address) + { + realm.ExternalAddress = std::make_unique<ACE_INET_Addr>(std::move(address)); + } + + if (!realm.LocalAddress || *realm.LocalAddress != localAddr) + { + realm.LocalAddress = std::make_unique<ACE_INET_Addr>(std::move(localAddr)); + } + + if (!realm.LocalSubnetMask || *realm.LocalSubnetMask != localSubmask) + { + realm.LocalSubnetMask = std::make_unique<ACE_INET_Addr>(std::move(localSubmask)); + } + + realm.Port = port; } void RealmList::UpdateIfNeed() { // maybe disabled or updated recently - if (!m_UpdateInterval || m_NextUpdateTime > time(nullptr)) + if (!_updateInterval || _nextUpdateTime > time(nullptr)) + { return; + } - m_NextUpdateTime = time(nullptr) + m_UpdateInterval; - - // Clears Realm list - m_realms.clear(); + _nextUpdateTime = time(nullptr) + _updateInterval; // Get the content of the realmlist table in the database UpdateRealms(); } -void RealmList::UpdateRealms(bool init) +void RealmList::UpdateRealms() { - LOG_INFO("server", "Updating Realm List..."); + LOG_DEBUG("server.authserver", "Updating Realm List..."); PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST); PreparedQueryResult result = LoginDatabase.Query(stmt); + std::map<RealmHandle, std::string> existingRealms; + for (auto const& [handle, realm] : _realms) + { + existingRealms[handle] = realm.Name; + } + + _realms.clear(); + // Circle through results and add them to the realm map if (result) { do { - Field* fields = result->Fetch(); - uint32 realmId = fields[0].GetUInt32(); - std::string name = fields[1].GetString(); - std::string externalAddress = fields[2].GetString(); - std::string localAddress = fields[3].GetString(); - std::string localSubmask = fields[4].GetString(); - uint16 port = fields[5].GetUInt16(); - uint8 icon = fields[6].GetUInt8(); + Field* fields = result->Fetch(); + uint32 realmId = fields[0].GetUInt32(); + std::string name = fields[1].GetString(); + std::string externalAddressString = fields[2].GetString(); + std::string localAddressString = fields[3].GetString(); + std::string localSubmaskString = fields[4].GetString(); + uint16 port = fields[5].GetUInt16(); + + Optional<ACE_INET_Addr> externalAddress = ACE_INET_Addr(port, externalAddressString.c_str(), AF_INET); + if (!externalAddress) + { + LOG_ERROR("server.authserver", "Could not resolve address %s for realm \"%s\" id %u", externalAddressString.c_str(), name.c_str(), realmId); + continue; + } + + Optional<ACE_INET_Addr> localAddress = ACE_INET_Addr(port, localAddressString.c_str(), AF_INET); + if (!localAddress) + { + LOG_ERROR("server.authserver", "Could not resolve localAddress %s for realm \"%s\" id %u", localAddressString.c_str(), name.c_str(), realmId); + continue; + } + + Optional<ACE_INET_Addr> localSubmask = ACE_INET_Addr(0, localSubmaskString.c_str(), AF_INET); + if (!localSubmask) + { + LOG_ERROR("server.authserver", "Could not resolve localSubnetMask %s for realm \"%s\" id %u", localSubmaskString.c_str(), name.c_str(), realmId); + continue; + } + + uint8 icon = fields[6].GetUInt8(); + + if (icon == REALM_TYPE_FFA_PVP) + { + icon = REALM_TYPE_PVP; + } + + if (icon >= MAX_CLIENT_REALM_TYPE) + { + icon = REALM_TYPE_NORMAL; + } + RealmFlags flag = RealmFlags(fields[7].GetUInt8()); uint8 timezone = fields[8].GetUInt8(); uint8 allowedSecurityLevel = fields[9].GetUInt8(); float pop = fields[10].GetFloat(); uint32 build = fields[11].GetUInt32(); - ACE_INET_Addr externalAddr(port, externalAddress.c_str(), AF_INET); - ACE_INET_Addr localAddr(port, localAddress.c_str(), AF_INET); - ACE_INET_Addr submask(0, localSubmask.c_str(), AF_INET); + RealmHandle id{ realmId }; + + UpdateRealm(id, build, name, std::move(externalAddress.value()), std::move(localAddress.value()), std::move(localSubmask.value()), port, icon, flag, + timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop); - UpdateRealm(realmId, name, externalAddr, localAddr, submask, icon, flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop, build); + if (!existingRealms.count(id)) + { + LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port); + } + else + { + LOG_DEBUG("server.authserver", "Updating realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port); + } - if (init) - LOG_INFO("server", "Added realm \"%s\" at %s:%u.", name.c_str(), m_realms[name].ExternalAddress.get_host_addr(), port); + existingRealms.erase(id); } while (result->NextRow()); } } + +Realm const* RealmList::GetRealm(RealmHandle const& id) const +{ + auto itr = _realms.find(id); + if (itr != _realms.end()) + { + return &itr->second; + } + + return nullptr; +} + +RealmBuildInfo const* RealmList::GetBuildInfo(uint32 build) const +{ + for (RealmBuildInfo const& clientBuild : _builds) + { + if (clientBuild.Build == build) + { + return &clientBuild; + } + } + + return nullptr; +} diff --git a/src/server/shared/Realms/RealmList.h b/src/server/shared/Realms/RealmList.h index 1b951e51c1..938fb5aa36 100644 --- a/src/server/shared/Realms/RealmList.h +++ b/src/server/shared/Realms/RealmList.h @@ -7,43 +7,29 @@ #ifndef _REALMLIST_H #define _REALMLIST_H -#include "Common.h" -#include <ace/INET_Addr.h> - -enum RealmFlags -{ - REALM_FLAG_NONE = 0x00, - REALM_FLAG_INVALID = 0x01, - REALM_FLAG_OFFLINE = 0x02, - REALM_FLAG_SPECIFYBUILD = 0x04, - REALM_FLAG_UNK1 = 0x08, - REALM_FLAG_UNK2 = 0x10, - REALM_FLAG_RECOMMENDED = 0x20, - REALM_FLAG_NEW = 0x40, - REALM_FLAG_FULL = 0x80 -}; - -// Storage object for a realm -struct Realm +#include "Define.h" +#include "Realm.h" +#include <array> +#include <map> +#include <vector> +#include <unordered_set> + +struct RealmBuildInfo { - ACE_INET_Addr ExternalAddress; - ACE_INET_Addr LocalAddress; - ACE_INET_Addr LocalSubnetMask; - std::string name; - uint8 icon; - RealmFlags flag; - uint8 timezone; - uint32 m_ID; - AccountTypes allowedSecurityLevel; - float populationLevel; - uint32 gamebuild; + uint32 Build; + uint32 MajorVersion; + uint32 MinorVersion; + uint32 BugfixVersion; + std::array<char, 4> HotfixVersion; + std::array<uint8, 20> WindowsHash; + std::array<uint8, 20> MacHash; }; /// Storage object for the list of realms on the server -class RealmList +class AC_SHARED_API RealmList { public: - typedef std::map<std::string, Realm> RealmMap; + typedef std::map<RealmHandle, Realm> RealmMap; RealmList(); ~RealmList() = default; @@ -52,19 +38,23 @@ public: void Initialize(uint32 updateInterval); void UpdateIfNeed(); - void AddRealm(const Realm& NewRealm) { m_realms[NewRealm.name] = NewRealm; } - [[nodiscard]] RealmMap::const_iterator begin() const { return m_realms.begin(); } - [[nodiscard]] RealmMap::const_iterator end() const { return m_realms.end(); } - [[nodiscard]] uint32 size() const { return m_realms.size(); } + RealmMap const& GetRealms() const { return _realms; } + Realm const* GetRealm(RealmHandle const& id) const; -private: - void UpdateRealms(bool init = false); - void UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build); + RealmBuildInfo const* GetBuildInfo(uint32 build) const; - RealmMap m_realms; - uint32 m_UpdateInterval{0}; - time_t m_NextUpdateTime; +private: + void LoadBuildInfo(); + void UpdateRealms(); + void UpdateRealm(RealmHandle const& id, uint32 build, std::string const& name, + ACE_INET_Addr&& address, ACE_INET_Addr&& localAddr, ACE_INET_Addr&& localSubmask, + uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population); + + std::vector<RealmBuildInfo> _builds; + RealmMap _realms; + uint32 _updateInterval; + time_t _nextUpdateTime; }; #define sRealmList RealmList::instance() diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 62e840d461..0567be32ff 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -39,8 +39,6 @@ char serviceDescription[] = "AzerothCore World of Warcraft emulator world servic int m_ServiceStatus = -1; #endif -uint32 realmID; ///< Id of the realm - /// Print out the usage string for this program on the console. void usage(const char* prog) { diff --git a/src/server/worldserver/Master.cpp b/src/server/worldserver/Master.cpp index 7fe941aaa1..35f4ea78a1 100644 --- a/src/server/worldserver/Master.cpp +++ b/src/server/worldserver/Master.cpp @@ -20,7 +20,7 @@ #include "Master.h" #include "OpenSSLCrypto.h" #include "RARunnable.h" -#include "RealmList.h" +#include "Realm.h" #include "ScriptMgr.h" #include "SignalHandler.h" #include "Timer.h" @@ -30,6 +30,7 @@ #include "WorldSocket.h" #include "WorldSocketMgr.h" #include "DatabaseLoader.h" +#include "Optional.h" #include "SecretMgr.h" #include <ace/Sig_Handler.h> @@ -100,6 +101,8 @@ public: } }; +bool LoadRealmInfo(); + Master* Master::instance() { static Master instance; @@ -131,7 +134,9 @@ int Master::Run() return 1; // set server offline (not connectable) - LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_INVALID, realmID); + LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm); + + LoadRealmInfo(); // Loading modules configs sConfigMgr->LoadModulesConfigs(); @@ -271,7 +276,7 @@ int Master::Run() } // set server online (allow connecting now) - LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID); + LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm); LOG_INFO("server", "%s (worldserver-daemon) ready...", GitRevision::GetFullVersion()); @@ -295,7 +300,7 @@ int Master::Run() } // set server offline - LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realmID); + LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm); ///- Clean database before leaving ClearOnlineAccounts(); @@ -379,16 +384,16 @@ bool Master::_StartDB() return false; ///- Get the realm Id from the configuration file - realmID = sConfigMgr->GetOption<int32>("RealmID", 0); - if (!realmID) + realm.Id.Realm = sConfigMgr->GetOption<int32>("RealmID", 0); + if (!realm.Id.Realm) { LOG_ERROR("server", "Realm ID not defined in configuration file"); return false; } - else if (realmID > 255) + else if (realm.Id.Realm > 255) { /* - * Due to the client only being able to read a realmID + * Due to the client only being able to read a realm.Id.Realm * with a size of uint8 we can "only" store up to 255 realms * anything further the client will behave anormaly */ @@ -396,7 +401,7 @@ bool Master::_StartDB() return false; } - LOG_INFO("server", "Realm running as realm ID %d", realmID); + LOG_INFO("server", "Realm running as realm ID %d", realm.Id.Realm); ///- Clean the database before starting ClearOnlineAccounts(); @@ -424,8 +429,55 @@ void Master::ClearOnlineAccounts() { // Reset online status for all accounts with characters on the current realm // pussywizard: tc query would set online=0 even if logged in on another realm >_> - LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online = %u", realmID); + LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online = %u", realm.Id.Realm); // Reset online status for all characters CharacterDatabase.DirectExecute("UPDATE characters SET online = 0 WHERE online <> 0"); } + +bool LoadRealmInfo() +{ + QueryResult result = LoginDatabase.PQuery("SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE id = %u", realm.Id.Realm); + if (!result) + { + LOG_ERROR("server.worldserver", "> Not found realm with ID %u", realm.Id.Realm); + return false; + } + + Field* fields = result->Fetch(); + realm.Name = fields[1].GetString(); + realm.Port = fields[5].GetUInt16(); + + Optional<ACE_INET_Addr> externalAddress = ACE_INET_Addr(realm.Port, fields[2].GetCString(), AF_INET); + if (!externalAddress) + { + LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[2].GetString().c_str()); + return false; + } + + Optional<ACE_INET_Addr> localAddress = ACE_INET_Addr(realm.Port, fields[3].GetCString(), AF_INET); + if (!localAddress) + { + LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[3].GetString().c_str()); + return false; + } + + Optional<ACE_INET_Addr> localSubmask = ACE_INET_Addr(0, fields[4].GetCString(), AF_INET); + if (!localSubmask) + { + LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[4].GetString().c_str()); + return false; + } + + realm.ExternalAddress = std::make_unique<ACE_INET_Addr>(*externalAddress); + realm.LocalAddress = std::make_unique<ACE_INET_Addr>(*localAddress); + realm.LocalSubnetMask = std::make_unique<ACE_INET_Addr>(*localSubmask); + + realm.Type = fields[6].GetUInt8(); + realm.Flags = RealmFlags(fields[7].GetUInt8()); + realm.Timezone = fields[8].GetUInt8(); + realm.AllowedSecurityLevel = AccountTypes(fields[9].GetUInt8()); + realm.PopulationLevel = fields[10].GetFloat(); + realm.Build = fields[11].GetUInt32(); + return true; +} diff --git a/src/test/mocks/WorldMock.h b/src/test/mocks/WorldMock.h index 059497bd46..f58be22e22 100644 --- a/src/test/mocks/WorldMock.h +++ b/src/test/mocks/WorldMock.h @@ -12,7 +12,6 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" -uint32 realmID; void AddScripts() {} bool ArenaSpectator::HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args) { return false; } |