TrinityCore
Loading...
Searching...
No Matches
RealmList.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "RealmList.h"
20#include "CryptoRandom.h"
21#include "DatabaseEnv.h"
22#include "DeadlineTimer.h"
23#include "Log.h"
24#include "MapUtils.h"
25#include "ProtobufJSON.h"
26#include "Resolver.h"
27#include "Util.h"
28#include "RealmList.pb.h"
29#include "advstd.h"
30#include <boost/asio/ip/tcp.hpp>
31#include <zlib.h>
32
33namespace
34{
35bool CompressJson(std::string const& json, std::vector<uint8>* compressed)
36{
37 uLong uncompressedLength = uLong(json.length() + 1);
38 uLong compressedLength = compressBound(uLong(json.length()));
39 compressed->resize(compressedLength + 4);
40 memcpy(compressed->data(), &uncompressedLength, sizeof(uncompressedLength));
41
42 if (compress(compressed->data() + 4, &compressedLength, reinterpret_cast<uint8 const*>(json.data()), uncompressedLength) != Z_OK)
43 {
44 compressed->clear();
45 return false;
46 }
47
48 compressed->resize(compressedLength + 4); // trim excess bytes
49 return true;
50}
51}
52
53RealmList::RealmList() : _updateInterval(0)
54{
55}
56
57RealmList::~RealmList() = default;
58
60{
61 static RealmList instance;
62 return &instance;
63}
64
65// Load the realm list from the database
67{
68 _updateInterval = updateInterval;
69 _updateTimer = std::make_unique<Trinity::Asio::DeadlineTimer>(ioContext);
70 _resolver = std::make_unique<Trinity::Net::Resolver>(ioContext);
71
73 // Get the content of the realmlist table in the database
75}
76
78{
79 _updateTimer->cancel();
80}
81
82void RealmList::UpdateRealm(Realm& realm, Battlenet::RealmHandle const& id, uint32 build, std::string const& name,
83 std::vector<boost::asio::ip::address>&& addresses,
84 uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel,
85 RealmPopulationState population)
86{
87 realm.Id = id;
88 realm.Build = build;
89 if (realm.Name != name)
90 realm.SetName(name);
91 realm.Type = icon;
92 realm.Flags = flag;
93 realm.Timezone = timezone;
94 realm.AllowedSecurityLevel = allowedSecurityLevel;
95 realm.PopulationLevel = population;
96 realm.Addresses = std::move(addresses);
97 realm.Port = port;
98}
99
101{
102 TC_LOG_DEBUG("realmlist", "Updating Realm List...");
103
105 PreparedQueryResult result = LoginDatabase.Query(stmt);
106
107 std::map<Battlenet::RealmHandle, std::string> existingRealms;
108 for (auto const& p : _realms)
109 existingRealms[p.first] = p.second->Name;
110
111 std::unordered_set<std::string> newSubRegions;
112 RealmMap newRealms;
113
114 // Circle through results and add them to the realm map
115 if (result)
116 {
117 do
118 {
119 Field* fields = result->Fetch();
120 uint32 realmId = fields[0].GetUInt32();
121 std::string name = fields[1].GetString();
122 std::vector<boost::asio::ip::address> addresses;
123
124 for (std::size_t i = 0; i < 4; ++i)
125 {
126 if (Optional<std::string_view> addressStr = fields[2 + i].GetStringViewOrNull())
127 {
128 for (boost::asio::ip::tcp::endpoint const& endpoint : _resolver->ResolveAll(*addressStr, ""))
129 {
130 boost::asio::ip::address address = endpoint.address();
131 if (advstd::ranges::contains(addresses, address))
132 continue;
133
134 addresses.push_back(std::move(address));
135 }
136 }
137 }
138
139 if (addresses.empty())
140 {
141 TC_LOG_ERROR("realmlist", "Could not resolve any address for realm \"{}\" id {}", name, realmId);
142 continue;
143 }
144
145 uint16 port = fields[6].GetUInt16();
146 uint8 icon = fields[7].GetUInt8();
147 if (icon == REALM_TYPE_FFA_PVP)
148 icon = REALM_TYPE_PVP;
149 if (icon >= MAX_CLIENT_REALM_TYPE)
150 icon = REALM_TYPE_NORMAL;
151 RealmFlags flag = ConvertLegacyRealmFlags(Trinity::Legacy::RealmFlags(fields[8].GetUInt8()));
152 uint8 timezone = fields[9].GetUInt8();
153 uint8 allowedSecurityLevel = fields[10].GetUInt8();
154 RealmPopulationState pop = ConvertLegacyPopulationState(Trinity::Legacy::RealmFlags(fields[8].GetUInt8()), fields[11].GetFloat());
155 uint32 build = fields[12].GetUInt32();
156 uint8 region = fields[13].GetUInt8();
157 uint8 battlegroup = fields[14].GetUInt8();
158
159 Battlenet::RealmHandle id{ region, battlegroup, realmId };
160
161 UpdateRealm(*newRealms.try_emplace(id, std::make_shared<Realm>()).first->second, id, build, name, std::move(addresses), port, icon,
162 flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
163
164 newSubRegions.insert(Battlenet::RealmHandle{ region, battlegroup, 0 }.GetAddressString());
165
166 auto buildAddressesLogText = [&]
167 {
168 std::string text;
169 for (boost::asio::ip::address const& address : newRealms[id]->Addresses)
170 {
171 text += address.to_string();
172 text += ' ';
173 }
174 return text;
175 };
176
177 if (!existingRealms.erase(id))
178 TC_LOG_INFO("realmlist", "Added realm \"{}\" at {}(port {}).", name, buildAddressesLogText(), port);
179 else
180 TC_LOG_DEBUG("realmlist", "Updating realm \"{}\" at {}(port {}).", name, buildAddressesLogText(), port);
181 }
182 while (result->NextRow());
183 }
184
185 for (auto itr = existingRealms.begin(); itr != existingRealms.end(); ++itr)
186 TC_LOG_INFO("realmlist", "Removed realm \"{}\".", itr->second);
187
188 {
189 std::scoped_lock lock(_realmsMutex);
190
191 _subRegions.swap(newSubRegions);
192 _realms.swap(newRealms);
193 _removedRealms.swap(existingRealms);
194
195 if (_currentRealmId)
196 if (std::shared_ptr<Realm> realm = Trinity::Containers::MapGetValuePtr(_realms, *_currentRealmId))
197 _currentRealmId = realm->Id; // fill other fields of realm id
198 }
199
200 if (_updateInterval)
201 {
202 _updateTimer->expires_after(std::chrono::seconds(_updateInterval));
203 _updateTimer->async_wait([this](boost::system::error_code const& error)
204 {
205 if (error)
206 return;
207
208 UpdateRealms();
209 });
210 }
211}
212
213std::shared_ptr<Realm const> RealmList::GetRealm(Battlenet::RealmHandle const& id) const
214{
215 std::shared_lock lock(_realmsMutex);
217}
218
223
228
229std::shared_ptr<Realm const> RealmList::GetCurrentRealm() const
230{
231 if (_currentRealmId)
232 return GetRealm(*_currentRealmId);
233 return nullptr;
234}
235
236std::vector<std::string> RealmList::GetSubRegions() const
237{
238 std::shared_lock lock(_realmsMutex);
239 return { _subRegions.begin(), _subRegions.end() };
240}
241
242void RealmList::FillRealmEntry(Realm const& realm, uint32 clientBuild, AccountTypes accountSecurityLevel, JSON::RealmList::RealmEntry* realmEntry) const
243{
244 realmEntry->set_wowrealmaddress(realm.Id.GetAddress());
245 realmEntry->set_cfgtimezonesid(1);
246 if (accountSecurityLevel >= realm.AllowedSecurityLevel || realm.PopulationLevel == RealmPopulationState::Offline)
247 realmEntry->set_populationstate(AsUnderlyingType(realm.PopulationLevel));
248 else
249 realmEntry->set_populationstate(AsUnderlyingType(RealmPopulationState::Locked));
250
251 realmEntry->set_cfgcategoriesid(realm.Timezone);
252
253 JSON::RealmList::ClientVersion* version = realmEntry->mutable_version();
254 if (ClientBuild::Info const* buildInfo = ClientBuild::GetBuildInfo(realm.Build))
255 {
256 version->set_versionmajor(buildInfo->MajorVersion);
257 version->set_versionminor(buildInfo->MinorVersion);
258 version->set_versionrevision(buildInfo->BugfixVersion);
259 version->set_versionbuild(buildInfo->Build);
260 }
261 else
262 {
263 version->set_versionmajor(6);
264 version->set_versionminor(2);
265 version->set_versionrevision(4);
266 version->set_versionbuild(realm.Build);
267 }
268
269 RealmFlags flag = realm.Flags;
270 if (realm.Build != clientBuild)
272
273 realmEntry->set_cfgrealmsid(realm.Id.Realm);
274 realmEntry->set_flags(AsUnderlyingType(flag));
275 realmEntry->set_name(realm.Name);
276 realmEntry->set_cfgconfigsid(realm.GetConfigId());
277 realmEntry->set_cfglanguagesid(1);
278 realmEntry->set_cfgcontentsetid(0);
279 realmEntry->set_usebleepchance(0.0f);
280}
281
282std::string RealmList::GetRealmEntryJSON(Battlenet::RealmHandle const& id, uint32 build, AccountTypes accountSecurityLevel) const
283{
284 if (std::shared_ptr<Realm const> realm = GetRealm(id))
285 {
286 if (realm->PopulationLevel != RealmPopulationState::Offline && realm->Build == build && accountSecurityLevel >= realm->AllowedSecurityLevel)
287 {
288 JSON::RealmList::RealmEntry realmEntry;
289 FillRealmEntry(*realm, build, accountSecurityLevel, &realmEntry);
290 return JSON::Serialize(realmEntry);
291 }
292 }
293
294 return { };
295}
296
297std::vector<uint8> RealmList::GetRealmList(uint32 build, AccountTypes accountSecurityLevel, std::string const& subRegion) const
298{
299 JSON::RealmList::RealmListUpdates realmList;
300 {
301 std::shared_lock lock(_realmsMutex);
302 for (auto const& [_, realm] : _realms)
303 {
304 if (realm->Id.GetSubRegionAddress() != subRegion)
305 continue;
306
307 JSON::RealmList::RealmListUpdatePart* state = realmList.add_updates();
308 FillRealmEntry(*realm, build, accountSecurityLevel, state->mutable_update());
309 state->set_deleting(false);
310 }
311
312 for (auto const& [id, _] : _removedRealms)
313 {
314 if (id.GetSubRegionAddress() != subRegion)
315 continue;
316
317 JSON::RealmList::RealmListUpdatePart* state = realmList.add_updates();
318 state->set_wowrealmaddress(id.GetAddress());
319 state->set_deleting(true);
320 }
321 }
322
323 std::string json = "JSONRealmListUpdates:" + JSON::Serialize(realmList);
324 std::vector<uint8> compressed;
325 CompressJson(json, &compressed);
326 return compressed;
327}
328
329RealmJoinResult RealmList::JoinRealm(uint32 realmAddress, uint32 build, ClientBuild::VariantId const& buildVariant, boost::asio::ip::address const& clientAddress,
330 std::array<uint8, 32> const& clientSecret, LocaleConstant locale, std::string const& os, Minutes timezoneOffset, std::string const& accountName,
331 AccountTypes accountSecurityLevel) const
332{
333 if (std::shared_ptr<Realm const> realm = GetRealm(realmAddress))
334 {
335 if (realm->PopulationLevel == RealmPopulationState::Offline || realm->Build != build || accountSecurityLevel < realm->AllowedSecurityLevel)
337
338 boost::asio::ip::address addressForClient = realm->GetAddressForClient(clientAddress);
339
340 JSON::RealmList::RealmListServerIPAddresses serverAddresses;
341 JSON::RealmList::RealmIPAddressFamily* addressFamily = serverAddresses.add_families();
342 addressFamily->set_family(addressForClient.is_v6() ? 2 : 1);
343
344 JSON::RealmList::IPAddress* address = addressFamily->add_addresses();
345 address->set_ip(addressForClient.to_string());
346 address->set_port(realm->Port);
347
348 std::string json = "JSONRealmListServerIPAddresses:" + JSON::Serialize(serverAddresses);
349 std::vector<uint8> serverAddressesCompressed;
350
351 if (!CompressJson(json, &serverAddressesCompressed))
353
354 std::vector<uint8> serverSecret(32);
356
357 std::array<uint8, 64> keyData;
358 auto keyDestItr = keyData.begin();
359 keyDestItr = std::ranges::copy(clientSecret, keyDestItr).out;
360 keyDestItr = std::ranges::copy(serverSecret, keyDestItr).out;
361
363 stmt->setBinary(0, keyData);
364 stmt->setString(1, clientAddress.to_string());
365 stmt->setUInt32(2, build);
366 stmt->setUInt8(3, locale);
367 stmt->setString(4, os);
368 stmt->setInt16(5, timezoneOffset.count());
369 stmt->setString(6, accountName);
370 LoginDatabase.DirectExecute(stmt);
371
372 JSON::RealmList::RealmJoinTicket joinTicket;
373 joinTicket.set_gameaccount(accountName);
374 joinTicket.set_platform(buildVariant.Platform);
375 joinTicket.set_clientarch(buildVariant.Arch);
376 joinTicket.set_type(buildVariant.Type);
377
378 std::string joinTicketJson = JSON::Serialize(joinTicket);
379
380 return {
381 .Result = ERROR_OK,
382 .JoinTicket = { joinTicketJson.begin(), joinTicketJson.end() },
383 .ServerAddresses = std::move(serverAddressesCompressed),
384 .JoinSecret = std::move(serverSecret)
385 };
386 }
387
388 return { .Result = ERROR_UTIL_SERVER_UNKNOWN_REALM };
389}
@ ERROR_UTIL_SERVER_FAILED_TO_SERIALIZE_RESPONSE
@ ERROR_USER_SERVER_NOT_PERMITTED_ON_REALM
@ ERROR_UTIL_SERVER_UNKNOWN_REALM
LocaleConstant
Definition Common.h:51
AccountTypes
Definition Common.h:42
@ SEC_ADMINISTRATOR
Definition Common.h:46
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
uint8_t uint8
Definition Define.h:156
uint16_t uint16
Definition Define.h:155
uint32_t uint32
Definition Define.h:154
std::chrono::minutes Minutes
Minutes shorthand typedef.
Definition Duration.h:32
#define TC_LOG_DEBUG(filterType__, message__,...)
Definition Log.h:173
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:182
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:176
@ LOGIN_SEL_REALMLIST
@ LOGIN_UPD_BNET_GAME_ACCOUNT_LOGIN_INFO
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
RealmFlags
Definition Realm.h:28
RealmPopulationState
Definition Realm.h:43
@ REALM_TYPE_FFA_PVP
Definition Realm.h:131
@ MAX_CLIENT_REALM_TYPE
Definition Realm.h:129
@ REALM_TYPE_PVP
Definition Realm.h:124
@ REALM_TYPE_NORMAL
Definition Realm.h:123
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
Class used to access individual fields of database query result.
Definition Field.h:94
uint32 GetUInt32() const noexcept
Definition Field.cpp:57
uint16 GetUInt16() const noexcept
Definition Field.cpp:43
uint8 GetUInt8() const noexcept
Definition Field.cpp:29
std::string GetString() const noexcept
Definition Field.cpp:113
void setBinary(uint8 index, std::vector< uint8 > &&value)
void setInt16(uint8 index, int16 value)
void setString(uint8 index, std::string &&value)
void setUInt32(uint8 index, uint32 value)
void setUInt8(uint8 index, uint8 value)
Storage object for the list of realms on the server.
Definition RealmList.h:48
void FillRealmEntry(Realm const &realm, uint32 clientBuild, AccountTypes accountSecurityLevel, JSON::RealmList::RealmEntry *realmEntry) const
std::shared_ptr< Realm const > GetCurrentRealm() const
void Close()
Definition RealmList.cpp:77
void UpdateRealms()
std::map< Battlenet::RealmHandle, std::shared_ptr< Realm > > RealmMap
Definition RealmList.h:50
std::map< Battlenet::RealmHandle, std::string > _removedRealms
Definition RealmList.h:87
RealmMap _realms
Definition RealmList.h:86
std::shared_ptr< Realm const > GetRealm(Battlenet::RealmHandle const &id) const
static void UpdateRealm(Realm &realm, Battlenet::RealmHandle const &id, uint32 build, std::string const &name, std::vector< boost::asio::ip::address > &&addresses, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, RealmPopulationState population)
Definition RealmList.cpp:82
Battlenet::RealmHandle GetCurrentRealmId() const
void SetCurrentRealmId(Battlenet::RealmHandle const &id)
std::vector< uint8 > GetRealmList(uint32 build, AccountTypes accountSecurityLevel, std::string const &subRegion) const
std::string GetRealmEntryJSON(Battlenet::RealmHandle const &id, uint32 build, AccountTypes accountSecurityLevel) const
std::unordered_set< std::string > _subRegions
Definition RealmList.h:88
uint32 _updateInterval
Definition RealmList.h:89
Optional< Battlenet::RealmHandle > _currentRealmId
Definition RealmList.h:92
void Initialize(Trinity::Asio::IoContext &ioContext, uint32 updateInterval)
Definition RealmList.cpp:66
std::shared_mutex _realmsMutex
Definition RealmList.h:85
std::vector< std::string > GetSubRegions() const
std::unique_ptr< Trinity::Net::Resolver > _resolver
Definition RealmList.h:91
std::unique_ptr< Trinity::Asio::DeadlineTimer > _updateTimer
Definition RealmList.h:90
static RealmList * Instance()
Definition RealmList.cpp:59
RealmJoinResult JoinRealm(uint32 realmAddress, uint32 build, ClientBuild::VariantId const &buildVariant, boost::asio::ip::address const &clientAddress, std::array< uint8, 32 > const &clientSecret, LocaleConstant locale, std::string const &os, Minutes timezoneOffset, std::string const &accountName, AccountTypes accountSecurityLevel) const
Info const * GetBuildInfo(uint32 build)
TC_SHARED_API std::string Serialize(google::protobuf::Message const &message)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:37
std::array< uint8, S > GetRandomBytes()
struct advstd::ranges::Contains contains
std::string GetAddressString() const
Definition Realm.cpp:60
uint32 GetAddress() const
Definition Realm.h:114
ClientBuild::Type::Id Type
ClientBuild::Arch::Id Arch
ClientBuild::Platform::Id Platform
Definition Realm.h:139
uint16 Port
Definition Realm.h:150
RealmFlags Flags
Definition Realm.h:154
AccountTypes AllowedSecurityLevel
Definition Realm.h:156
uint32 GetConfigId() const
Definition Realm.cpp:50
uint8 Timezone
Definition Realm.h:155
uint32 Build
Definition Realm.h:148
std::string Name
Definition Realm.h:151
RealmPopulationState PopulationLevel
Definition Realm.h:157
void SetName(std::string name)
Definition Realm.cpp:32
Battlenet::RealmHandle Id
Definition Realm.h:147
std::vector< boost::asio::ip::address > Addresses
Definition Realm.h:149
uint8 Type
Definition Realm.h:153