mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-26 11:52:32 +01:00
Core: Updated to 6.2.4
* Rewrite bnetserver for new authentication protocol
This commit is contained in:
@@ -44,7 +44,9 @@ target_link_libraries(shared
|
||||
PUBLIC
|
||||
database
|
||||
rapidjson
|
||||
proto)
|
||||
proto
|
||||
zlib
|
||||
gsoap)
|
||||
|
||||
set_target_properties(shared
|
||||
PROPERTIES
|
||||
|
||||
456
src/server/shared/JSON/ProtobufJSON.cpp
Normal file
456
src/server/shared/JSON/ProtobufJSON.cpp
Normal 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;
|
||||
}
|
||||
31
src/server/shared/JSON/ProtobufJSON.h
Normal file
31
src/server/shared/JSON/ProtobufJSON.h
Normal 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__
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user