Core: Updated to 6.2.4

* Rewrite bnetserver for new authentication protocol
This commit is contained in:
Shauren
2016-03-28 17:12:57 +02:00
parent 619669c620
commit dde620c402
85 changed files with 9688 additions and 4976 deletions

View File

@@ -44,7 +44,9 @@ target_link_libraries(shared
PUBLIC
database
rapidjson
proto)
proto
zlib
gsoap)
set_target_properties(shared
PROPERTIES

View File

@@ -0,0 +1,456 @@
/*
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
*
* 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 "ProtobufJSON.h"
#include "StringFormat.h"
#include "Common.h"
#include "Log.h"
#include <rapidjson/writer.h>
#include <rapidjson/reader.h>
#include <rapidjson/stringbuffer.h>
#include <stack>
class Serializer
{
public:
Serializer() : _writer(_buffer) { }
void WriteInt32(int32 value) { _writer.Int(value); }
void WriteInt64(int64 value) { _writer.Int64(value); }
void WriteUInt32(uint32 value) { _writer.Uint(value); }
void WriteUInt64(uint64 value) { _writer.Uint64(value); }
void WriteDouble(double value) { _writer.Double(value); }
void WriteFloat(float value) { _writer.Double(value); }
void WriteBool(bool value) { _writer.Bool(value); }
void WriteEnum(google::protobuf::EnumValueDescriptor const* value) { _writer.String(value->name()); }
void WriteString(std::string const& value) { _writer.String(value); }
void WriteMessage(google::protobuf::Message const& value);
std::string GetString() const { return std::string(_buffer.GetString(), _buffer.GetSize()); }
private:
void WriteMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
void WriteSimpleMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
void WriteRepeatedMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field);
rapidjson::StringBuffer _buffer;
rapidjson::Writer<rapidjson::StringBuffer> _writer;
};
void Serializer::WriteMessage(google::protobuf::Message const& value)
{
google::protobuf::Reflection const* reflection = value.GetReflection();
std::vector<google::protobuf::FieldDescriptor const*> fields;
reflection->ListFields(value, &fields);
_writer.StartObject();
for (std::size_t i = 0; i < fields.size(); ++i)
WriteMessageField(value, fields[i]);
_writer.EndObject();
}
void Serializer::WriteMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
{
_writer.Key(field->name().c_str());
if (field->is_repeated())
{
_writer.StartArray();
WriteRepeatedMessageField(value, field);
_writer.EndArray();
}
else
WriteSimpleMessageField(value, field);
}
void Serializer::WriteSimpleMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
{
google::protobuf::Reflection const* reflection = value.GetReflection();
switch (field->cpp_type())
{
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
WriteInt32(reflection->GetInt32(value, field));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
WriteInt64(reflection->GetInt64(value, field));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
WriteUInt32(reflection->GetUInt32(value, field));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
WriteUInt64(reflection->GetUInt64(value, field));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
WriteDouble(reflection->GetDouble(value, field));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
WriteFloat(reflection->GetFloat(value, field));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
WriteBool(reflection->GetBool(value, field));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
WriteEnum(reflection->GetEnum(value, field));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
{
std::string strValue = reflection->GetString(value, field);
if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING)
WriteString(strValue);
else
{
_writer.StartArray();
for (std::size_t i = 0; i < strValue.length(); ++i)
WriteUInt32(uint32(strValue[i]));
_writer.EndArray();
}
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
WriteMessage(reflection->GetMessage(value, field));
break;
default:
break;
}
}
void Serializer::WriteRepeatedMessageField(google::protobuf::Message const& value, google::protobuf::FieldDescriptor const* field)
{
google::protobuf::Reflection const* reflection = value.GetReflection();
for (int32 i = 0; i < reflection->FieldSize(value, field); ++i)
{
switch (field->cpp_type())
{
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
WriteInt32(reflection->GetRepeatedInt32(value, field, i));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
WriteInt64(reflection->GetRepeatedInt64(value, field, i));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
WriteUInt32(reflection->GetRepeatedUInt32(value, field, i));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
WriteUInt64(reflection->GetRepeatedUInt64(value, field, i));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
WriteDouble(reflection->GetRepeatedDouble(value, field, i));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
WriteFloat(reflection->GetRepeatedFloat(value, field, i));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
WriteBool(reflection->GetRepeatedBool(value, field, i));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
WriteEnum(reflection->GetRepeatedEnum(value, field, i));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
{
std::string strValue = reflection->GetRepeatedString(value, field, i);
if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING)
WriteString(strValue);
else
{
_writer.StartArray();
for (std::size_t j = 0; j < strValue.length(); ++j)
WriteUInt32(uint32(strValue[j]));
_writer.EndArray();
}
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
WriteMessage(reflection->GetRepeatedMessage(value, field, i));
break;
default:
break;
}
}
}
class Deserializer : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, Deserializer>
{
public:
bool ReadMessage(std::string json, google::protobuf::Message* message);
bool Key(const Ch* str, rapidjson::SizeType length, bool copy);
bool Null();
bool Bool(bool b);
bool Int(int32 i);
bool Uint(uint32 i);
bool Int64(int64 i);
bool Uint64(uint64 i);
bool Double(double d);
bool String(const Ch* str, rapidjson::SizeType length, bool copy);
bool StartObject();
bool EndObject(rapidjson::SizeType memberCount);
bool StartArray();
bool EndArray(rapidjson::SizeType memberCount);
std::vector<std::string> const& GetErrors() const { return _errors; }
private:
bool CheckType(google::protobuf::FieldDescriptor::CppType expectedType);
rapidjson::Reader _reader;
std::stack<google::protobuf::FieldDescriptor const*> _state;
std::stack<google::protobuf::Message*> _objectState;
std::vector<std::string> _errors;
};
bool Deserializer::ReadMessage(std::string json, google::protobuf::Message* message)
{
rapidjson::StringStream ss(json.c_str());
_objectState.push(message);
rapidjson::ParseResult result = _reader.Parse(ss, *this);
ASSERT(result.IsError() || (_objectState.empty() && _state.empty()));
return !result.IsError() && _errors.empty();
}
bool Deserializer::Key(const Ch* str, rapidjson::SizeType /*length*/, bool /*copy*/)
{
google::protobuf::FieldDescriptor const* field = _objectState.top()->GetDescriptor()->FindFieldByName(str);
if (!field)
{
_errors.push_back(Trinity::StringFormat("Message %s has no field %s.", _objectState.top()->GetTypeName().c_str(), str));
return false;
}
_state.push(field);
return true;
}
bool Deserializer::Null()
{
_state.pop();
return true;
}
#define SET_FIELD(message, field, Type, val) do { \
if (!field->is_repeated()) \
message->GetReflection()->Set ## Type(message, field, val); \
else \
message->GetReflection()->Add ## Type(message, field, val); \
_state.pop(); \
} while (0)
bool Deserializer::Bool(bool b)
{
if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_BOOL))
return false;
SET_FIELD(_objectState.top(), _state.top(), Bool, b);
return true;
}
bool Deserializer::Int(int32 i)
{
if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_INT32))
return false;
SET_FIELD(_objectState.top(), _state.top(), Int32, i);
return true;
}
bool Deserializer::Uint(uint32 i)
{
google::protobuf::FieldDescriptor const* field = _state.top();
google::protobuf::Message* message = _objectState.top();
switch (field->cpp_type())
{
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
SET_FIELD(message, field, UInt32, i);
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
{
if (field->type() != google::protobuf::FieldDescriptor::TYPE_BYTES)
{
_errors.emplace_back("Expected field type to be bytes but got string instead.");
return false;
}
std::string currentValue = message->GetReflection()->GetString(*message, field);
currentValue.append(1, (char)i);
message->GetReflection()->SetString(message, field, currentValue);
break;
}
default:
_errors.push_back(Trinity::StringFormat("Expected field type to be uint32 or string but got %s instead.", _state.top()->cpp_type_name()));
return false;
}
return true;
}
bool Deserializer::Int64(int64 i)
{
if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_INT64))
return false;
SET_FIELD(_objectState.top(), _state.top(), Int64, i);
return true;
}
bool Deserializer::Uint64(uint64 i)
{
if (!CheckType(google::protobuf::FieldDescriptor::CPPTYPE_UINT64))
return false;
SET_FIELD(_objectState.top(), _state.top(), UInt64, i);
return true;
}
bool Deserializer::Double(double d)
{
google::protobuf::FieldDescriptor const* field = _state.top();
google::protobuf::Message* message = _objectState.top();
switch (field->cpp_type())
{
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
SET_FIELD(message, field, Float, d);
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
SET_FIELD(message, field, Double, d);
break;
default:
_errors.push_back(Trinity::StringFormat("Expected field type to be float or double but got %s instead.", _state.top()->cpp_type_name()));
return false;
}
return true;
}
bool Deserializer::String(const Ch* str, rapidjson::SizeType /*length*/, bool /*copy*/)
{
google::protobuf::FieldDescriptor const* field = _state.top();
google::protobuf::Message* message = _objectState.top();
switch (field->cpp_type())
{
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
{
google::protobuf::EnumValueDescriptor const* enumValue = field->enum_type()->FindValueByName(str);
if (!enumValue)
{
_errors.push_back(Trinity::StringFormat("Field %s enum %s does not have a value named %s.", field->full_name().c_str(), field->enum_type()->full_name().c_str(), str));
return false;
}
SET_FIELD(message, field, Enum, enumValue);
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
SET_FIELD(message, field, String, str);
break;
default:
_errors.push_back(Trinity::StringFormat("Expected field type to be string or enum but got %s instead.", _state.top()->cpp_type_name()));
return false;
}
return true;
}
bool Deserializer::StartObject()
{
// not a root object
if (!_state.empty())
{
if (_state.top()->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
{
_errors.push_back(Trinity::StringFormat("Expected field %s to be a message but got %s instead.", _state.top()->cpp_type_name()));
return false;
}
google::protobuf::Message* containingMessage = _objectState.top();
if (!_state.top()->is_repeated())
_objectState.push(containingMessage->GetReflection()->MutableMessage(containingMessage, _state.top()));
else
_objectState.push(containingMessage->GetReflection()->AddMessage(containingMessage, _state.top()));
}
else if (_objectState.size() != 1)
return false;
return true;
}
bool Deserializer::EndObject(rapidjson::SizeType /*memberCount*/)
{
if (!_state.empty() && !_state.top()->is_repeated())
_state.pop();
_objectState.pop();
return true;
}
bool Deserializer::StartArray()
{
if (_state.empty())
{
_errors.emplace_back("Root cannot be an array.");
return false;
}
if (_state.top()->is_repeated() ^ (_state.top()->type() != google::protobuf::FieldDescriptor::TYPE_BYTES))
{
_errors.push_back(Trinity::StringFormat("Expected field %s type to be exactly an array OR bytes but it was both or none.", _state.top()->full_name().c_str()));
return false;
}
return true;
}
bool Deserializer::CheckType(google::protobuf::FieldDescriptor::CppType expectedType)
{
if (_state.top()->cpp_type() != expectedType)
{
_errors.push_back(Trinity::StringFormat("Expected field %s type to be %s but got %s instead.",
_state.top()->full_name().c_str(), google::protobuf::FieldDescriptor::CppTypeName(expectedType), _state.top()->cpp_type_name()));
return false;
}
return true;
}
bool Deserializer::EndArray(rapidjson::SizeType /*memberCount*/)
{
_state.pop();
return true;
}
#undef SET_FIELD
std::string JSON::Serialize(google::protobuf::Message const& message)
{
Serializer serializer;
serializer.WriteMessage(message);
return serializer.GetString();
}
bool JSON::Deserialize(std::string json, google::protobuf::Message* message)
{
Deserializer deserializer;
if (!deserializer.ReadMessage(std::forward<std::string>(json), message))
{
for (std::size_t i = 0; i < deserializer.GetErrors().size(); ++i)
TC_LOG_ERROR("json", "%s", deserializer.GetErrors()[i].c_str());
return false;
}
return true;
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
*
* 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 ProtobufJSON_h__
#define ProtobufJSON_h__
#include "Define.h"
#include <google/protobuf/message.h>
#include <string>
namespace JSON
{
TC_SHARED_API std::string Serialize(google::protobuf::Message const& message);
TC_SHARED_API bool Deserialize(std::string json, google::protobuf::Message* message);
}
#endif // ProtobufJSON_h__

View File

@@ -61,3 +61,13 @@ uint32 const Realm::ConfigIdByType[MAX_CLIENT_REALM_TYPE] =
{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
};
std::string Battlenet::RealmHandle::GetAddressString() const
{
return Trinity::StringFormat("%u-%u-%u", Region, Site, Realm);
}
std::string Battlenet::RealmHandle::GetSubRegionAddress() const
{
return Trinity::StringFormat("%u-%u-0", Region, Site);
}

View File

@@ -46,6 +46,7 @@ namespace Battlenet
RealmHandle() : Region(0), Site(0), Realm(0) { }
RealmHandle(uint8 region, uint8 battlegroup, uint32 index)
: Region(region), Site(battlegroup), Realm(index) { }
RealmHandle(uint32 realmAddress) : Region((realmAddress >> 24) & 0xFF), Site((realmAddress >> 16) & 0xFF), Realm(realmAddress & 0xFFFF) { }
uint8 Region;
uint8 Site;
@@ -56,7 +57,9 @@ namespace Battlenet
return Realm < r.Realm;
}
uint32 GetAddress() const { return ((Site << 16) & 0xFF0000) | uint16(Realm); }
uint32 GetAddress() const { return (Region << 24) | (Site << 16) | uint16(Realm); }
std::string GetAddressString() const;
std::string GetSubRegionAddress() const;
};
}

View File

@@ -16,11 +16,16 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "Util.h"
#include "RealmList.h"
#include <boost/asio/ip/tcp.hpp>
#include "BattlenetRpcErrorCodes.h"
#include "Database/DatabaseEnv.h"
#include "ProtobufJSON.h"
#include "SHA256.h"
#include "BigNumber.h"
#include "Util.h"
#include "game_utilities_service.pb.h"
#include "RealmList.pb.h"
#include <zlib.h>
RealmList::RealmList() : _updateInterval(0), _updateTimer(nullptr), _resolver(nullptr)
{
@@ -53,16 +58,6 @@ void RealmList::Close()
_updateTimer->cancel();
}
template<typename FieldType>
inline void UpdateField(FieldType& out, FieldType const& in, bool& changed)
{
if (out != in)
{
out = in;
changed = true;
}
}
void RealmList::UpdateRealm(Battlenet::RealmHandle const& id, uint32 build, const std::string& name, ip::address const& address, ip::address const& localAddr,
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel,
float population)
@@ -163,6 +158,8 @@ void RealmList::UpdateRealms(boost::system::error_code const& error)
UpdateRealm(id, build, name, externalAddress, localAddress, localSubmask, port, icon, flag,
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
_subRegions.insert(Battlenet::RealmHandle{ region, battlegroup, 0 }.GetAddressString());
if (!existingRealms.count(id))
TC_LOG_INFO("realmlist", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddress.to_string().c_str(), port);
else
@@ -197,3 +194,208 @@ Realm const* RealmList::GetRealm(Battlenet::RealmHandle const& id) const
return NULL;
}
RealmBuildInfo const* RealmList::GetBuildInfo(uint32 build) const
{
// List of client builds for verbose version info in realmlist packet
static std::vector<RealmBuildInfo> const ClientBuilds =
{
{ 21355, 6, 2, 4, ' ' },
{ 20726, 6, 2, 3, ' ' },
{ 20574, 6, 2, 2, 'a' },
{ 20490, 6, 2, 2, 'a' },
{ 15595, 4, 3, 4, ' ' },
{ 14545, 4, 2, 2, ' ' },
{ 13623, 4, 0, 6, 'a' },
{ 13930, 3, 3, 5, 'a' }, // 3.3.5a China Mainland build
{ 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, ' ' },
{ 6141, 1, 12, 3, ' ' },
{ 6005, 1, 12, 2, ' ' },
{ 5875, 1, 12, 1, ' ' },
};
for (std::size_t i = 0; i < ClientBuilds.size(); ++i)
if (ClientBuilds[i].Build == build)
return &ClientBuilds[i];
return nullptr;
}
void RealmList::WriteSubRegions(bgs::protocol::game_utilities::v1::GetAllValuesForAttributeResponse* response) const
{
for (std::string const& subRegion : GetSubRegions())
response->add_attribute_value()->set_string_value(subRegion);
}
std::vector<uint8> RealmList::GetRealmEntryJSON(Battlenet::RealmHandle const& id, uint32 build) const
{
std::vector<uint8> compressed;
if (Realm const* realm = GetRealm(id))
{
if (!(realm->Flags & REALM_FLAG_OFFLINE) && realm->Build == build)
{
JSON::RealmList::RealmEntry realmEntry;
realmEntry.set_wowrealmaddress(realm->Id.GetAddress());
realmEntry.set_cfgtimezonesid(1);
realmEntry.set_populationstate(std::max(uint32(realm->PopulationLevel), 1u));
realmEntry.set_cfgcategoriesid(realm->Timezone);
JSON::RealmList::ClientVersion* version = realmEntry.mutable_version();
if (RealmBuildInfo const* buildInfo = GetBuildInfo(realm->Build))
{
version->set_versionmajor(buildInfo->MajorVersion);
version->set_versionminor(buildInfo->MinorVersion);
version->set_versionrevision(buildInfo->BugfixVersion);
version->set_versionbuild(buildInfo->Build);
}
else
{
version->set_versionmajor(6);
version->set_versionminor(2);
version->set_versionrevision(4);
version->set_versionbuild(realm->Build);
}
realmEntry.set_cfgrealmsid(realm->Id.Realm);
realmEntry.set_flags(realm->Flags);
realmEntry.set_name(realm->Name);
realmEntry.set_cfgconfigsid(realm->GetConfigId());
realmEntry.set_cfglanguagesid(1);
std::string json = "JamJSONRealmEntry:" + JSON::Serialize(realmEntry);
uLong compressedLength = compressBound(json.length());
compressed.resize(compressedLength + 4);
*reinterpret_cast<uint32*>(compressed.data()) = json.length() + 1;
if (compress(compressed.data() + 4, &compressedLength, reinterpret_cast<uint8 const*>(json.c_str()), json.length() + 1) == Z_OK)
compressed.resize(compressedLength + 4);
else
compressed.clear();
}
}
return compressed;
}
std::vector<uint8> RealmList::GetRealmList(uint32 build, std::string const& subRegion) const
{
JSON::RealmList::RealmListUpdates realmList;
for (auto const& realm : _realms)
{
if (realm.second.Id.GetSubRegionAddress() != subRegion)
continue;
uint32 flag = realm.second.Flags;
if (realm.second.Build != build)
flag |= REALM_FLAG_VERSION_MISMATCH;
JSON::RealmList::RealmState* state = realmList.add_updates();
state->mutable_update()->set_wowrealmaddress(realm.second.Id.GetAddress());
state->mutable_update()->set_cfgtimezonesid(1);
state->mutable_update()->set_populationstate((realm.second.Flags & REALM_FLAG_OFFLINE) ? 0u : std::max(uint32(realm.second.PopulationLevel), 1u));
state->mutable_update()->set_cfgcategoriesid(realm.second.Timezone);
JSON::RealmList::ClientVersion* version = state->mutable_update()->mutable_version();
if (RealmBuildInfo const* buildInfo = GetBuildInfo(realm.second.Build))
{
version->set_versionmajor(buildInfo->MajorVersion);
version->set_versionminor(buildInfo->MinorVersion);
version->set_versionrevision(buildInfo->BugfixVersion);
version->set_versionbuild(buildInfo->Build);
}
else
{
version->set_versionmajor(6);
version->set_versionminor(2);
version->set_versionrevision(4);
version->set_versionbuild(realm.second.Build);
}
state->mutable_update()->set_cfgrealmsid(realm.second.Id.Realm);
state->mutable_update()->set_flags(flag);
state->mutable_update()->set_name(realm.second.Name);
state->mutable_update()->set_cfgconfigsid(realm.second.GetConfigId());
state->mutable_update()->set_cfglanguagesid(1);
state->set_deleting(false);
}
std::string json = "JSONRealmListUpdates:" + JSON::Serialize(realmList);
uLong compressedLength = compressBound(json.length());
std::vector<uint8> compressed;
compressed.resize(4 + compressedLength);
*reinterpret_cast<uint32*>(compressed.data()) = json.length() + 1;
compress(compressed.data() + 4, &compressedLength, reinterpret_cast<uint8 const*>(json.c_str()), json.length() + 1);
compressed.resize(compressedLength + 4);
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, std::string accountName, bgs::protocol::game_utilities::v1::ClientResponse* response) const
{
if (Realm const* realm = GetRealm(Battlenet::RealmHandle(realmAddress)))
{
if (realm->Flags & REALM_FLAG_OFFLINE || realm->Build != build)
return ERROR_USER_SERVER_NOT_PERMITTED_ON_REALM;
JSON::RealmList::RealmListServerIPAddresses serverAddresses;
JSON::RealmList::RealmIPAddressFamily* addressFamily = serverAddresses.add_families();
addressFamily->set_family(1);
JSON::RealmList::IPAddress* address = addressFamily->add_addresses();
address->set_ip(realm->GetAddressForClient(clientAddress).address().to_string());
address->set_port(realm->Port);
std::string json = "JSONRealmListServerIPAddresses:" + JSON::Serialize(serverAddresses);
uLong compressedLength = compressBound(json.length());
std::vector<uint8> compressed;
compressed.resize(4 + compressedLength);
*reinterpret_cast<uint32*>(compressed.data()) = json.length() + 1;
if (compress(compressed.data() + 4, &compressedLength, reinterpret_cast<uint8 const*>(json.c_str()), json.length() + 1) != Z_OK)
return ERROR_UTIL_SERVER_FAILED_TO_SERIALIZE_RESPONSE;
BigNumber serverSecret;
serverSecret.SetRand(8 * 32);
SHA256Hash wowSessionKey;
wowSessionKey.UpdateData(clientSecret.data(), clientSecret.size());
wowSessionKey.UpdateData(serverSecret.AsByteArray(32).get(), 32);
wowSessionKey.Finalize();
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_GAME_ACCOUNT_LOGIN_INFO);
stmt->setString(0, ByteArrayToHexStr(wowSessionKey.GetDigest(), wowSessionKey.GetLength(), true));
stmt->setString(1, clientAddress.to_string());
stmt->setUInt8(2, locale);
stmt->setString(3, os);
stmt->setString(4, accountName);
LoginDatabase.DirectExecute(stmt);
bgs::protocol::Attribute* attribute = response->add_attribute();
attribute->set_name("Param_RealmJoinTicket");
attribute->mutable_value()->set_blob_value(accountName);
attribute = response->add_attribute();
attribute->set_name("Param_ServerAddresses");
attribute->mutable_value()->set_blob_value(compressed.data(), compressedLength + 4);
attribute = response->add_attribute();
attribute->set_name("Param_JoinSecret");
attribute->mutable_value()->set_blob_value(serverSecret.AsByteArray(32).get(), 32);
return ERROR_OK;
}
return ERROR_UTIL_SERVER_UNKNOWN_REALM;
}

View File

@@ -28,6 +28,38 @@
using namespace boost::asio;
struct RealmBuildInfo
{
uint32 Build;
uint32 MajorVersion;
uint32 MinorVersion;
uint32 BugfixVersion;
uint32 HotfixVersion;
};
namespace bgs
{
namespace protocol
{
namespace game_utilities
{
namespace v1
{
class ClientResponse;
class GetAllValuesForAttributeResponse;
}
}
}
}
namespace JSON
{
namespace RealmList
{
class RealmListUpdates;
}
}
/// Storage object for the list of realms on the server
class TC_SHARED_API RealmList
{
@@ -44,6 +76,14 @@ public:
RealmMap const& GetRealms() const { return _realms; }
Realm const* GetRealm(Battlenet::RealmHandle const& id) const;
RealmBuildInfo const* GetBuildInfo(uint32 build) const;
std::unordered_set<std::string> const& GetSubRegions() const { return _subRegions; }
void WriteSubRegions(bgs::protocol::game_utilities::v1::GetAllValuesForAttributeResponse* response) const;
std::vector<uint8> GetRealmEntryJSON(Battlenet::RealmHandle const& id, uint32 build) const;
std::vector<uint8> GetRealmList(uint32 build, 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, std::string accountName, bgs::protocol::game_utilities::v1::ClientResponse* response) const;
private:
RealmList();
@@ -52,6 +92,7 @@ private:
ip::address const& localSubmask, uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population);
RealmMap _realms;
std::unordered_set<std::string> _subRegions;
uint32 _updateInterval;
boost::asio::deadline_timer* _updateTimer;
boost::asio::ip::tcp::resolver* _resolver;