diff options
Diffstat (limited to 'src/server/shared')
| -rw-r--r-- | src/server/shared/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/server/shared/JSON/ProtobufJSON.cpp | 456 | ||||
| -rw-r--r-- | src/server/shared/JSON/ProtobufJSON.h | 31 | ||||
| -rw-r--r-- | src/server/shared/Realm/Realm.cpp | 10 | ||||
| -rw-r--r-- | src/server/shared/Realm/Realm.h | 5 | ||||
| -rw-r--r-- | src/server/shared/Realm/RealmList.cpp | 228 | ||||
| -rw-r--r-- | src/server/shared/Realm/RealmList.h | 41 |
7 files changed, 760 insertions, 15 deletions
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt index 052902de9c8..6b7ce7b5a1a 100644 --- a/src/server/shared/CMakeLists.txt +++ b/src/server/shared/CMakeLists.txt @@ -44,7 +44,9 @@ target_link_libraries(shared PUBLIC database rapidjson - proto) + proto + zlib + gsoap) set_target_properties(shared PROPERTIES diff --git a/src/server/shared/JSON/ProtobufJSON.cpp b/src/server/shared/JSON/ProtobufJSON.cpp new file mode 100644 index 00000000000..11108453f06 --- /dev/null +++ b/src/server/shared/JSON/ProtobufJSON.cpp @@ -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; +} diff --git a/src/server/shared/JSON/ProtobufJSON.h b/src/server/shared/JSON/ProtobufJSON.h new file mode 100644 index 00000000000..5ddc09bb474 --- /dev/null +++ b/src/server/shared/JSON/ProtobufJSON.h @@ -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__ diff --git a/src/server/shared/Realm/Realm.cpp b/src/server/shared/Realm/Realm.cpp index fca9e86046c..2af537b45e5 100644 --- a/src/server/shared/Realm/Realm.cpp +++ b/src/server/shared/Realm/Realm.cpp @@ -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); +} diff --git a/src/server/shared/Realm/Realm.h b/src/server/shared/Realm/Realm.h index 9dc672403f7..c9042cb3da7 100644 --- a/src/server/shared/Realm/Realm.h +++ b/src/server/shared/Realm/Realm.h @@ -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; }; } diff --git a/src/server/shared/Realm/RealmList.cpp b/src/server/shared/Realm/RealmList.cpp index 57b9ecb0166..260b120dd8c 100644 --- a/src/server/shared/Realm/RealmList.cpp +++ b/src/server/shared/Realm/RealmList.cpp @@ -16,11 +16,16 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "Common.h" +#include "RealmList.h" +#include "BattlenetRpcErrorCodes.h" #include "Database/DatabaseEnv.h" +#include "ProtobufJSON.h" +#include "SHA256.h" +#include "BigNumber.h" #include "Util.h" -#include "RealmList.h" -#include <boost/asio/ip/tcp.hpp> +#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; +} diff --git a/src/server/shared/Realm/RealmList.h b/src/server/shared/Realm/RealmList.h index 47d501888cd..3a8761e638f 100644 --- a/src/server/shared/Realm/RealmList.h +++ b/src/server/shared/Realm/RealmList.h @@ -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; |
