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"
29#include "RealmList.pb.h"
30#include "advstd.h"
31#include <boost/asio/ip/tcp.hpp>
32#include <zlib.h>
33
34namespace
35{
36bool CompressJson(std::string const& json, std::vector<uint8>* compressed)
37{
38 uLong uncompressedLength = uLong(json.length() + 1);
39 uLong compressedLength = compressBound(uLong(json.length()));
40 compressed->resize(compressedLength + 4);
41 memcpy(compressed->data(), &uncompressedLength, sizeof(uncompressedLength));
42
43 if (compress(compressed->data() + 4, &compressedLength, reinterpret_cast<uint8 const*>(json.data()), uncompressedLength) != Z_OK)
44 {
45 compressed->clear();
46 return false;
47 }
48
49 compressed->resize(compressedLength + 4); // trim excess bytes
50 return true;
51}
52}
53
54RealmList::RealmList() : _updateInterval(0)
55{
56}
57
58RealmList::~RealmList() = default;
59
61{
62 static RealmList instance;
63 return &instance;
64}
65
66// Load the realm list from the database
68{
69 _updateInterval = updateInterval;
70 _updateTimer = std::make_unique<Trinity::Asio::DeadlineTimer>(ioContext);
71 _resolver = std::make_unique<Trinity::Net::Resolver>(ioContext);
72
74 // Get the content of the realmlist table in the database
76}
77
79{
80 _updateTimer->cancel();
81}
82
83void RealmList::UpdateRealm(Realm& realm, Battlenet::RealmHandle const& id, uint32 build, std::string const& name,
84 std::vector<boost::asio::ip::address>&& addresses,
85 uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel,
86 RealmPopulationState population)
87{
88 realm.Id = id;
89 realm.Build = build;
90 if (realm.Name != name)
91 realm.SetName(name);
92 realm.Type = icon;
93 realm.Flags = flag;
94 realm.Timezone = timezone;
95 realm.AllowedSecurityLevel = allowedSecurityLevel;
96 realm.PopulationLevel = population;
97 realm.Addresses = std::move(addresses);
98 realm.Port = port;
99}
100
102{
103 TC_LOG_DEBUG("realmlist", "Updating Realm List...");
104
106 PreparedQueryResult result = LoginDatabase.Query(stmt);
107
108 std::map<Battlenet::RealmHandle, std::string> existingRealms;
109 for (auto const& p : _realms)
110 existingRealms[p.first] = p.second->Name;
111
112 std::unordered_set<std::string> newSubRegions;
113 RealmMap newRealms;
114
115 // Circle through results and add them to the realm map
116 if (result)
117 {
118 do
119 {
120 Field* fields = result->Fetch();
121 uint32 realmId = fields[0].GetUInt32();
122 std::string name = fields[1].GetString();
123 std::vector<boost::asio::ip::address> addresses;
124
125 for (std::size_t i = 0; i < 4; ++i)
126 {
127 if (Optional<std::string_view> addressStr = fields[2 + i].GetStringViewOrNull())
128 {
129 for (boost::asio::ip::tcp::endpoint const& endpoint : _resolver->ResolveAll(*addressStr, ""))
130 {
131 boost::asio::ip::address address = endpoint.address();
132 if (advstd::ranges::contains(addresses, address))
133 continue;
134
135 addresses.push_back(std::move(address));
136 }
137 }
138 }
139
140 if (addresses.empty())
141 {
142 TC_LOG_ERROR("realmlist", "Could not resolve any address for realm \"{}\" id {}", name, realmId);
143 continue;
144 }
145
146 uint16 port = fields[6].GetUInt16();
147 uint8 icon = fields[7].GetUInt8();
148 if (icon == REALM_TYPE_FFA_PVP)
149 icon = REALM_TYPE_PVP;
150 if (icon >= MAX_CLIENT_REALM_TYPE)
151 icon = REALM_TYPE_NORMAL;
152 RealmFlags flag = ConvertLegacyRealmFlags(Trinity::Legacy::RealmFlags(fields[8].GetUInt8()));
153 uint8 timezone = fields[9].GetUInt8();
154 uint8 allowedSecurityLevel = fields[10].GetUInt8();
155 RealmPopulationState pop = ConvertLegacyPopulationState(Trinity::Legacy::RealmFlags(fields[8].GetUInt8()), fields[11].GetFloat());
156 uint32 build = fields[12].GetUInt32();
157 uint8 region = fields[13].GetUInt8();
158 uint8 battlegroup = fields[14].GetUInt8();
159
160 Battlenet::RealmHandle id{ region, battlegroup, realmId };
161
162 UpdateRealm(*newRealms.try_emplace(id, std::make_shared<Realm>()).first->second, id, build, name, std::move(addresses), port, icon,
163 flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
164
165 newSubRegions.insert(Battlenet::RealmHandle{ region, battlegroup, 0 }.GetAddressString());
166
167 auto buildAddressesLogText = [&]
168 {
169 std::string text;
170 for (boost::asio::ip::address const& address : newRealms[id]->Addresses)
171 {
172 text += address.to_string();
173 text += ' ';
174 }
175 return text;
176 };
177
178 if (!existingRealms.erase(id))
179 TC_LOG_INFO("realmlist", "Added realm \"{}\" at {}(port {}).", name, buildAddressesLogText(), port);
180 else
181 TC_LOG_DEBUG("realmlist", "Updating realm \"{}\" at {}(port {}).", name, buildAddressesLogText(), port);
182 }
183 while (result->NextRow());
184 }
185
186 for (auto itr = existingRealms.begin(); itr != existingRealms.end(); ++itr)
187 TC_LOG_INFO("realmlist", "Removed realm \"{}\".", itr->second);
188
189 {
190 std::scoped_lock lock(_realmsMutex);
191
192 _subRegions.swap(newSubRegions);
193 _realms.swap(newRealms);
194 _removedRealms.swap(existingRealms);
195
196 if (_currentRealmId)
197 if (std::shared_ptr<Realm> realm = Trinity::Containers::MapGetValuePtr(_realms, *_currentRealmId))
198 _currentRealmId = realm->Id; // fill other fields of realm id
199 }
200
201 if (_updateInterval)
202 {
203 _updateTimer->expires_after(std::chrono::seconds(_updateInterval));
204 _updateTimer->async_wait([this](boost::system::error_code const& error)
205 {
206 if (error)
207 return;
208
209 UpdateRealms();
210 });
211 }
212}
213
214std::shared_ptr<Realm const> RealmList::GetRealm(Battlenet::RealmHandle const& id) const
215{
216 std::shared_lock lock(_realmsMutex);
218}
219
224
229
230std::shared_ptr<Realm const> RealmList::GetCurrentRealm() const
231{
232 if (_currentRealmId)
233 return GetRealm(*_currentRealmId);
234 return nullptr;
235}
236
238{
239 std::shared_lock lock(_realmsMutex);
240 for (std::string const& subRegion : _subRegions)
241 response->add_attribute_value()->set_string_value(subRegion);
242}
243
244void RealmList::FillRealmEntry(Realm const& realm, uint32 clientBuild, AccountTypes accountSecurityLevel, JSON::RealmList::RealmEntry* realmEntry) const
245{
246 realmEntry->set_wowrealmaddress(realm.Id.GetAddress());
247 realmEntry->set_cfgtimezonesid(1);
248 if (accountSecurityLevel >= realm.AllowedSecurityLevel || realm.PopulationLevel == RealmPopulationState::Offline)
250 else
252
253 realmEntry->set_cfgcategoriesid(realm.Timezone);
254
255 JSON::RealmList::ClientVersion* version = realmEntry->mutable_version();
256 if (ClientBuild::Info const* buildInfo = ClientBuild::GetBuildInfo(realm.Build))
257 {
258 version->set_versionmajor(buildInfo->MajorVersion);
259 version->set_versionminor(buildInfo->MinorVersion);
260 version->set_versionrevision(buildInfo->BugfixVersion);
261 version->set_versionbuild(buildInfo->Build);
262 }
263 else
264 {
265 version->set_versionmajor(6);
266 version->set_versionminor(2);
267 version->set_versionrevision(4);
268 version->set_versionbuild(realm.Build);
269 }
270
271 RealmFlags flag = realm.Flags;
272 if (realm.Build != clientBuild)
274
275 realmEntry->set_cfgrealmsid(realm.Id.Realm);
276 realmEntry->set_flags(AsUnderlyingType(flag));
277 realmEntry->set_name(realm.Name);
278 realmEntry->set_cfgconfigsid(realm.GetConfigId());
279 realmEntry->set_cfglanguagesid(1);
280}
281
282std::vector<uint8> RealmList::GetRealmEntryJSON(Battlenet::RealmHandle const& id, uint32 build, AccountTypes accountSecurityLevel) const
283{
284 std::vector<uint8> compressed;
285 if (std::shared_ptr<Realm const> realm = GetRealm(id))
286 {
287 if (realm->PopulationLevel != RealmPopulationState::Offline && realm->Build == build && accountSecurityLevel >= realm->AllowedSecurityLevel)
288 {
290 FillRealmEntry(*realm, build, accountSecurityLevel, &realmEntry);
291
292 std::string json = "JamJSONRealmEntry:" + JSON::Serialize(realmEntry);
293 CompressJson(json, &compressed);
294 }
295 }
296
297 return compressed;
298}
299
300std::vector<uint8> RealmList::GetRealmList(uint32 build, AccountTypes accountSecurityLevel, std::string const& subRegion) const
301{
303 {
304 std::shared_lock lock(_realmsMutex);
305 for (auto const& [_, realm] : _realms)
306 {
307 if (realm->Id.GetSubRegionAddress() != subRegion)
308 continue;
309
311 FillRealmEntry(*realm, build, accountSecurityLevel, state->mutable_update());
312 state->set_deleting(false);
313 }
314
315 for (auto const& [id, _] : _removedRealms)
316 {
317 if (id.GetSubRegionAddress() != subRegion)
318 continue;
319
321 state->set_wowrealmaddress(id.GetAddress());
322 state->set_deleting(true);
323 }
324 }
325
326 std::string json = "JSONRealmListUpdates:" + JSON::Serialize(realmList);
327 std::vector<uint8> compressed;
328 CompressJson(json, &compressed);
329 return compressed;
330}
331
332uint32 RealmList::JoinRealm(uint32 realmAddress, uint32 build, ClientBuild::VariantId const& buildVariant, boost::asio::ip::address const& clientAddress,
333 std::array<uint8, 32> const& clientSecret, LocaleConstant locale, std::string const& os, Minutes timezoneOffset, std::string const& accountName,
334 AccountTypes accountSecurityLevel, bgs::protocol::game_utilities::v1::ClientResponse* response) const
335{
336 if (std::shared_ptr<Realm const> realm = GetRealm(realmAddress))
337 {
338 if (realm->PopulationLevel == RealmPopulationState::Offline || realm->Build != build || accountSecurityLevel < realm->AllowedSecurityLevel)
340
341 boost::asio::ip::address addressForClient = realm->GetAddressForClient(clientAddress);
342
344 JSON::RealmList::RealmIPAddressFamily* addressFamily = serverAddresses.add_families();
345 addressFamily->set_family(addressForClient.is_v6() ? 2 : 1);
346
347 JSON::RealmList::IPAddress* address = addressFamily->add_addresses();
348 address->set_ip(addressForClient.to_string());
349 address->set_port(realm->Port);
350
351 std::string json = "JSONRealmListServerIPAddresses:" + JSON::Serialize(serverAddresses);
352 std::vector<uint8> compressed;
353
354 if (!CompressJson(json, &compressed))
356
357 std::array<uint8, 32> serverSecret = Trinity::Crypto::GetRandomBytes<32>();
358
359 std::array<uint8, 64> keyData;
360 auto keyDestItr = keyData.begin();
361 keyDestItr = std::ranges::copy(clientSecret, keyDestItr).out;
362 keyDestItr = std::ranges::copy(serverSecret, keyDestItr).out;
363
365 stmt->setBinary(0, keyData);
366 stmt->setString(1, clientAddress.to_string());
367 stmt->setUInt32(2, build);
368 stmt->setUInt8(3, locale);
369 stmt->setString(4, os);
370 stmt->setInt16(5, timezoneOffset.count());
371 stmt->setString(6, accountName);
372 LoginDatabase.DirectExecute(stmt);
373
375 joinTicket.set_gameaccount(accountName);
376 joinTicket.set_platform(buildVariant.Platform);
377 joinTicket.set_clientarch(buildVariant.Arch);
378 joinTicket.set_type(buildVariant.Type);
379
380 bgs::protocol::Attribute* attribute = response->add_attribute();
381 attribute->set_name("Param_RealmJoinTicket");
382 attribute->mutable_value()->set_blob_value(JSON::Serialize(joinTicket));
383
384 attribute = response->add_attribute();
385 attribute->set_name("Param_ServerAddresses");
386 attribute->mutable_value()->set_blob_value(compressed.data(), compressed.size());
387
388 attribute = response->add_attribute();
389 attribute->set_name("Param_JoinSecret");
390 attribute->mutable_value()->set_blob_value(serverSecret.data(), serverSecret.size());
391 return ERROR_OK;
392 }
393
395}
@ 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:181
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
#define TC_LOG_INFO(filterType__, message__,...)
Definition Log.h:184
@ 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 set_versionminor(::google::protobuf::uint32 value)
void set_versionmajor(::google::protobuf::uint32 value)
void set_versionrevision(::google::protobuf::uint32 value)
void set_versionbuild(::google::protobuf::uint32 value)
void set_port(::google::protobuf::uint32 value)
void set_ip(const ::std::string &value)
inline ::JSON::RealmList::ClientVersion * mutable_version()
void set_populationstate(::google::protobuf::uint32 value)
void set_wowrealmaddress(::google::protobuf::uint32 value)
void set_cfgrealmsid(::google::protobuf::uint32 value)
void set_name(const ::std::string &value)
void set_flags(::google::protobuf::uint32 value)
void set_cfgcategoriesid(::google::protobuf::uint32 value)
void set_cfgtimezonesid(::google::protobuf::uint32 value)
void set_cfgconfigsid(::google::protobuf::uint32 value)
void set_cfglanguagesid(::google::protobuf::uint32 value)
void set_family(::google::protobuf::uint32 value)
inline ::JSON::RealmList::IPAddress * add_addresses()
void set_gameaccount(const ::std::string &value)
void set_clientarch(::google::protobuf::uint32 value)
void set_type(::google::protobuf::uint32 value)
void set_platform(::google::protobuf::uint32 value)
inline ::JSON::RealmList::RealmIPAddressFamily * add_families()
void set_wowrealmaddress(::google::protobuf::uint32 value)
inline ::JSON::RealmList::RealmEntry * mutable_update()
inline ::JSON::RealmList::RealmListUpdatePart * add_updates()
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:46
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:78
void UpdateRealms()
std::map< Battlenet::RealmHandle, std::shared_ptr< Realm > > RealmMap
Definition RealmList.h:48
std::map< Battlenet::RealmHandle, std::string > _removedRealms
Definition RealmList.h:85
RealmMap _realms
Definition RealmList.h:84
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:83
Battlenet::RealmHandle GetCurrentRealmId() const
std::vector< uint8 > GetRealmEntryJSON(Battlenet::RealmHandle const &id, uint32 build, AccountTypes accountSecurityLevel) const
void SetCurrentRealmId(Battlenet::RealmHandle const &id)
std::vector< uint8 > GetRealmList(uint32 build, AccountTypes accountSecurityLevel, std::string const &subRegion) const
std::unordered_set< std::string > _subRegions
Definition RealmList.h:86
uint32 _updateInterval
Definition RealmList.h:87
Optional< Battlenet::RealmHandle > _currentRealmId
Definition RealmList.h:90
void Initialize(Trinity::Asio::IoContext &ioContext, uint32 updateInterval)
Definition RealmList.cpp:67
uint32 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, bgs::protocol::game_utilities::v1::ClientResponse *response) const
std::shared_mutex _realmsMutex
Definition RealmList.h:83
std::unique_ptr< Trinity::Net::Resolver > _resolver
Definition RealmList.h:89
std::unique_ptr< Trinity::Asio::DeadlineTimer > _updateTimer
Definition RealmList.h:88
static RealmList * Instance()
Definition RealmList.cpp:60
void WriteSubRegions(bgs::protocol::game_utilities::v1::GetAllValuesForAttributeResponse *response) const
inline ::bgs::protocol::Variant * mutable_value()
void set_name(const ::std::string &value)
void set_blob_value(const ::std::string &value)
void set_string_value(const ::std::string &value)
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
struct advstd::ranges::Contains contains
std::string GetAddressString() const
Definition Realm.cpp:53
uint32 GetAddress() const
Definition Realm.h:114
Definition Realm.h:139
uint16 Port
Definition Realm.h:143
RealmFlags Flags
Definition Realm.h:147
AccountTypes AllowedSecurityLevel
Definition Realm.h:149
uint32 GetConfigId() const
Definition Realm.cpp:43
uint8 Timezone
Definition Realm.h:148
uint32 Build
Definition Realm.h:141
std::string Name
Definition Realm.h:144
RealmPopulationState PopulationLevel
Definition Realm.h:150
void SetName(std::string name)
Definition Realm.cpp:25
Battlenet::RealmHandle Id
Definition Realm.h:140
std::vector< boost::asio::ip::address > Addresses
Definition Realm.h:142
uint8 Type
Definition Realm.h:146