aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/bnetserver/Server/Session.cpp6
-rw-r--r--src/server/bnetserver/Server/Session.h2
-rw-r--r--src/server/game/Server/WorldSession.cpp3
-rw-r--r--src/server/game/Server/WorldSession.h5
-rw-r--r--src/server/game/Server/WorldSocket.cpp58
-rw-r--r--src/server/game/Server/WorldSocket.h8
-rw-r--r--src/server/game/Services/WorldserverGameUtilitiesService.cpp5
-rw-r--r--src/server/proto/RealmList/RealmList.pb.cc113
-rw-r--r--src/server/proto/RealmList/RealmList.pb.h254
-rw-r--r--src/server/proto/RealmList/RealmList.proto7
-rw-r--r--src/server/shared/Realm/ClientBuildInfo.cpp72
-rw-r--r--src/server/shared/Realm/ClientBuildInfo.h110
-rw-r--r--src/server/shared/Realm/RealmList.cpp59
-rw-r--r--src/server/shared/Realm/RealmList.h25
14 files changed, 659 insertions, 68 deletions
diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp
index d95e99e34a6..a4f760a905a 100644
--- a/src/server/bnetserver/Server/Session.cpp
+++ b/src/server/bnetserver/Server/Session.cpp
@@ -219,7 +219,7 @@ uint32 Battlenet::Session::HandleLogon(authentication::v1::LogonRequest const* l
return ERROR_BAD_PROGRAM;
}
- if (logonRequest->platform() != "Win" && logonRequest->platform() != "Wn64" && logonRequest->platform() != "Mc64" && logonRequest->platform() != "MacA")
+ if (!ClientBuild::Platform::IsValid(logonRequest->platform()))
{
TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] {} attempted to log in from an unsupported platform (using {})!", GetClientInfo(), logonRequest->platform());
return ERROR_BAD_PLATFORM;
@@ -591,6 +591,8 @@ uint32 Battlenet::Session::GetRealmListTicket(std::unordered_map<std::string, Va
clientInfoOk = true;
memcpy(_clientSecret.data(), data.info().secret().data(), _clientSecret.size());
}
+
+ _clientInfo = { .Platform = data.info().platformtype(), .Arch = data.info().clientarch(), .Type = data.info().type() };
}
}
@@ -691,7 +693,7 @@ uint32 Battlenet::Session::GetRealmList(std::unordered_map<std::string, Variant
uint32 Battlenet::Session::JoinRealm(std::unordered_map<std::string, Variant const*> const& params, game_utilities::v1::ClientResponse* response)
{
if (Variant const* realmAddress = Trinity::Containers::MapGetValuePtr(params, "Param_RealmAddress"))
- return sRealmList->JoinRealm(realmAddress->uint_value(), _build, GetRemoteIpAddress(), _clientSecret, GetLocaleByName(_locale),
+ return sRealmList->JoinRealm(realmAddress->uint_value(), _build, _clientInfo, GetRemoteIpAddress(), _clientSecret, GetLocaleByName(_locale),
_os, _timezoneOffset, _gameAccountInfo->Name, _gameAccountInfo->SecurityLevel, response);
return ERROR_WOW_SERVICES_INVALID_JOIN_TICKET;
diff --git a/src/server/bnetserver/Server/Session.h b/src/server/bnetserver/Server/Session.h
index 33a058f940c..8a8b1f0a15b 100644
--- a/src/server/bnetserver/Server/Session.h
+++ b/src/server/bnetserver/Server/Session.h
@@ -19,6 +19,7 @@
#define Session_h__
#include "AsyncCallbackProcessor.h"
+#include "ClientBuildInfo.h"
#include "Duration.h"
#include "QueryResult.h"
#include "Realm.h"
@@ -173,6 +174,7 @@ namespace Battlenet
std::string _locale;
std::string _os;
uint32 _build;
+ ClientBuild::VariantId _clientInfo;
Minutes _timezoneOffset;
std::string _ipCountry;
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index ba3bb98d958..1d3e5eb4cd7 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -106,7 +106,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time,
- std::string os, Minutes timezoneOffset, uint32 build, LocaleConstant locale, uint32 recruiter, bool isARecruiter):
+ std::string os, Minutes timezoneOffset, uint32 build, ClientBuild::VariantId clientBuildVariant, LocaleConstant locale, uint32 recruiter, bool isARecruiter):
m_muteTime(mute_time),
m_timeOutTime(0),
AntiDOS(this),
@@ -120,6 +120,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun
m_expansion(std::min<uint8>(expansion, sWorld->getIntConfig(CONFIG_EXPANSION))),
_os(std::move(os)),
_clientBuild(build),
+ _clientBuildVariant(clientBuildVariant),
_battlenetRequestToken(0),
_logoutTime(0),
m_inQueue(false),
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 49cc7721504..c7a405a9a10 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -25,6 +25,7 @@
#include "Common.h"
#include "AsyncCallbackProcessor.h"
#include "AuthDefines.h"
+#include "ClientBuildInfo.h"
#include "DatabaseEnvFwd.h"
#include "Duration.h"
#include "IteratorPair.h"
@@ -938,7 +939,7 @@ class TC_GAME_API WorldSession
{
public:
WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time,
- std::string os, Minutes timezoneOffset, uint32 build, LocaleConstant locale, uint32 recruiter, bool isARecruiter);
+ std::string os, Minutes timezoneOffset, uint32 build, ClientBuild::VariantId clientBuildVariant, LocaleConstant locale, uint32 recruiter, bool isARecruiter);
~WorldSession();
bool PlayerLoading() const { return !m_playerLoading.IsEmpty(); }
@@ -988,6 +989,7 @@ class TC_GAME_API WorldSession
uint8 GetExpansion() const { return m_expansion; }
std::string const& GetOS() const { return _os; }
uint32 GetClientBuild() const { return _clientBuild; }
+ ClientBuild::VariantId const& GetClientBuildVariant() const { return _clientBuildVariant; }
bool CanAccessAlliedRaces() const;
Warden* GetWarden() { return _warden.get(); }
@@ -1886,6 +1888,7 @@ class TC_GAME_API WorldSession
uint8 m_expansion;
std::string _os;
uint32 _clientBuild;
+ ClientBuild::VariantId _clientBuildVariant;
std::array<uint8, 32> _realmListSecret;
std::unordered_map<uint32 /*realmAddress*/, uint8> _realmCharacterCounts;
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index f583c488367..1fde981ad53 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -27,8 +27,10 @@
#include "HMAC.h"
#include "IPLocation.h"
#include "PacketLog.h"
+#include "ProtobufJSON.h"
#include "RealmList.h"
#include "RBAC.h"
+#include "RealmList.pb.h"
#include "ScriptMgr.h"
#include "SessionKeyGenerator.h"
#include "World.h"
@@ -671,18 +673,27 @@ struct AccountInfo
void WorldSocket::HandleAuthSession(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession)
{
+ std::shared_ptr<JSON::RealmList::RealmJoinTicket> joinTicket = std::make_shared<JSON::RealmList::RealmJoinTicket>();
+ if (!JSON::Deserialize(authSession->RealmJoinTicket, joinTicket.get()))
+ {
+ SendAuthResponseError(ERROR_WOW_SERVICES_INVALID_JOIN_TICKET);
+ DelayedCloseSocket();
+ return;
+ }
+
// Get the account information from the auth database
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME);
stmt->setInt32(0, int32(sRealmList->GetCurrentRealmId().Realm));
- stmt->setString(1, authSession->RealmJoinTicket);
+ stmt->setString(1, joinTicket->gameaccount());
- _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback([this, authSession = std::move(authSession)](PreparedQueryResult result) mutable
+ _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback([this, authSession = std::move(authSession), joinTicket = std::move(joinTicket)](PreparedQueryResult result) mutable
{
- HandleAuthSessionCallback(std::move(authSession), std::move(result));
+ HandleAuthSessionCallback(std::move(authSession), std::move(joinTicket), std::move(result));
}));
}
-void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession, PreparedQueryResult result)
+void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession,
+ std::shared_ptr<JSON::RealmList::RealmJoinTicket> joinTicket, PreparedQueryResult result)
{
// Stop if the account is not found
if (!result)
@@ -695,11 +706,23 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::
AccountInfo account(result->Fetch());
- RealmBuildInfo const* buildInfo = sRealmList->GetBuildInfo(account.Game.Build);
+ ClientBuild::Info const* buildInfo = sRealmList->GetBuildInfo(account.Game.Build);
if (!buildInfo)
{
SendAuthResponseError(ERROR_BAD_VERSION);
- TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Missing auth seed for realm build {} ({}).", account.Game.Build, GetRemoteIpAddress().to_string());
+ TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Missing client build info for build {} ({}).", account.Game.Build, GetRemoteIpAddress().to_string());
+ DelayedCloseSocket();
+ return;
+ }
+
+ ClientBuild::VariantId buildVariant = { .Platform = joinTicket->platform(), .Arch = joinTicket->clientarch(), .Type = joinTicket->type() };
+ auto clientBuildAuthKey = std::ranges::find(buildInfo->AuthKeys, buildVariant, &ClientBuild::AuthKey::Variant);
+ if (clientBuildAuthKey == buildInfo->AuthKeys.end())
+ {
+ SendAuthResponseError(ERROR_BAD_VERSION);
+ TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Missing client build auth key for build {} variant {}-{}-{} ({}).", account.Game.Build,
+ ClientBuild::ToCharArray(buildVariant.Platform).data(), ClientBuild::ToCharArray(buildVariant.Arch).data(),
+ ClientBuild::ToCharArray(buildVariant.Type).data(), GetRemoteIpAddress().to_string());
DelayedCloseSocket();
return;
}
@@ -709,13 +732,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::
Trinity::Crypto::SHA256 digestKeyHash;
digestKeyHash.UpdateData(account.Game.KeyData.data(), account.Game.KeyData.size());
- if (account.Game.OS == "Wn64")
- digestKeyHash.UpdateData(buildInfo->Win64AuthSeed.data(), buildInfo->Win64AuthSeed.size());
- else if (account.Game.OS == "Mc64")
- digestKeyHash.UpdateData(buildInfo->Mac64AuthSeed.data(), buildInfo->Mac64AuthSeed.size());
- else if (account.Game.OS == "MacA")
- digestKeyHash.UpdateData(buildInfo->MacArmAuthSeed.data(), buildInfo->MacArmAuthSeed.size());
-
+ digestKeyHash.UpdateData(clientBuildAuthKey->Key.data(), clientBuildAuthKey->Key.size());
digestKeyHash.Finalize();
Trinity::Crypto::HMAC_SHA256 hmac(digestKeyHash.GetDigest());
@@ -728,7 +745,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::
if (memcmp(hmac.GetDigest().data(), authSession->Digest.data(), authSession->Digest.size()) != 0)
{
SendAuthResponseError(ERROR_DENIED);
- TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: {} ('{}') address: {}", account.Game.Id, authSession->RealmJoinTicket, address);
+ TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: {} ('{}') address: {}", account.Game.Id, joinTicket->gameaccount(), address);
DelayedCloseSocket();
return;
}
@@ -762,7 +779,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::
// As we don't know if attempted login process by ip works, we update last_attempt_ip right away
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP);
stmt->setString(0, address);
- stmt->setString(1, authSession->RealmJoinTicket);
+ stmt->setString(1, joinTicket->gameaccount());
LoginDatabase.Execute(stmt);
// This also allows to check for possible "hack" attempts on account
}
@@ -792,7 +809,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::
// Must be done before WorldSession is created
bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED);
- if (wardenActive && account.Game.OS != "Win" && account.Game.OS != "Wn64" && account.Game.OS != "Mc64" && account.Game.OS != "MacA")
+ if (wardenActive && !ClientBuild::Platform::IsValid(account.Game.OS))
{
SendAuthResponseError(ERROR_DENIED);
TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client {} attempted to log in using invalid client OS ({}).", address, account.Game.OS);
@@ -862,7 +879,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::
return;
}
- TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '{}' authenticated successfully from {}.", authSession->RealmJoinTicket, address);
+ TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '{}' authenticated successfully from {}.", joinTicket->gameaccount(), address);
if (sWorld->getBoolConfig(CONFIG_ALLOW_LOGGING_IP_ADDRESSES_IN_DATABASE))
{
@@ -870,7 +887,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP);
stmt->setString(0, address);
- stmt->setString(1, authSession->RealmJoinTicket);
+ stmt->setString(1, joinTicket->gameaccount());
LoginDatabase.Execute(stmt);
}
@@ -879,8 +896,9 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::
sScriptMgr->OnAccountLogin(account.Game.Id);
_authed = true;
- _worldSession = new WorldSession(account.Game.Id, std::move(authSession->RealmJoinTicket), account.BattleNet.Id, shared_from_this(), account.Game.Security,
- account.Game.Expansion, mutetime, account.Game.OS, account.Game.TimezoneOffset, account.Game.Build, account.Game.Locale, account.Game.Recruiter, account.Game.IsRectuiter);
+ _worldSession = new WorldSession(account.Game.Id, std::move(*joinTicket->mutable_gameaccount()), account.BattleNet.Id, shared_from_this(), account.Game.Security,
+ account.Game.Expansion, mutetime, account.Game.OS, account.Game.TimezoneOffset, account.Game.Build, buildVariant, account.Game.Locale,
+ account.Game.Recruiter, account.Game.IsRectuiter);
// Initialize Warden system only if it is enabled by config
if (wardenActive)
diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h
index 2cf288bbd7b..35f58161b53 100644
--- a/src/server/game/Server/WorldSocket.h
+++ b/src/server/game/Server/WorldSocket.h
@@ -30,6 +30,11 @@
#include <boost/asio/ip/tcp.hpp>
#include <mutex>
+namespace JSON::RealmList
+{
+class RealmJoinTicket;
+}
+
typedef struct z_stream_s z_stream;
class EncryptablePacket;
class WorldPacket;
@@ -140,7 +145,8 @@ private:
void HandleSendAuthSession();
void HandleAuthSession(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession);
- void HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession, PreparedQueryResult result);
+ void HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthSession> authSession,
+ std::shared_ptr<JSON::RealmList::RealmJoinTicket> joinTicket, PreparedQueryResult result);
void HandleAuthContinuedSession(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession);
void HandleAuthContinuedSessionCallback(std::shared_ptr<WorldPackets::Auth::AuthContinuedSession> authSession, PreparedQueryResult result);
void LoadSessionPermissionsCallback(PreparedQueryResult result);
diff --git a/src/server/game/Services/WorldserverGameUtilitiesService.cpp b/src/server/game/Services/WorldserverGameUtilitiesService.cpp
index 083b827c19e..20af196fe7b 100644
--- a/src/server/game/Services/WorldserverGameUtilitiesService.cpp
+++ b/src/server/game/Services/WorldserverGameUtilitiesService.cpp
@@ -118,8 +118,9 @@ uint32 Battlenet::Services::GameUtilitiesService::HandleRealmListRequest(std::un
uint32 Battlenet::Services::GameUtilitiesService::HandleRealmJoinRequest(std::unordered_map<std::string, Variant const*> const& params, game_utilities::v1::ClientResponse* response)
{
if (Variant const* realmAddress = Trinity::Containers::MapGetValuePtr(params, "Param_RealmAddress"))
- return sRealmList->JoinRealm(uint32(realmAddress->uint_value()), _session->GetClientBuild(), Trinity::Net::make_address(_session->GetRemoteAddress()), _session->GetRealmListSecret(),
- _session->GetSessionDbcLocale(), _session->GetOS(), _session->GetTimezoneOffset(), _session->GetAccountName(), _session->GetSecurity(), response);
+ return sRealmList->JoinRealm(uint32(realmAddress->uint_value()), _session->GetClientBuild(), _session->GetClientBuildVariant(),
+ Trinity::Net::make_address(_session->GetRemoteAddress()), _session->GetRealmListSecret(), _session->GetSessionDbcLocale(),
+ _session->GetOS(), _session->GetTimezoneOffset(), _session->GetAccountName(), _session->GetSecurity(), response);
return ERROR_WOW_SERVICES_INVALID_JOIN_TICKET;
}
diff --git a/src/server/proto/RealmList/RealmList.pb.cc b/src/server/proto/RealmList/RealmList.pb.cc
index 44c27eadcd8..41050ae0824 100644
--- a/src/server/proto/RealmList/RealmList.pb.cc
+++ b/src/server/proto/RealmList/RealmList.pb.cc
@@ -60,6 +60,9 @@ const ::google::protobuf::internal::GeneratedMessageReflection*
const ::google::protobuf::Descriptor* RealmListServerIPAddresses_descriptor_ = NULL;
const ::google::protobuf::internal::GeneratedMessageReflection*
RealmListServerIPAddresses_reflection_ = NULL;
+const ::google::protobuf::Descriptor* RealmJoinTicket_descriptor_ = NULL;
+const ::google::protobuf::internal::GeneratedMessageReflection*
+ RealmJoinTicket_reflection_ = NULL;
} // namespace
@@ -296,6 +299,24 @@ void protobuf_AssignDesc_RealmList_2eproto() {
::google::protobuf::DescriptorPool::generated_pool(),
::google::protobuf::MessageFactory::generated_factory(),
sizeof(RealmListServerIPAddresses));
+ RealmJoinTicket_descriptor_ = file->message_type(13);
+ static const int RealmJoinTicket_offsets_[4] = {
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RealmJoinTicket, gameaccount_),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RealmJoinTicket, platform_),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RealmJoinTicket, type_),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RealmJoinTicket, clientarch_),
+ };
+ RealmJoinTicket_reflection_ =
+ new ::google::protobuf::internal::GeneratedMessageReflection(
+ RealmJoinTicket_descriptor_,
+ RealmJoinTicket::default_instance_,
+ RealmJoinTicket_offsets_,
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RealmJoinTicket, _has_bits_[0]),
+ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(RealmJoinTicket, _unknown_fields_),
+ -1,
+ ::google::protobuf::DescriptorPool::generated_pool(),
+ ::google::protobuf::MessageFactory::generated_factory(),
+ sizeof(RealmJoinTicket));
}
namespace {
@@ -334,6 +355,8 @@ void protobuf_RegisterTypes(const ::std::string&) {
RealmIPAddressFamily_descriptor_, &RealmIPAddressFamily::default_instance());
::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
RealmListServerIPAddresses_descriptor_, &RealmListServerIPAddresses::default_instance());
+ ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+ RealmJoinTicket_descriptor_, &RealmJoinTicket::default_instance());
}
} // namespace
@@ -365,6 +388,8 @@ void protobuf_ShutdownFile_RealmList_2eproto() {
delete RealmIPAddressFamily_reflection_;
delete RealmListServerIPAddresses::default_instance_;
delete RealmListServerIPAddresses_reflection_;
+ delete RealmJoinTicket::default_instance_;
+ delete RealmJoinTicket_reflection_;
}
void protobuf_AddDesc_RealmList_2eproto() {
@@ -411,7 +436,9 @@ void protobuf_AddDesc_RealmList_2eproto() {
"mily\030\001 \002(\r\022,\n\taddresses\030\002 \003(\0132\031.JSON.Rea"
"lmList.IPAddress\"T\n\032RealmListServerIPAdd"
"resses\0226\n\010families\030\001 \003(\0132$.JSON.RealmLis"
- "t.RealmIPAddressFamilyB\002H\002", 1506);
+ "t.RealmIPAddressFamily\"Z\n\017RealmJoinTicke"
+ "t\022\023\n\013gameAccount\030\001 \002(\t\022\020\n\010platform\030\002 \002(\007"
+ "\022\014\n\004type\030\003 \002(\007\022\022\n\nclientArch\030\004 \002(\007B\002H\002", 1598);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"RealmList.proto", &protobuf_RegisterTypes);
RealmListTicketIdentity::default_instance_ = new RealmListTicketIdentity();
@@ -427,6 +454,7 @@ void protobuf_AddDesc_RealmList_2eproto() {
IPAddress::default_instance_ = new IPAddress();
RealmIPAddressFamily::default_instance_ = new RealmIPAddressFamily();
RealmListServerIPAddresses::default_instance_ = new RealmListServerIPAddresses();
+ RealmJoinTicket::default_instance_ = new RealmJoinTicket();
RealmListTicketIdentity::default_instance_->InitAsDefaultInstance();
ClientVersion::default_instance_->InitAsDefaultInstance();
ClientInformation::default_instance_->InitAsDefaultInstance();
@@ -440,6 +468,7 @@ void protobuf_AddDesc_RealmList_2eproto() {
IPAddress::default_instance_->InitAsDefaultInstance();
RealmIPAddressFamily::default_instance_->InitAsDefaultInstance();
RealmListServerIPAddresses::default_instance_->InitAsDefaultInstance();
+ RealmJoinTicket::default_instance_->InitAsDefaultInstance();
::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_RealmList_2eproto);
}
@@ -1474,6 +1503,88 @@ void RealmListServerIPAddresses::Swap(RealmListServerIPAddresses* other) {
return metadata;
}
+// ===================================================================
+
+#ifndef _MSC_VER
+const int RealmJoinTicket::kGameAccountFieldNumber;
+const int RealmJoinTicket::kPlatformFieldNumber;
+const int RealmJoinTicket::kTypeFieldNumber;
+const int RealmJoinTicket::kClientArchFieldNumber;
+#endif // !_MSC_VER
+
+RealmJoinTicket::RealmJoinTicket()
+ : ::google::protobuf::Message() {
+ SharedCtor();
+ // @@protoc_insertion_point(constructor:JSON.RealmList.RealmJoinTicket)
+}
+
+void RealmJoinTicket::InitAsDefaultInstance() {
+}
+
+RealmJoinTicket::RealmJoinTicket(const RealmJoinTicket& from)
+ : ::google::protobuf::Message() {
+ SharedCtor();
+ MergeFrom(from);
+ // @@protoc_insertion_point(copy_constructor:JSON.RealmList.RealmJoinTicket)
+}
+
+void RealmJoinTicket::SharedCtor() {
+ ::google::protobuf::internal::GetEmptyString();
+ _cached_size_ = 0;
+ gameaccount_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ platform_ = 0u;
+ type_ = 0u;
+ clientarch_ = 0u;
+ ::memset(_has_bits_, 0, sizeof(_has_bits_));
+}
+
+RealmJoinTicket::~RealmJoinTicket() {
+ // @@protoc_insertion_point(destructor:JSON.RealmList.RealmJoinTicket)
+ SharedDtor();
+}
+
+void RealmJoinTicket::SharedDtor() {
+ if (gameaccount_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete gameaccount_;
+ }
+ if (this != default_instance_) {
+ }
+}
+
+void RealmJoinTicket::SetCachedSize(int size) const {
+ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+ _cached_size_ = size;
+ GOOGLE_SAFE_CONCURRENT_WRITES_END();
+}
+const ::google::protobuf::Descriptor* RealmJoinTicket::descriptor() {
+ protobuf_AssignDescriptorsOnce();
+ return RealmJoinTicket_descriptor_;
+}
+
+const RealmJoinTicket& RealmJoinTicket::default_instance() {
+ if (default_instance_ == NULL) protobuf_AddDesc_RealmList_2eproto();
+ return *default_instance_;
+}
+
+RealmJoinTicket* RealmJoinTicket::default_instance_ = NULL;
+
+RealmJoinTicket* RealmJoinTicket::New() const {
+ return new RealmJoinTicket;
+}
+
+void RealmJoinTicket::Swap(RealmJoinTicket* other) {
+ if (other != this) {
+ GetReflection()->Swap(this, other);}
+}
+
+::google::protobuf::Metadata RealmJoinTicket::GetMetadata() const {
+ protobuf_AssignDescriptorsOnce();
+ ::google::protobuf::Metadata metadata;
+ metadata.descriptor = RealmJoinTicket_descriptor_;
+ metadata.reflection = RealmJoinTicket_reflection_;
+ return metadata;
+}
+
// @@protoc_insertion_point(namespace_scope)
} // namespace RealmList
diff --git a/src/server/proto/RealmList/RealmList.pb.h b/src/server/proto/RealmList/RealmList.pb.h
index 56e61f22ae1..44cc4520d3b 100644
--- a/src/server/proto/RealmList/RealmList.pb.h
+++ b/src/server/proto/RealmList/RealmList.pb.h
@@ -48,6 +48,7 @@ class RealmListUpdates;
class IPAddress;
class RealmIPAddressFamily;
class RealmListServerIPAddresses;
+class RealmJoinTicket;
// ===================================================================
@@ -1277,6 +1278,107 @@ class TC_PROTO_API RealmListServerIPAddresses : public ::google::protobuf::Messa
void InitAsDefaultInstance();
static RealmListServerIPAddresses* default_instance_;
};
+// -------------------------------------------------------------------
+
+class TC_PROTO_API RealmJoinTicket : public ::google::protobuf::Message {
+ public:
+ RealmJoinTicket();
+ virtual ~RealmJoinTicket();
+
+ RealmJoinTicket(const RealmJoinTicket& from);
+
+ inline RealmJoinTicket& operator=(const RealmJoinTicket& from) {
+ CopyFrom(from);
+ return *this;
+ }
+
+ inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+ return _unknown_fields_;
+ }
+
+ inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+ return &_unknown_fields_;
+ }
+
+ static const ::google::protobuf::Descriptor* descriptor();
+ static const RealmJoinTicket& default_instance();
+
+ void Swap(RealmJoinTicket* other);
+
+ // implements Message ----------------------------------------------
+
+ RealmJoinTicket* New() const;
+ int GetCachedSize() const { return _cached_size_; }
+ private:
+ void SharedCtor();
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ public:
+ ::google::protobuf::Metadata GetMetadata() const;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ // required string gameAccount = 1;
+ inline bool has_gameaccount() const;
+ inline void clear_gameaccount();
+ static const int kGameAccountFieldNumber = 1;
+ inline const ::std::string& gameaccount() const;
+ inline void set_gameaccount(const ::std::string& value);
+ inline void set_gameaccount(const char* value);
+ inline void set_gameaccount(const char* value, size_t size);
+ inline ::std::string* mutable_gameaccount();
+ inline ::std::string* release_gameaccount();
+ inline void set_allocated_gameaccount(::std::string* gameaccount);
+
+ // required fixed32 platform = 2;
+ inline bool has_platform() const;
+ inline void clear_platform();
+ static const int kPlatformFieldNumber = 2;
+ inline ::google::protobuf::uint32 platform() const;
+ inline void set_platform(::google::protobuf::uint32 value);
+
+ // required fixed32 type = 3;
+ inline bool has_type() const;
+ inline void clear_type();
+ static const int kTypeFieldNumber = 3;
+ inline ::google::protobuf::uint32 type() const;
+ inline void set_type(::google::protobuf::uint32 value);
+
+ // required fixed32 clientArch = 4;
+ inline bool has_clientarch() const;
+ inline void clear_clientarch();
+ static const int kClientArchFieldNumber = 4;
+ inline ::google::protobuf::uint32 clientarch() const;
+ inline void set_clientarch(::google::protobuf::uint32 value);
+
+ // @@protoc_insertion_point(class_scope:JSON.RealmList.RealmJoinTicket)
+ private:
+ inline void set_has_gameaccount();
+ inline void clear_has_gameaccount();
+ inline void set_has_platform();
+ inline void clear_has_platform();
+ inline void set_has_type();
+ inline void clear_has_type();
+ inline void set_has_clientarch();
+ inline void clear_has_clientarch();
+
+ ::google::protobuf::UnknownFieldSet _unknown_fields_;
+
+ ::google::protobuf::uint32 _has_bits_[1];
+ mutable int _cached_size_;
+ ::std::string* gameaccount_;
+ ::google::protobuf::uint32 platform_;
+ ::google::protobuf::uint32 type_;
+ ::google::protobuf::uint32 clientarch_;
+ friend void TC_PROTO_API protobuf_AddDesc_RealmList_2eproto();
+ friend void protobuf_AssignDesc_RealmList_2eproto();
+ friend void protobuf_ShutdownFile_RealmList_2eproto();
+
+ void InitAsDefaultInstance();
+ static RealmJoinTicket* default_instance_;
+};
// ===================================================================
// ===================================================================
@@ -2817,6 +2919,158 @@ RealmListServerIPAddresses::mutable_families() {
return &families_;
}
+// -------------------------------------------------------------------
+
+// RealmJoinTicket
+
+// required string gameAccount = 1;
+inline bool RealmJoinTicket::has_gameaccount() const {
+ return (_has_bits_[0] & 0x00000001u) != 0;
+}
+inline void RealmJoinTicket::set_has_gameaccount() {
+ _has_bits_[0] |= 0x00000001u;
+}
+inline void RealmJoinTicket::clear_has_gameaccount() {
+ _has_bits_[0] &= ~0x00000001u;
+}
+inline void RealmJoinTicket::clear_gameaccount() {
+ if (gameaccount_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ gameaccount_->clear();
+ }
+ clear_has_gameaccount();
+}
+inline const ::std::string& RealmJoinTicket::gameaccount() const {
+ // @@protoc_insertion_point(field_get:JSON.RealmList.RealmJoinTicket.gameAccount)
+ return *gameaccount_;
+}
+inline void RealmJoinTicket::set_gameaccount(const ::std::string& value) {
+ set_has_gameaccount();
+ if (gameaccount_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ gameaccount_ = new ::std::string;
+ }
+ gameaccount_->assign(value);
+ // @@protoc_insertion_point(field_set:JSON.RealmList.RealmJoinTicket.gameAccount)
+}
+inline void RealmJoinTicket::set_gameaccount(const char* value) {
+ set_has_gameaccount();
+ if (gameaccount_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ gameaccount_ = new ::std::string;
+ }
+ gameaccount_->assign(value);
+ // @@protoc_insertion_point(field_set_char:JSON.RealmList.RealmJoinTicket.gameAccount)
+}
+inline void RealmJoinTicket::set_gameaccount(const char* value, size_t size) {
+ set_has_gameaccount();
+ if (gameaccount_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ gameaccount_ = new ::std::string;
+ }
+ gameaccount_->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:JSON.RealmList.RealmJoinTicket.gameAccount)
+}
+inline ::std::string* RealmJoinTicket::mutable_gameaccount() {
+ set_has_gameaccount();
+ if (gameaccount_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ gameaccount_ = new ::std::string;
+ }
+ // @@protoc_insertion_point(field_mutable:JSON.RealmList.RealmJoinTicket.gameAccount)
+ return gameaccount_;
+}
+inline ::std::string* RealmJoinTicket::release_gameaccount() {
+ clear_has_gameaccount();
+ if (gameaccount_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ return NULL;
+ } else {
+ ::std::string* temp = gameaccount_;
+ gameaccount_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ return temp;
+ }
+}
+inline void RealmJoinTicket::set_allocated_gameaccount(::std::string* gameaccount) {
+ if (gameaccount_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
+ delete gameaccount_;
+ }
+ if (gameaccount) {
+ set_has_gameaccount();
+ gameaccount_ = gameaccount;
+ } else {
+ clear_has_gameaccount();
+ gameaccount_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
+ }
+ // @@protoc_insertion_point(field_set_allocated:JSON.RealmList.RealmJoinTicket.gameAccount)
+}
+
+// required fixed32 platform = 2;
+inline bool RealmJoinTicket::has_platform() const {
+ return (_has_bits_[0] & 0x00000002u) != 0;
+}
+inline void RealmJoinTicket::set_has_platform() {
+ _has_bits_[0] |= 0x00000002u;
+}
+inline void RealmJoinTicket::clear_has_platform() {
+ _has_bits_[0] &= ~0x00000002u;
+}
+inline void RealmJoinTicket::clear_platform() {
+ platform_ = 0u;
+ clear_has_platform();
+}
+inline ::google::protobuf::uint32 RealmJoinTicket::platform() const {
+ // @@protoc_insertion_point(field_get:JSON.RealmList.RealmJoinTicket.platform)
+ return platform_;
+}
+inline void RealmJoinTicket::set_platform(::google::protobuf::uint32 value) {
+ set_has_platform();
+ platform_ = value;
+ // @@protoc_insertion_point(field_set:JSON.RealmList.RealmJoinTicket.platform)
+}
+
+// required fixed32 type = 3;
+inline bool RealmJoinTicket::has_type() const {
+ return (_has_bits_[0] & 0x00000004u) != 0;
+}
+inline void RealmJoinTicket::set_has_type() {
+ _has_bits_[0] |= 0x00000004u;
+}
+inline void RealmJoinTicket::clear_has_type() {
+ _has_bits_[0] &= ~0x00000004u;
+}
+inline void RealmJoinTicket::clear_type() {
+ type_ = 0u;
+ clear_has_type();
+}
+inline ::google::protobuf::uint32 RealmJoinTicket::type() const {
+ // @@protoc_insertion_point(field_get:JSON.RealmList.RealmJoinTicket.type)
+ return type_;
+}
+inline void RealmJoinTicket::set_type(::google::protobuf::uint32 value) {
+ set_has_type();
+ type_ = value;
+ // @@protoc_insertion_point(field_set:JSON.RealmList.RealmJoinTicket.type)
+}
+
+// required fixed32 clientArch = 4;
+inline bool RealmJoinTicket::has_clientarch() const {
+ return (_has_bits_[0] & 0x00000008u) != 0;
+}
+inline void RealmJoinTicket::set_has_clientarch() {
+ _has_bits_[0] |= 0x00000008u;
+}
+inline void RealmJoinTicket::clear_has_clientarch() {
+ _has_bits_[0] &= ~0x00000008u;
+}
+inline void RealmJoinTicket::clear_clientarch() {
+ clientarch_ = 0u;
+ clear_has_clientarch();
+}
+inline ::google::protobuf::uint32 RealmJoinTicket::clientarch() const {
+ // @@protoc_insertion_point(field_get:JSON.RealmList.RealmJoinTicket.clientArch)
+ return clientarch_;
+}
+inline void RealmJoinTicket::set_clientarch(::google::protobuf::uint32 value) {
+ set_has_clientarch();
+ clientarch_ = value;
+ // @@protoc_insertion_point(field_set:JSON.RealmList.RealmJoinTicket.clientArch)
+}
+
// @@protoc_insertion_point(namespace_scope)
} // namespace RealmList
diff --git a/src/server/proto/RealmList/RealmList.proto b/src/server/proto/RealmList/RealmList.proto
index aa184d46f8c..7fca92b8413 100644
--- a/src/server/proto/RealmList/RealmList.proto
+++ b/src/server/proto/RealmList/RealmList.proto
@@ -87,3 +87,10 @@ message RealmIPAddressFamily {
message RealmListServerIPAddresses {
repeated RealmIPAddressFamily families = 1;
}
+
+message RealmJoinTicket {
+ required string gameAccount = 1;
+ required fixed32 platform = 2;
+ required fixed32 type = 3;
+ required fixed32 clientArch = 4;
+}
diff --git a/src/server/shared/Realm/ClientBuildInfo.cpp b/src/server/shared/Realm/ClientBuildInfo.cpp
new file mode 100644
index 00000000000..12a4b9ec5a0
--- /dev/null
+++ b/src/server/shared/Realm/ClientBuildInfo.cpp
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ClientBuildInfo.h"
+#include <algorithm>
+#include <cctype>
+
+std::array<char, 5> ClientBuild::ToCharArray(uint32 value)
+{
+ auto normalize = [](uint8 c) -> char
+ {
+ if (!c || std::isprint(c))
+ return char(c);
+ return ' ';
+ };
+
+ std::array<char, 5> chars = { char((value >> 24) & 0xFF), char((value >> 16) & 0xFF), char((value >> 8) & 0xFF), char(value & 0xFF), '\0' };
+
+ auto firstNonZero = std::ranges::find_if(chars, [](char c) { return c != '\0'; });
+ if (firstNonZero != chars.end())
+ {
+ // move leading zeros to end
+ std::ranges::rotate(chars, firstNonZero);
+
+ // ensure we only have printable characters remaining
+ std::ranges::transform(chars, chars.begin(), normalize);
+ }
+
+ return chars;
+}
+
+bool ClientBuild::Platform::IsValid(std::string_view platform)
+{
+ if (platform.length() > sizeof(uint32))
+ return false;
+
+ uint32 platformInt = 0;
+ for (uint8 c : platform)
+ {
+ platformInt <<= 8;
+ platformInt |= c;
+ }
+
+ switch (platformInt)
+ {
+ case Win_x86:
+ case Win_x64:
+ case Win_arm64:
+ case Mac_x86:
+ case Mac_x64:
+ case Mac_arm64:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
diff --git a/src/server/shared/Realm/ClientBuildInfo.h b/src/server/shared/Realm/ClientBuildInfo.h
new file mode 100644
index 00000000000..e52b585b5b0
--- /dev/null
+++ b/src/server/shared/Realm/ClientBuildInfo.h
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITYCORE_CLIENT_BUILD_INFO_H
+#define TRINITYCORE_CLIENT_BUILD_INFO_H
+
+#include "Define.h"
+#include <array>
+#include <string_view>
+#include <vector>
+
+namespace ClientBuild
+{
+consteval uint32 operator""_fourcc(char const* chars, std::size_t length)
+{
+ if (length > sizeof(uint32))
+ throw "Text can only be max 4 characters long";
+
+ uint32 uintValue = 0;
+ for (uint8 c : std::string_view(chars, length))
+ {
+ uintValue <<= 8;
+ uintValue |= c;
+ }
+
+ return uintValue;
+}
+
+TC_SHARED_API std::array<char, 5> ToCharArray(uint32 value);
+
+namespace Platform
+{
+ inline constexpr uint32 Win_x86 = "Win"_fourcc;
+ inline constexpr uint32 Win_x64 = "Wn64"_fourcc;
+ inline constexpr uint32 Win_arm64 = "WinA"_fourcc;
+ inline constexpr uint32 Mac_x86 = "Mac"_fourcc;
+ inline constexpr uint32 Mac_x64 = "Mc64"_fourcc;
+ inline constexpr uint32 Mac_arm64 = "MacA"_fourcc;
+
+ TC_SHARED_API bool IsValid(std::string_view platform);
+}
+
+namespace PlatformType
+{
+ inline constexpr uint32 Windows = "Win"_fourcc;
+ inline constexpr uint32 macOS = "Mac"_fourcc;
+}
+
+namespace Arch
+{
+ inline constexpr uint32 x86 = "x86"_fourcc;
+ inline constexpr uint32 x64 = "x64"_fourcc;
+ inline constexpr uint32 Arm32 = "A32"_fourcc;
+ inline constexpr uint32 Arm64 = "A64"_fourcc;
+ inline constexpr uint32 WA32 = "WA32"_fourcc;
+}
+
+namespace Type
+{
+ inline constexpr uint32 Retail = "WoW"_fourcc;
+ inline constexpr uint32 RetailChina = "WoWC"_fourcc;
+ inline constexpr uint32 Beta = "WoWB"_fourcc;
+ inline constexpr uint32 BetaRelease = "WoWE"_fourcc;
+ inline constexpr uint32 Ptr = "WoWT"_fourcc;
+ inline constexpr uint32 PtrRelease = "WoWR"_fourcc;
+}
+
+struct VariantId
+{
+ uint32 Platform;
+ uint32 Arch;
+ uint32 Type;
+
+ friend bool operator==(VariantId const& left, VariantId const& right) = default;
+};
+
+struct AuthKey
+{
+ static constexpr std::size_t Size = 16;
+
+ VariantId Variant;
+ std::array<uint8, Size> Key;
+};
+
+struct Info
+{
+ uint32 Build;
+ uint32 MajorVersion;
+ uint32 MinorVersion;
+ uint32 BugfixVersion;
+ std::array<char, 4> HotfixVersion;
+ std::vector<AuthKey> AuthKeys;
+};
+}
+
+#endif // TRINITYCORE_CLIENT_BUILD_INFO_H
diff --git a/src/server/shared/Realm/RealmList.cpp b/src/server/shared/Realm/RealmList.cpp
index fd1e6e3a165..095640f949b 100644
--- a/src/server/shared/Realm/RealmList.cpp
+++ b/src/server/shared/Realm/RealmList.cpp
@@ -81,14 +81,17 @@ void RealmList::Close()
void RealmList::LoadBuildInfo()
{
+ _builds.clear();
+
// 0 1 2 3 4 5 6
if (QueryResult result = LoginDatabase.Query("SELECT majorVersion, minorVersion, bugfixVersion, hotfixVersion, build, win64AuthSeed, mac64AuthSeed, macArmAuthSeed FROM build_info ORDER BY build ASC"))
{
do
{
+ using namespace ClientBuild;
+
Field* fields = result->Fetch();
- _builds.emplace_back();
- RealmBuildInfo& build = _builds.back();
+ Info& build = _builds.emplace_back();
build.MajorVersion = fields[0].GetUInt32();
build.MinorVersion = fields[1].GetUInt32();
build.BugfixVersion = fields[2].GetUInt32();
@@ -100,22 +103,28 @@ void RealmList::LoadBuildInfo()
build.Build = fields[4].GetUInt32();
std::string win64AuthSeedHexStr = fields[5].GetString();
- if (win64AuthSeedHexStr.length() == build.Win64AuthSeed.size() * 2)
- HexStrToByteArray(win64AuthSeedHexStr, build.Win64AuthSeed);
- else
- build.Win64AuthSeed = { };
+ if (win64AuthSeedHexStr.length() == AuthKey::Size * 2)
+ {
+ AuthKey& buildKey = build.AuthKeys.emplace_back();
+ buildKey.Variant = { .Platform = PlatformType::Windows, .Arch = Arch::x64, .Type = Type::Retail };
+ HexStrToByteArray(win64AuthSeedHexStr, buildKey.Key);
+ }
std::string mac64AuthSeedHexStr = fields[6].GetString();
- if (mac64AuthSeedHexStr.length() == build.Mac64AuthSeed.size() * 2)
- HexStrToByteArray(mac64AuthSeedHexStr, build.Mac64AuthSeed);
- else
- build.Mac64AuthSeed = { };
+ if (mac64AuthSeedHexStr.length() == AuthKey::Size * 2)
+ {
+ AuthKey& buildKey = build.AuthKeys.emplace_back();
+ buildKey.Variant = { .Platform = PlatformType::macOS, .Arch = Arch::x64, .Type = Type::Retail };
+ HexStrToByteArray(mac64AuthSeedHexStr, buildKey.Key);
+ }
std::string macArmAuthSeedHexStr = fields[7].GetString();
- if (macArmAuthSeedHexStr.length() == build.MacArmAuthSeed.size() * 2)
- HexStrToByteArray(macArmAuthSeedHexStr, build.MacArmAuthSeed);
- else
- build.MacArmAuthSeed = { };
+ if (macArmAuthSeedHexStr.length() == AuthKey::Size * 2)
+ {
+ AuthKey& buildKey = build.AuthKeys.emplace_back();
+ buildKey.Variant = { .Platform = PlatformType::macOS, .Arch = Arch::Arm64, .Type = Type::Retail };
+ HexStrToByteArray(macArmAuthSeedHexStr, buildKey.Key);
+ }
} while (result->NextRow());
}
@@ -260,15 +269,15 @@ std::shared_ptr<Realm const> RealmList::GetCurrentRealm() const
return nullptr;
}
-RealmBuildInfo const* RealmList::GetBuildInfo(uint32 build) const
+ClientBuild::Info const* RealmList::GetBuildInfo(uint32 build) const
{
- auto buildInfo = std::ranges::find(_builds, build, &RealmBuildInfo::Build);
+ auto buildInfo = std::ranges::find(_builds, build, &ClientBuild::Info::Build);
return buildInfo != _builds.end() ? &*buildInfo : nullptr;
}
uint32 RealmList::GetMinorMajorBugfixVersionForBuild(uint32 build) const
{
- auto buildInfo = std::ranges::lower_bound(_builds, build, {}, &RealmBuildInfo::Build);
+ auto buildInfo = std::ranges::lower_bound(_builds, build, {}, &ClientBuild::Info::Build);
return buildInfo != _builds.end() ? (buildInfo->MajorVersion * 10000 + buildInfo->MinorVersion * 100 + buildInfo->BugfixVersion) : 0;
}
@@ -291,7 +300,7 @@ void RealmList::FillRealmEntry(Realm const& realm, uint32 clientBuild, AccountTy
realmEntry->set_cfgcategoriesid(realm.Timezone);
JSON::RealmList::ClientVersion* version = realmEntry->mutable_version();
- if (RealmBuildInfo const* buildInfo = GetBuildInfo(realm.Build))
+ if (ClientBuild::Info const* buildInfo = GetBuildInfo(realm.Build))
{
version->set_versionmajor(buildInfo->MajorVersion);
version->set_versionminor(buildInfo->MinorVersion);
@@ -367,9 +376,9 @@ std::vector<uint8> RealmList::GetRealmList(uint32 build, AccountTypes accountSec
return compressed;
}
-uint32 RealmList::JoinRealm(uint32 realmAddress, uint32 build, 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
+uint32 RealmList::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
{
if (std::shared_ptr<Realm const> realm = GetRealm(realmAddress))
{
@@ -407,9 +416,15 @@ uint32 RealmList::JoinRealm(uint32 realmAddress, uint32 build, boost::asio::ip::
stmt->setString(6, accountName);
LoginDatabase.DirectExecute(stmt);
+ JSON::RealmList::RealmJoinTicket joinTicket;
+ joinTicket.set_gameaccount(accountName);
+ joinTicket.set_platform(buildVariant.Platform);
+ joinTicket.set_clientarch(buildVariant.Arch);
+ joinTicket.set_type(buildVariant.Type);
+
bgs::protocol::Attribute* attribute = response->add_attribute();
attribute->set_name("Param_RealmJoinTicket");
- attribute->mutable_value()->set_blob_value(accountName);
+ attribute->mutable_value()->set_blob_value(JSON::Serialize(joinTicket));
attribute = response->add_attribute();
attribute->set_name("Param_ServerAddresses");
diff --git a/src/server/shared/Realm/RealmList.h b/src/server/shared/Realm/RealmList.h
index b61ad502877..ac408feada2 100644
--- a/src/server/shared/Realm/RealmList.h
+++ b/src/server/shared/Realm/RealmList.h
@@ -18,6 +18,7 @@
#ifndef _REALMLIST_H
#define _REALMLIST_H
+#include "ClientBuildInfo.h"
#include "Define.h"
#include "Duration.h"
#include "Optional.h"
@@ -29,18 +30,6 @@
#include <unordered_set>
#include <vector>
-struct RealmBuildInfo
-{
- uint32 Build;
- uint32 MajorVersion;
- uint32 MinorVersion;
- uint32 BugfixVersion;
- std::array<char, 4> HotfixVersion;
- std::array<uint8, 16> Win64AuthSeed;
- std::array<uint8, 16> Mac64AuthSeed;
- std::array<uint8, 16> MacArmAuthSeed;
-};
-
namespace bgs::protocol::game_utilities::v1
{
class ClientResponse;
@@ -68,6 +57,7 @@ public:
~RealmList();
void Initialize(Trinity::Asio::IoContext& ioContext, uint32 updateInterval);
+ void LoadBuildInfo();
void Close();
std::shared_ptr<Realm const> GetRealm(Battlenet::RealmHandle const& id) const;
@@ -75,26 +65,25 @@ public:
void SetCurrentRealmId(Battlenet::RealmHandle const& id);
std::shared_ptr<Realm const> GetCurrentRealm() const;
- RealmBuildInfo const* GetBuildInfo(uint32 build) const;
+ ClientBuild::Info const* GetBuildInfo(uint32 build) const;
uint32 GetMinorMajorBugfixVersionForBuild(uint32 build) const;
void WriteSubRegions(bgs::protocol::game_utilities::v1::GetAllValuesForAttributeResponse* response) const;
std::vector<uint8> GetRealmEntryJSON(Battlenet::RealmHandle const& id, uint32 build, AccountTypes accountSecurityLevel) const;
std::vector<uint8> GetRealmList(uint32 build, AccountTypes accountSecurityLevel, std::string const& subRegion) const;
- uint32 JoinRealm(uint32 realmAddress, uint32 build, 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;
+ 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;
private:
RealmList();
- void LoadBuildInfo();
void UpdateRealms();
static void UpdateRealm(Realm& realm, Battlenet::RealmHandle const& id, uint32 build, std::string const& name,
boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr,
uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, RealmPopulationState population);
void FillRealmEntry(Realm const& realm, uint32 clientBuild, AccountTypes accountSecurityLevel, JSON::RealmList::RealmEntry* realmEntry) const;
- std::vector<RealmBuildInfo> _builds;
+ std::vector<ClientBuild::Info> _builds;
mutable std::shared_mutex _realmsMutex;
RealmMap _realms;
std::map<Battlenet::RealmHandle, std::string> _removedRealms;