aboutsummaryrefslogtreecommitdiff
path: root/src/server/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/shared')
-rw-r--r--src/server/shared/CMakeLists.txt4
-rw-r--r--src/server/shared/JSON/ProtobufJSON.cpp456
-rw-r--r--src/server/shared/JSON/ProtobufJSON.h31
-rw-r--r--src/server/shared/Realm/Realm.cpp10
-rw-r--r--src/server/shared/Realm/Realm.h5
-rw-r--r--src/server/shared/Realm/RealmList.cpp228
-rw-r--r--src/server/shared/Realm/RealmList.h41
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;