mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 10:31:29 +00:00
Implement a basic websockets server
This commit is contained in:
parent
5bfcef600f
commit
ebca112769
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,3 +13,6 @@
|
|||||||
[submodule "submodules/recastnavigation"]
|
[submodule "submodules/recastnavigation"]
|
||||||
path = submodules/recastnavigation
|
path = submodules/recastnavigation
|
||||||
url = https://github.com/recastnavigation/recastnavigation.git
|
url = https://github.com/recastnavigation/recastnavigation.git
|
||||||
|
[submodule "submodules/websocketpp"]
|
||||||
|
path = submodules/websocketpp
|
||||||
|
url = https://github.com/zaphoyd/websocketpp.git
|
||||||
|
|||||||
@ -349,6 +349,7 @@ INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/submodules/recastnavigat
|
|||||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/submodules/recastnavigation/DetourCrowd/Include")
|
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/submodules/recastnavigation/DetourCrowd/Include")
|
||||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/submodules/recastnavigation/DetourTileCache/Include")
|
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/submodules/recastnavigation/DetourTileCache/Include")
|
||||||
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/submodules/recastnavigation/Recast/Include")
|
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/submodules/recastnavigation/Recast/Include")
|
||||||
|
INCLUDE_DIRECTORIES(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/submodules/websocketpp")
|
||||||
|
|
||||||
IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS OR EQEMU_BUILD_HC)
|
IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS OR EQEMU_BUILD_HC)
|
||||||
ADD_SUBDIRECTORY(common)
|
ADD_SUBDIRECTORY(common)
|
||||||
|
|||||||
@ -86,6 +86,8 @@ SET(common_sources
|
|||||||
net/servertalk_server_connection.cpp
|
net/servertalk_server_connection.cpp
|
||||||
net/tcp_connection.cpp
|
net/tcp_connection.cpp
|
||||||
net/tcp_server.cpp
|
net/tcp_server.cpp
|
||||||
|
net/websocket_server.cpp
|
||||||
|
net/websocket_server_connection.cpp
|
||||||
patches/patches.cpp
|
patches/patches.cpp
|
||||||
patches/sod.cpp
|
patches/sod.cpp
|
||||||
patches/sod_limits.cpp
|
patches/sod_limits.cpp
|
||||||
@ -230,6 +232,8 @@ SET(common_headers
|
|||||||
net/servertalk_server_connection.h
|
net/servertalk_server_connection.h
|
||||||
net/tcp_connection.h
|
net/tcp_connection.h
|
||||||
net/tcp_server.h
|
net/tcp_server.h
|
||||||
|
net/websocket_server.h
|
||||||
|
net/websocket_server_connection.h
|
||||||
patches/patches.h
|
patches/patches.h
|
||||||
patches/sod.h
|
patches/sod.h
|
||||||
patches/sod_limits.h
|
patches/sod_limits.h
|
||||||
@ -309,6 +313,10 @@ SOURCE_GROUP(Net FILES
|
|||||||
net/tcp_connection.h
|
net/tcp_connection.h
|
||||||
net/tcp_server.cpp
|
net/tcp_server.cpp
|
||||||
net/tcp_server.h
|
net/tcp_server.h
|
||||||
|
net/websocket_server.cpp
|
||||||
|
net/websocket_server.h
|
||||||
|
net/websocket_server_connection.cpp
|
||||||
|
net/websocket_server_connection.h
|
||||||
)
|
)
|
||||||
|
|
||||||
SOURCE_GROUP(Patches FILES
|
SOURCE_GROUP(Patches FILES
|
||||||
|
|||||||
@ -95,6 +95,7 @@ enum GameChatColor {
|
|||||||
EQEmuLogSys::EQEmuLogSys()
|
EQEmuLogSys::EQEmuLogSys()
|
||||||
{
|
{
|
||||||
on_log_gmsay_hook = [](uint16 log_type, const std::string &) {};
|
on_log_gmsay_hook = [](uint16 log_type, const std::string &) {};
|
||||||
|
on_log_console_hook = [](uint16 debug_level, uint16 log_type, const std::string &) {};
|
||||||
bool file_logs_enabled = false;
|
bool file_logs_enabled = false;
|
||||||
int log_platform = 0;
|
int log_platform = 0;
|
||||||
}
|
}
|
||||||
@ -346,6 +347,8 @@ void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category,
|
|||||||
#else
|
#else
|
||||||
std::cout << EQEmuLogSys::GetLinuxConsoleColorFromCategory(log_category) << message << LC_RESET << std::endl;
|
std::cout << EQEmuLogSys::GetLinuxConsoleColorFromCategory(log_category) << message << LC_RESET << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
on_log_console_hook(debug_level, log_category, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -505,4 +508,4 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name)
|
|||||||
std::ios_base::app | std::ios_base::out
|
std::ios_base::app | std::ios_base::out
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -240,7 +240,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
uint16 GetGMSayColorFromCategory(uint16 log_category);
|
uint16 GetGMSayColorFromCategory(uint16 log_category);
|
||||||
|
|
||||||
void OnLogHookCallBackZone(std::function<void(uint16 log_type, const std::string&)> f) { on_log_gmsay_hook = f; }
|
void SetGMSayHandler(std::function<void(uint16 log_type, const std::string&)> f) { on_log_gmsay_hook = f; }
|
||||||
|
void SetConsoleHandler(std::function<void(uint16 debug_level, uint16 log_type, const std::string&)> f) { on_log_console_hook = f; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -248,6 +249,7 @@ private:
|
|||||||
* Callback pointer to zone process for hooking logs to zone using GMSay
|
* Callback pointer to zone process for hooking logs to zone using GMSay
|
||||||
*/
|
*/
|
||||||
std::function<void(uint16 log_category, const std::string&)> on_log_gmsay_hook;
|
std::function<void(uint16 log_category, const std::string&)> on_log_gmsay_hook;
|
||||||
|
std::function<void(uint16 debug_level, uint16 log_category, const std::string&)> on_log_console_hook;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats log messages like '[Category] This is a log message'
|
* Formats log messages like '[Category] This is a log message'
|
||||||
|
|||||||
216
common/net/websocket_server.cpp
Normal file
216
common/net/websocket_server.cpp
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#include "websocket_server.h"
|
||||||
|
#include "../event/event_loop.h"
|
||||||
|
#include "../event/timer.h"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
struct MethodHandlerEntry
|
||||||
|
{
|
||||||
|
MethodHandlerEntry(EQ::Net::WebsocketServer::MethodHandler h, int s) {
|
||||||
|
handler = h;
|
||||||
|
status = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
EQ::Net::WebsocketServer::MethodHandler handler;
|
||||||
|
int status;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EQ::Net::WebsocketServer::Impl
|
||||||
|
{
|
||||||
|
std::unique_ptr<TCPServer> server;
|
||||||
|
std::unique_ptr<EQ::Timer> ping_timer;
|
||||||
|
std::map<std::shared_ptr<websocket_connection>, std::unique_ptr<WebsocketServerConnection>> connections;
|
||||||
|
std::map<std::string, MethodHandlerEntry> methods;
|
||||||
|
websocket_server websocket_server;
|
||||||
|
LoginHandler login_handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
EQ::Net::WebsocketServer::WebsocketServer(const std::string &addr, int port)
|
||||||
|
{
|
||||||
|
_impl.reset(new Impl());
|
||||||
|
_impl->server.reset(new EQ::Net::TCPServer());
|
||||||
|
_impl->server->Listen(addr, port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
|
||||||
|
auto wsc = _impl->websocket_server.get_connection();
|
||||||
|
WebsocketServerConnection *c = new WebsocketServerConnection(this, connection, wsc);
|
||||||
|
_impl->connections.insert(std::make_pair(wsc, std::unique_ptr<WebsocketServerConnection>(c)));
|
||||||
|
});
|
||||||
|
|
||||||
|
_impl->websocket_server.set_write_handler(
|
||||||
|
[this](websocketpp::connection_hdl hdl, char const *data, size_t size) -> websocketpp::lib::error_code {
|
||||||
|
auto c = _impl->websocket_server.get_con_from_hdl(hdl);
|
||||||
|
auto iter = _impl->connections.find(c);
|
||||||
|
if (iter != _impl->connections.end()) {
|
||||||
|
iter->second->GetTCPConnection()->Write(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return websocketpp::lib::error_code();
|
||||||
|
});
|
||||||
|
|
||||||
|
_impl->ping_timer.reset(new EQ::Timer(5000, true, [this](EQ::Timer *t) {
|
||||||
|
auto iter = _impl->connections.begin();
|
||||||
|
|
||||||
|
while (iter != _impl->connections.end()) {
|
||||||
|
try {
|
||||||
|
auto &connection = iter->second;
|
||||||
|
connection->GetWebsocketConnection()->ping("keepalive");
|
||||||
|
}
|
||||||
|
catch (std::exception) {
|
||||||
|
iter->second->GetTCPConnection()->Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
iter++;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
_impl->methods.insert(std::make_pair("login", MethodHandlerEntry(std::bind(&WebsocketServer::Login, this, std::placeholders::_1, std::placeholders::_2), 0)));
|
||||||
|
_impl->methods.insert(std::make_pair("subscribe", MethodHandlerEntry(std::bind(&WebsocketServer::Subscribe, this, std::placeholders::_1, std::placeholders::_2), 0)));
|
||||||
|
_impl->methods.insert(std::make_pair("unsubscribe", MethodHandlerEntry(std::bind(&WebsocketServer::Unsubscribe, this, std::placeholders::_1, std::placeholders::_2), 0)));
|
||||||
|
_impl->login_handler = [](const WebsocketServerConnection* connection, const std::string& user, const std::string& pass) {
|
||||||
|
WebsocketLoginStatus ret;
|
||||||
|
ret.account_name = "admin";
|
||||||
|
|
||||||
|
if (connection->RemoteIP() == "127.0.0.1" || connection->RemoteIP() == "::") {
|
||||||
|
ret.logged_in = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.logged_in = false;
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
_impl->websocket_server.clear_access_channels(websocketpp::log::alevel::all);
|
||||||
|
}
|
||||||
|
|
||||||
|
EQ::Net::WebsocketServer::~WebsocketServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServer::ReleaseConnection(WebsocketServerConnection *connection)
|
||||||
|
{
|
||||||
|
//Clear any subscriptions here
|
||||||
|
|
||||||
|
_impl->connections.erase(connection->GetWebsocketConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value EQ::Net::WebsocketServer::HandleRequest(WebsocketServerConnection *connection, const std::string &method, const Json::Value ¶ms)
|
||||||
|
{
|
||||||
|
Json::Value err;
|
||||||
|
if (method != "login") {
|
||||||
|
if (!connection->IsAuthorized()) {
|
||||||
|
throw WebsocketException("Not logged in");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = _impl->methods.find(method);
|
||||||
|
if (iter != _impl->methods.end()) {
|
||||||
|
auto &s = iter->second;
|
||||||
|
if (s.status > connection->GetStatus()) {
|
||||||
|
throw WebsocketException("Status too low");
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.handler(connection, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw WebsocketException("Unknown Method");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServer::SetMethodHandler(const std::string &method, MethodHandler handler, int required_status)
|
||||||
|
{
|
||||||
|
//Reserved method names
|
||||||
|
if (method == "subscribe" ||
|
||||||
|
method == "unsubscribe" ||
|
||||||
|
method == "login") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_impl->methods.insert_or_assign(method, MethodHandlerEntry(handler, required_status));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServer::SetLoginHandler(LoginHandler handler)
|
||||||
|
{
|
||||||
|
_impl->login_handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServer::DispatchEvent(const std::string &evt, Json::Value data, int required_status)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Json::Value event_obj;
|
||||||
|
event_obj["type"] = "event";
|
||||||
|
event_obj["event"] = evt;
|
||||||
|
event_obj["data"] = data;
|
||||||
|
|
||||||
|
std::stringstream payload;
|
||||||
|
payload << event_obj;
|
||||||
|
|
||||||
|
for (auto &iter : _impl->connections) {
|
||||||
|
auto &c = iter.second;
|
||||||
|
|
||||||
|
//Might be better to get rid of subscriptions and just send everything and
|
||||||
|
//let the client sort out what they want idk
|
||||||
|
if (c->GetStatus() >= required_status && c->IsSubscribed(evt)) {
|
||||||
|
c->GetWebsocketConnection()->send(payload.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value EQ::Net::WebsocketServer::Login(WebsocketServerConnection *connection, const Json::Value ¶ms)
|
||||||
|
{
|
||||||
|
Json::Value ret;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Json::Value ret;
|
||||||
|
|
||||||
|
auto user = params[0].asString();
|
||||||
|
auto pass = params[1].asString();
|
||||||
|
|
||||||
|
auto r = _impl->login_handler(connection, user, pass);
|
||||||
|
|
||||||
|
if (r.logged_in) {
|
||||||
|
connection->SetAuthorized(true, r.account_name, r.account_id, 255);
|
||||||
|
ret["status"] = "Ok";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
connection->SetAuthorized(false, "", 0, 0);
|
||||||
|
ret["status"] = "Not Authorized";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (std::exception) {
|
||||||
|
throw WebsocketException("Unable to process login request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value EQ::Net::WebsocketServer::Subscribe(WebsocketServerConnection *connection, const Json::Value ¶ms)
|
||||||
|
{
|
||||||
|
Json::Value ret;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto evt = params[0].asString();
|
||||||
|
connection->Subscribe(evt);
|
||||||
|
ret["status"] = "Ok";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (std::exception) {
|
||||||
|
throw WebsocketException("Unable to process subscribe request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value EQ::Net::WebsocketServer::Unsubscribe(WebsocketServerConnection *connection, const Json::Value ¶ms)
|
||||||
|
{
|
||||||
|
Json::Value ret;
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto evt = params[0].asString();
|
||||||
|
connection->Unsubscribe(evt);
|
||||||
|
ret["status"] = "Ok";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
catch (std::exception) {
|
||||||
|
throw WebsocketException("Unable to process unsubscribe request");
|
||||||
|
}
|
||||||
|
}
|
||||||
63
common/net/websocket_server.h
Normal file
63
common/net/websocket_server.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "websocket_server_connection.h"
|
||||||
|
|
||||||
|
#include "../json/json.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
namespace EQ
|
||||||
|
{
|
||||||
|
namespace Net
|
||||||
|
{
|
||||||
|
struct WebsocketLoginStatus
|
||||||
|
{
|
||||||
|
bool logged_in;
|
||||||
|
std::string account_name;
|
||||||
|
uint32 account_id;
|
||||||
|
int status;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WebsocketException : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WebsocketException(const std::string &msg)
|
||||||
|
: _msg(msg.empty() ? "Unknown Error" : msg) { }
|
||||||
|
|
||||||
|
~WebsocketException() throw() {}
|
||||||
|
|
||||||
|
virtual char const *what() const throw() {
|
||||||
|
return _msg.c_str();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
const std::string _msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WebsocketServer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::function<Json::Value(WebsocketServerConnection*, const Json::Value&)> MethodHandler;
|
||||||
|
typedef std::function<WebsocketLoginStatus(WebsocketServerConnection*, const std::string&, const std::string&)> LoginHandler;
|
||||||
|
|
||||||
|
WebsocketServer(const std::string &addr, int port);
|
||||||
|
~WebsocketServer();
|
||||||
|
|
||||||
|
void SetMethodHandler(const std::string& method, MethodHandler handler, int required_status);
|
||||||
|
void SetLoginHandler(LoginHandler handler);
|
||||||
|
void DispatchEvent(const std::string& evt, Json::Value data = Json::Value(), int required_status = 0);
|
||||||
|
private:
|
||||||
|
void ReleaseConnection(WebsocketServerConnection *connection);
|
||||||
|
Json::Value HandleRequest(WebsocketServerConnection *connection, const std::string& method, const Json::Value ¶ms);
|
||||||
|
|
||||||
|
Json::Value Login(WebsocketServerConnection *connection, const Json::Value ¶ms);
|
||||||
|
Json::Value Subscribe(WebsocketServerConnection *connection, const Json::Value ¶ms);
|
||||||
|
Json::Value Unsubscribe(WebsocketServerConnection *connection, const Json::Value ¶ms);
|
||||||
|
|
||||||
|
struct Impl;
|
||||||
|
std::unique_ptr<Impl> _impl;
|
||||||
|
|
||||||
|
friend class WebsocketServerConnection;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
189
common/net/websocket_server_connection.cpp
Normal file
189
common/net/websocket_server_connection.cpp
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
#include "websocket_server_connection.h"
|
||||||
|
#include "websocket_server.h"
|
||||||
|
#include "../timer.h"
|
||||||
|
#include "../util/uuid.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
struct EQ::Net::WebsocketServerConnection::Impl {
|
||||||
|
WebsocketServer *parent;
|
||||||
|
std::shared_ptr<TCPConnection> connection;
|
||||||
|
std::shared_ptr<websocket_connection> ws_connection;
|
||||||
|
std::string id;
|
||||||
|
bool authorized;
|
||||||
|
std::string account_name;
|
||||||
|
uint32 account_id;
|
||||||
|
int status;
|
||||||
|
std::unordered_set<std::string> subscribed;
|
||||||
|
};
|
||||||
|
|
||||||
|
EQ::Net::WebsocketServerConnection::WebsocketServerConnection(WebsocketServer *parent,
|
||||||
|
std::shared_ptr<TCPConnection> connection,
|
||||||
|
std::shared_ptr<websocket_connection> ws_connection)
|
||||||
|
{
|
||||||
|
_impl.reset(new Impl());
|
||||||
|
_impl->parent = parent;
|
||||||
|
_impl->connection = connection;
|
||||||
|
_impl->id = EQ::Util::UUID::Generate().ToString();
|
||||||
|
_impl->authorized = false;
|
||||||
|
_impl->account_id = 0;
|
||||||
|
_impl->status = 0;
|
||||||
|
_impl->ws_connection = ws_connection;
|
||||||
|
_impl->ws_connection->set_message_handler(std::bind(&WebsocketServerConnection::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
_impl->ws_connection->start();
|
||||||
|
|
||||||
|
connection->OnDisconnect([this](EQ::Net::TCPConnection *connection) {
|
||||||
|
_impl->parent->ReleaseConnection(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection->OnRead([this](EQ::Net::TCPConnection *c, const unsigned char *buffer, size_t buffer_size) {
|
||||||
|
_impl->ws_connection->read_all((const char*)buffer, buffer_size);
|
||||||
|
});
|
||||||
|
|
||||||
|
connection->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
EQ::Net::WebsocketServerConnection::~WebsocketServerConnection()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string EQ::Net::WebsocketServerConnection::GetID() const
|
||||||
|
{
|
||||||
|
return _impl->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EQ::Net::WebsocketServerConnection::IsAuthorized() const
|
||||||
|
{
|
||||||
|
return _impl->authorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string EQ::Net::WebsocketServerConnection::GetAccountName() const
|
||||||
|
{
|
||||||
|
return _impl->account_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 EQ::Net::WebsocketServerConnection::GetAccountID() const
|
||||||
|
{
|
||||||
|
return _impl->account_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EQ::Net::WebsocketServerConnection::GetStatus() const
|
||||||
|
{
|
||||||
|
return _impl->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string EQ::Net::WebsocketServerConnection::RemoteIP() const
|
||||||
|
{
|
||||||
|
return _impl->connection->RemoteIP();
|
||||||
|
}
|
||||||
|
|
||||||
|
int EQ::Net::WebsocketServerConnection::RemotePort() const
|
||||||
|
{
|
||||||
|
return _impl->connection->RemotePort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<EQ::Net::websocket_connection> EQ::Net::WebsocketServerConnection::GetWebsocketConnection()
|
||||||
|
{
|
||||||
|
return _impl->ws_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<EQ::Net::TCPConnection> EQ::Net::WebsocketServerConnection::GetTCPConnection()
|
||||||
|
{
|
||||||
|
return _impl->connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServerConnection::OnMessage(websocketpp::connection_hdl hdl, websocket_message_ptr msg)
|
||||||
|
{
|
||||||
|
BenchTimer timer;
|
||||||
|
timer.reset();
|
||||||
|
|
||||||
|
if (msg->get_opcode() == websocketpp::frame::opcode::text) {
|
||||||
|
try {
|
||||||
|
auto &payload = msg->get_payload();
|
||||||
|
|
||||||
|
std::stringstream ss(payload);
|
||||||
|
Json::Value root;
|
||||||
|
|
||||||
|
ss >> root;
|
||||||
|
|
||||||
|
auto method = root["method"].asString();
|
||||||
|
auto params = root["params"];
|
||||||
|
std::string id = "";
|
||||||
|
|
||||||
|
auto idNode = root["id"];
|
||||||
|
if (!idNode.isNull() && idNode.isString()) {
|
||||||
|
id = idNode.asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
|
response["type"] = "method";
|
||||||
|
response["data"] = _impl->parent->HandleRequest(this, method, params);
|
||||||
|
response["method"] = method;
|
||||||
|
if(id != "") {
|
||||||
|
response["id"] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendResponse(response, timer.elapsed());
|
||||||
|
}
|
||||||
|
catch (std::exception &ex) {
|
||||||
|
Json::Value error;
|
||||||
|
error["type"] = "method";
|
||||||
|
error["error"] = fmt::format("{0}", ex.what());
|
||||||
|
SendResponse(error, timer.elapsed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServerConnection::SendResponse(const Json::Value &response, double time_elapsed)
|
||||||
|
{
|
||||||
|
Json::Value root = response;
|
||||||
|
root["execution_time"] = std::to_string(time_elapsed);
|
||||||
|
|
||||||
|
std::stringstream payload;
|
||||||
|
payload << root;
|
||||||
|
|
||||||
|
_impl->ws_connection->send(payload.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServerConnection::SetAuthorized(bool v, const std::string account_name, uint32 account_id, int status)
|
||||||
|
{
|
||||||
|
_impl->authorized = v;
|
||||||
|
_impl->account_name = account_name;
|
||||||
|
_impl->account_id = account_id;
|
||||||
|
_impl->status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServerConnection::Subscribe(const std::string &evt)
|
||||||
|
{
|
||||||
|
if (evt == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = _impl->subscribed.find(evt);
|
||||||
|
if (iter == _impl->subscribed.end()) {
|
||||||
|
_impl->subscribed.insert(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::Net::WebsocketServerConnection::Unsubscribe(const std::string &evt)
|
||||||
|
{
|
||||||
|
if (evt == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = _impl->subscribed.find(evt);
|
||||||
|
if (iter != _impl->subscribed.end()) {
|
||||||
|
_impl->subscribed.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EQ::Net::WebsocketServerConnection::IsSubscribed(const std::string &evt) const
|
||||||
|
{
|
||||||
|
auto iter = _impl->subscribed.find(evt);
|
||||||
|
if (iter != _impl->subscribed.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
49
common/net/websocket_server_connection.h
Normal file
49
common/net/websocket_server_connection.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tcp_server.h"
|
||||||
|
#include "../types.h"
|
||||||
|
#include "../json/json-forwards.h"
|
||||||
|
#include <websocketpp/config/core.hpp>
|
||||||
|
#include <websocketpp/server.hpp>
|
||||||
|
|
||||||
|
namespace EQ
|
||||||
|
{
|
||||||
|
namespace Net
|
||||||
|
{
|
||||||
|
typedef websocketpp::server<websocketpp::config::core> websocket_server;
|
||||||
|
typedef websocketpp::connection<websocketpp::config::core> websocket_connection;
|
||||||
|
typedef websocket_server::message_ptr websocket_message_ptr;
|
||||||
|
|
||||||
|
class WebsocketServer;
|
||||||
|
class WebsocketServerConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WebsocketServerConnection(WebsocketServer *parent,
|
||||||
|
std::shared_ptr<TCPConnection> connection,
|
||||||
|
std::shared_ptr<websocket_connection> ws_connection);
|
||||||
|
~WebsocketServerConnection();
|
||||||
|
|
||||||
|
std::string GetID() const;
|
||||||
|
bool IsAuthorized() const;
|
||||||
|
std::string GetAccountName() const;
|
||||||
|
uint32 GetAccountID() const;
|
||||||
|
int GetStatus() const;
|
||||||
|
std::string RemoteIP() const;
|
||||||
|
int RemotePort() const;
|
||||||
|
private:
|
||||||
|
std::shared_ptr<websocket_connection> GetWebsocketConnection();
|
||||||
|
std::shared_ptr<TCPConnection> GetTCPConnection();
|
||||||
|
void OnMessage(websocketpp::connection_hdl hdl, websocket_message_ptr msg);
|
||||||
|
void SendResponse(const Json::Value &response, double time_elapsed);
|
||||||
|
void SetAuthorized(bool v, const std::string account_name, uint32 account_id, int status);
|
||||||
|
void Subscribe(const std::string &evt);
|
||||||
|
void Unsubscribe(const std::string &evt);
|
||||||
|
bool IsSubscribed(const std::string &evt) const;
|
||||||
|
|
||||||
|
struct Impl;
|
||||||
|
std::unique_ptr<Impl> _impl;
|
||||||
|
|
||||||
|
friend class WebsocketServer;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
1
submodules/websocketpp
Submodule
1
submodules/websocketpp
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit c6d7e295bf5a0ab9b5f896720cc1a0e0fdc397a7
|
||||||
@ -5,8 +5,9 @@ SET(zone_sources
|
|||||||
aa_ability.cpp
|
aa_ability.cpp
|
||||||
aggro.cpp
|
aggro.cpp
|
||||||
aggromanager.cpp
|
aggromanager.cpp
|
||||||
aura.cpp
|
api_service.cpp
|
||||||
attack.cpp
|
attack.cpp
|
||||||
|
aura.cpp
|
||||||
beacon.cpp
|
beacon.cpp
|
||||||
bonuses.cpp
|
bonuses.cpp
|
||||||
bot.cpp
|
bot.cpp
|
||||||
@ -17,7 +18,6 @@ SET(zone_sources
|
|||||||
client_mods.cpp
|
client_mods.cpp
|
||||||
client_packet.cpp
|
client_packet.cpp
|
||||||
client_process.cpp
|
client_process.cpp
|
||||||
console.cpp
|
|
||||||
command.cpp
|
command.cpp
|
||||||
corpse.cpp
|
corpse.cpp
|
||||||
data_bucket.cpp
|
data_bucket.cpp
|
||||||
@ -29,7 +29,6 @@ SET(zone_sources
|
|||||||
embxs.cpp
|
embxs.cpp
|
||||||
encounter.cpp
|
encounter.cpp
|
||||||
entity.cpp
|
entity.cpp
|
||||||
eqemu_api_zone_data_service.cpp
|
|
||||||
exp.cpp
|
exp.cpp
|
||||||
fastmath.cpp
|
fastmath.cpp
|
||||||
fearpath.cpp
|
fearpath.cpp
|
||||||
@ -149,6 +148,7 @@ SET(zone_headers
|
|||||||
aa.h
|
aa.h
|
||||||
aa_ability.h
|
aa_ability.h
|
||||||
aggromanager.h
|
aggromanager.h
|
||||||
|
api_service.h
|
||||||
aura.h
|
aura.h
|
||||||
basic_functions.h
|
basic_functions.h
|
||||||
beacon.h
|
beacon.h
|
||||||
@ -160,7 +160,6 @@ SET(zone_headers
|
|||||||
client_packet.h
|
client_packet.h
|
||||||
command.h
|
command.h
|
||||||
common.h
|
common.h
|
||||||
console.h
|
|
||||||
corpse.h
|
corpse.h
|
||||||
data_bucket.h
|
data_bucket.h
|
||||||
doors.h
|
doors.h
|
||||||
@ -169,7 +168,6 @@ SET(zone_headers
|
|||||||
embxs.h
|
embxs.h
|
||||||
encounter.h
|
encounter.h
|
||||||
entity.h
|
entity.h
|
||||||
eqemu_api_zone_data_service.h
|
|
||||||
errmsg.h
|
errmsg.h
|
||||||
event_codes.h
|
event_codes.h
|
||||||
fastmath.h
|
fastmath.h
|
||||||
|
|||||||
@ -18,10 +18,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "../common/net/websocket_server.h"
|
||||||
|
#include "../common/eqemu_logsys.h"
|
||||||
|
#include "zonedb.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "entity.h"
|
#include "entity.h"
|
||||||
#include "corpse.h"
|
#include "corpse.h"
|
||||||
#include "eqemu_api_zone_data_service.h"
|
#include "api_service.h"
|
||||||
#include "npc.h"
|
#include "npc.h"
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include "zone.h"
|
#include "zone.h"
|
||||||
@ -30,8 +34,117 @@
|
|||||||
|
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
|
|
||||||
void callGetNpcListDetail(Json::Value &response)
|
EQ::Net::WebsocketLoginStatus CheckLogin(EQ::Net::WebsocketServerConnection *connection, const std::string &username, const std::string &password) {
|
||||||
{
|
EQ::Net::WebsocketLoginStatus ret;
|
||||||
|
ret.logged_in = false;
|
||||||
|
|
||||||
|
ret.account_id = database.CheckLogin(username.c_str(), password.c_str());
|
||||||
|
|
||||||
|
if (ret.account_id == 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char account_name[64];
|
||||||
|
database.GetAccountName(static_cast<uint32>(ret.account_id), account_name);
|
||||||
|
ret.account_name = account_name;
|
||||||
|
ret.logged_in = true;
|
||||||
|
ret.status = database.CheckStatus(ret.account_id);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value ApiGetPacketStatistics(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
|
auto &list = entity_list.GetClientList();
|
||||||
|
|
||||||
|
for (auto &iter : list) {
|
||||||
|
auto client = iter.second;
|
||||||
|
auto connection = client->Connection();
|
||||||
|
auto opts = connection->GetManager()->GetOptions();
|
||||||
|
auto eqs_stats = connection->GetStats();
|
||||||
|
auto &stats = eqs_stats.DaybreakStats;
|
||||||
|
auto now = EQ::Net::Clock::now();
|
||||||
|
auto sec_since_stats_reset = std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||||
|
now - stats.created
|
||||||
|
).count();
|
||||||
|
|
||||||
|
Json::Value row;
|
||||||
|
|
||||||
|
row["client_id"] = client->GetID();
|
||||||
|
row["client_name"] = client->GetCleanName();
|
||||||
|
row["seconds_since_reset"] = sec_since_stats_reset;
|
||||||
|
row["sent_bytes"] = stats.sent_bytes;
|
||||||
|
row["receive_bytes"] = stats.recv_bytes;
|
||||||
|
row["min_ping"] = stats.min_ping;
|
||||||
|
row["max_ping"] = stats.max_ping;
|
||||||
|
row["last_ping"] = stats.last_ping;
|
||||||
|
row["average_ping"] = stats.avg_ping;
|
||||||
|
row["realtime_receive_packets"] = stats.recv_packets;
|
||||||
|
row["realtime_sent_packets"] = stats.sent_packets;
|
||||||
|
row["sync_recv_packets"] = stats.sync_recv_packets;
|
||||||
|
row["sync_sent_packets"] = stats.sync_sent_packets;
|
||||||
|
row["sync_remote_recv_packets"] = stats.sync_remote_recv_packets;
|
||||||
|
row["sync_remote_sent_packets"] = stats.sync_remote_sent_packets;
|
||||||
|
row["packet_loss_in"] = (100.0 * (1.0 - static_cast<double>(stats.sync_recv_packets) /
|
||||||
|
static_cast<double>(stats.sync_remote_sent_packets)));
|
||||||
|
row["packet_loss_out"] = (100.0 * (1.0 - static_cast<double>(stats.sync_remote_recv_packets) /
|
||||||
|
static_cast<double>(stats.sync_sent_packets)));
|
||||||
|
row["resent_packets"] = stats.resent_packets;
|
||||||
|
row["resent_fragments"] = stats.resent_fragments;
|
||||||
|
row["resent_non_fragments"] = stats.resent_full;
|
||||||
|
row["dropped_datarate_packets"] = stats.dropped_datarate_packets;
|
||||||
|
|
||||||
|
Json::Value sent_packet_types;
|
||||||
|
|
||||||
|
for (auto i = 0; i < _maxEmuOpcode; ++i) {
|
||||||
|
auto count = eqs_stats.SentCount[i];
|
||||||
|
if (count > 0) {
|
||||||
|
sent_packet_types[OpcodeNames[i]] = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value receive_packet_types;
|
||||||
|
|
||||||
|
for (auto i = 0; i < _maxEmuOpcode; ++i) {
|
||||||
|
auto count = eqs_stats.RecvCount[i];
|
||||||
|
if (count > 0) {
|
||||||
|
receive_packet_types[OpcodeNames[i]] = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
row["sent_packet_types"] = sent_packet_types;
|
||||||
|
row["receive_packet_types"] = receive_packet_types;
|
||||||
|
|
||||||
|
response.append(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value ApiGetOpcodeList(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
|
for (auto i = 0; i < _maxEmuOpcode; ++i) {
|
||||||
|
Json::Value row = OpcodeNames[i];
|
||||||
|
|
||||||
|
response.append(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value ApiGetNpcListDetail(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
auto &list = entity_list.GetNPCList();
|
auto &list = entity_list.GetNPCList();
|
||||||
|
|
||||||
for (auto &iter : list) {
|
for (auto &iter : list) {
|
||||||
@ -115,10 +228,16 @@ void callGetNpcListDetail(Json::Value &response)
|
|||||||
|
|
||||||
response.append(row);
|
response.append(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void callGetDoorListDetail(Json::Value &response)
|
Json::Value ApiGetDoorListDetail(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
{
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
auto &door_list = entity_list.GetDoorsList();
|
auto &door_list = entity_list.GetDoorsList();
|
||||||
|
|
||||||
for (auto itr : door_list) {
|
for (auto itr : door_list) {
|
||||||
@ -152,10 +271,16 @@ void callGetDoorListDetail(Json::Value &response)
|
|||||||
|
|
||||||
response.append(row);
|
response.append(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void callGetCorpseListDetail(Json::Value &response)
|
Json::Value ApiGetCorpseListDetail(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
{
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
auto &corpse_list = entity_list.GetCorpseList();
|
auto &corpse_list = entity_list.GetCorpseList();
|
||||||
|
|
||||||
for (auto itr : corpse_list) {
|
for (auto itr : corpse_list) {
|
||||||
@ -185,10 +310,16 @@ void callGetCorpseListDetail(Json::Value &response)
|
|||||||
|
|
||||||
response.append(row);
|
response.append(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void callGetObjectListDetail(Json::Value &response)
|
Json::Value ApiGetObjectListDetail(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
{
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
auto &list = entity_list.GetObjectList();
|
auto &list = entity_list.GetObjectList();
|
||||||
|
|
||||||
for (auto &iter : list) {
|
for (auto &iter : list) {
|
||||||
@ -214,10 +345,16 @@ void callGetObjectListDetail(Json::Value &response)
|
|||||||
|
|
||||||
response.append(row);
|
response.append(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void callGetMobListDetail(Json::Value &response)
|
Json::Value ApiGetMobListDetail(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
{
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
auto &list = entity_list.GetMobList();
|
auto &list = entity_list.GetMobList();
|
||||||
|
|
||||||
for (auto &iter : list) {
|
for (auto &iter : list) {
|
||||||
@ -414,10 +551,16 @@ void callGetMobListDetail(Json::Value &response)
|
|||||||
|
|
||||||
response.append(row);
|
response.append(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void callGetClientListDetail(Json::Value &response)
|
Json::Value ApiGetClientListDetail(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
{
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
auto &list = entity_list.GetClientList();
|
auto &list = entity_list.GetClientList();
|
||||||
|
|
||||||
for (auto &iter : list) {
|
for (auto &iter : list) {
|
||||||
@ -607,10 +750,16 @@ void callGetClientListDetail(Json::Value &response)
|
|||||||
|
|
||||||
response.append(row);
|
response.append(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void callGetZoneAttributes(Json::Value &response)
|
Json::Value ApiGetZoneAttributes(EQ::Net::WebsocketServerConnection *connection, Json::Value params) {
|
||||||
{
|
if (zone->GetZoneID() == 0) {
|
||||||
|
throw EQ::Net::WebsocketException("Zone must be loaded to invoke this call");
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value response;
|
||||||
Json::Value row;
|
Json::Value row;
|
||||||
|
|
||||||
row["aggro_limit_reached"] = zone->AggroLimitReached();
|
row["aggro_limit_reached"] = zone->AggroLimitReached();
|
||||||
@ -648,129 +797,31 @@ void callGetZoneAttributes(Json::Value &response)
|
|||||||
row["zone_type"] = zone->GetZoneType();
|
row["zone_type"] = zone->GetZoneType();
|
||||||
|
|
||||||
response.append(row);
|
response.append(row);
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void callGetPacketStatistics(Json::Value &response)
|
void RegisterApiLogEvent(std::unique_ptr<EQ::Net::WebsocketServer> &server)
|
||||||
{
|
{
|
||||||
auto &list = entity_list.GetClientList();
|
LogSys.SetConsoleHandler([&](uint16 debug_level, uint16 log_category, const std::string &msg) {
|
||||||
|
Json::Value data;
|
||||||
for (auto &iter : list) {
|
data["debug_level"] = debug_level;
|
||||||
auto client = iter.second;
|
data["log_category"] = log_category;
|
||||||
auto connection = client->Connection();
|
data["msg"] = msg;
|
||||||
auto opts = connection->GetManager()->GetOptions();
|
server->DispatchEvent("log", data, 50);
|
||||||
auto eqs_stats = connection->GetStats();
|
});
|
||||||
auto &stats = eqs_stats.DaybreakStats;
|
|
||||||
auto now = EQ::Net::Clock::now();
|
|
||||||
auto sec_since_stats_reset = std::chrono::duration_cast<std::chrono::duration<double>>(
|
|
||||||
now - stats.created
|
|
||||||
).count();
|
|
||||||
|
|
||||||
Json::Value row;
|
|
||||||
|
|
||||||
row["client_id"] = client->GetID();
|
|
||||||
row["client_name"] = client->GetCleanName();
|
|
||||||
row["seconds_since_reset"] = sec_since_stats_reset;
|
|
||||||
row["sent_bytes"] = stats.sent_bytes;
|
|
||||||
row["receive_bytes"] = stats.recv_bytes;
|
|
||||||
row["min_ping"] = stats.min_ping;
|
|
||||||
row["max_ping"] = stats.max_ping;
|
|
||||||
row["last_ping"] = stats.last_ping;
|
|
||||||
row["average_ping"] = stats.avg_ping;
|
|
||||||
row["realtime_receive_packets"] = stats.recv_packets;
|
|
||||||
row["realtime_sent_packets"] = stats.sent_packets;
|
|
||||||
row["sync_recv_packets"] = stats.sync_recv_packets;
|
|
||||||
row["sync_sent_packets"] = stats.sync_sent_packets;
|
|
||||||
row["sync_remote_recv_packets"] = stats.sync_remote_recv_packets;
|
|
||||||
row["sync_remote_sent_packets"] = stats.sync_remote_sent_packets;
|
|
||||||
row["packet_loss_in"] = (100.0 * (1.0 - static_cast<double>(stats.sync_recv_packets) /
|
|
||||||
static_cast<double>(stats.sync_remote_sent_packets)));
|
|
||||||
row["packet_loss_out"] = (100.0 * (1.0 - static_cast<double>(stats.sync_remote_recv_packets) /
|
|
||||||
static_cast<double>(stats.sync_sent_packets)));
|
|
||||||
row["resent_packets"] = stats.resent_packets;
|
|
||||||
row["resent_fragments"] = stats.resent_fragments;
|
|
||||||
row["resent_non_fragments"] = stats.resent_full;
|
|
||||||
row["dropped_datarate_packets"] = stats.dropped_datarate_packets;
|
|
||||||
|
|
||||||
Json::Value sent_packet_types;
|
|
||||||
|
|
||||||
for (auto i = 0; i < _maxEmuOpcode; ++i) {
|
|
||||||
auto count = eqs_stats.SentCount[i];
|
|
||||||
if (count > 0) {
|
|
||||||
sent_packet_types[OpcodeNames[i]] = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value receive_packet_types;
|
|
||||||
|
|
||||||
for (auto i = 0; i < _maxEmuOpcode; ++i) {
|
|
||||||
auto count = eqs_stats.RecvCount[i];
|
|
||||||
if (count > 0) {
|
|
||||||
receive_packet_types[OpcodeNames[i]] = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
row["sent_packet_types"] = sent_packet_types;
|
|
||||||
row["receive_packet_types"] = receive_packet_types;
|
|
||||||
|
|
||||||
response.append(row);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RegisterApiService(std::unique_ptr<EQ::Net::WebsocketServer> &server) {
|
||||||
|
server->SetLoginHandler(CheckLogin);
|
||||||
|
server->SetMethodHandler("get_packet_statistics", &ApiGetPacketStatistics, 50);
|
||||||
|
server->SetMethodHandler("get_opcode_list", &ApiGetOpcodeList, 50);
|
||||||
|
server->SetMethodHandler("get_npc_list_detail", &ApiGetNpcListDetail, 50);
|
||||||
|
server->SetMethodHandler("get_door_list_detail", &ApiGetDoorListDetail, 50);
|
||||||
|
server->SetMethodHandler("get_corpse_list_detail", &ApiGetCorpseListDetail, 50);
|
||||||
|
server->SetMethodHandler("get_object_list_detail", &ApiGetObjectListDetail, 50);
|
||||||
|
server->SetMethodHandler("get_mob_list_detail", &ApiGetMobListDetail, 50);
|
||||||
|
server->SetMethodHandler("get_client_list_detail", &ApiGetClientListDetail, 50);
|
||||||
|
server->SetMethodHandler("get_zone_attributes", &ApiGetZoneAttributes, 50);
|
||||||
|
|
||||||
void callGetOpcodeList(Json::Value &response)
|
RegisterApiLogEvent(server);
|
||||||
{
|
|
||||||
for (auto i = 0; i < _maxEmuOpcode; ++i) {
|
|
||||||
Json::Value row = OpcodeNames[i];
|
|
||||||
|
|
||||||
response.append(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EQEmuApiZoneDataService::get(Json::Value &response, const std::vector<std::string> &args)
|
|
||||||
{
|
|
||||||
std::string method = args[0];
|
|
||||||
|
|
||||||
if (zone->GetZoneID() == 0) {
|
|
||||||
response["error"] = "Zone must be loaded to invoke calls";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Packet statistics
|
|
||||||
*/
|
|
||||||
if (method == "get_packet_statistics") {
|
|
||||||
callGetPacketStatistics(response);
|
|
||||||
}
|
|
||||||
if (method == "get_opcode_list") {
|
|
||||||
callGetOpcodeList(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List detail
|
|
||||||
*/
|
|
||||||
if (method == "get_npc_list_detail") {
|
|
||||||
callGetNpcListDetail(response);
|
|
||||||
}
|
|
||||||
if (method == "get_client_list_detail") {
|
|
||||||
callGetClientListDetail(response);
|
|
||||||
}
|
|
||||||
if (method == "get_mob_list_detail") {
|
|
||||||
callGetMobListDetail(response);
|
|
||||||
}
|
|
||||||
if (method == "get_door_list_detail") {
|
|
||||||
callGetDoorListDetail(response);
|
|
||||||
}
|
|
||||||
if (method == "get_corpse_list_detail") {
|
|
||||||
callGetCorpseListDetail(response);
|
|
||||||
}
|
|
||||||
if (method == "get_object_list_detail") {
|
|
||||||
callGetObjectListDetail(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Zone attributes
|
|
||||||
*/
|
|
||||||
if (method == "get_zone_attributes") {
|
|
||||||
callGetZoneAttributes(response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* EQEmulator: Everquest Server Emulator
|
* EQEmulator: Everquest Server Emulator
|
||||||
* Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server)
|
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../common/net/console_server.h"
|
#include <memory>
|
||||||
|
#include "../common/net/websocket_server.h"
|
||||||
|
|
||||||
void RegisterConsoleFunctions(std::unique_ptr<EQ::Net::ConsoleServer> &console);
|
void RegisterApiService(std::unique_ptr<EQ::Net::WebsocketServer> &server);
|
||||||
122
zone/console.cpp
122
zone/console.cpp
@ -1,122 +0,0 @@
|
|||||||
/**
|
|
||||||
* EQEmulator: Everquest Server Emulator
|
|
||||||
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
|
|
||||||
*
|
|
||||||
* 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; version 2 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY except by those people which sell it, which
|
|
||||||
* are required to give you total support for your newly bought product;
|
|
||||||
* 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, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "console.h"
|
|
||||||
#include "../common/string_util.h"
|
|
||||||
#include "../common/md5.h"
|
|
||||||
#include "../common/database.h"
|
|
||||||
#include "../common/json/json.h"
|
|
||||||
#include "zone.h"
|
|
||||||
#include "npc.h"
|
|
||||||
#include "entity.h"
|
|
||||||
#include "eqemu_api_zone_data_service.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param username
|
|
||||||
* @param password
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string &username, const std::string &password)
|
|
||||||
{
|
|
||||||
struct EQ::Net::ConsoleLoginStatus ret;
|
|
||||||
ret.account_id = database.CheckLogin(username.c_str(), password.c_str());
|
|
||||||
if (ret.account_id == 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
char account_name[64];
|
|
||||||
database.GetAccountName(static_cast<uint32>(ret.account_id), account_name);
|
|
||||||
|
|
||||||
ret.account_name = account_name;
|
|
||||||
ret.status = database.CheckStatus(static_cast<uint32>(ret.account_id));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param connection
|
|
||||||
* @param command
|
|
||||||
* @param args
|
|
||||||
*/
|
|
||||||
void ConsoleApi(
|
|
||||||
EQ::Net::ConsoleServerConnection *connection,
|
|
||||||
const std::string &command,
|
|
||||||
const std::vector<std::string> &args
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Json::Value root;
|
|
||||||
Json::Value response;
|
|
||||||
|
|
||||||
BenchTimer timer;
|
|
||||||
timer.reset();
|
|
||||||
|
|
||||||
EQEmuApiZoneDataService::get(response, args);
|
|
||||||
|
|
||||||
std::string method = args[0];
|
|
||||||
|
|
||||||
root["execution_time"] = std::to_string(timer.elapsed());
|
|
||||||
root["method"] = method;
|
|
||||||
root["data"] = response;
|
|
||||||
|
|
||||||
std::stringstream payload;
|
|
||||||
payload << root;
|
|
||||||
|
|
||||||
connection->SendLine(payload.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param connection
|
|
||||||
* @param command
|
|
||||||
* @param args
|
|
||||||
*/
|
|
||||||
void ConsoleNull(
|
|
||||||
EQ::Net::ConsoleServerConnection *connection,
|
|
||||||
const std::string &command,
|
|
||||||
const std::vector<std::string> &args
|
|
||||||
)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param connection
|
|
||||||
* @param command
|
|
||||||
* @param args
|
|
||||||
*/
|
|
||||||
void ConsoleQuit(
|
|
||||||
EQ::Net::ConsoleServerConnection *connection,
|
|
||||||
const std::string &command,
|
|
||||||
const std::vector<std::string> &args
|
|
||||||
)
|
|
||||||
{
|
|
||||||
connection->SendLine("Exiting...");
|
|
||||||
connection->Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param console
|
|
||||||
*/
|
|
||||||
void RegisterConsoleFunctions(std::unique_ptr<EQ::Net::ConsoleServer> &console)
|
|
||||||
{
|
|
||||||
console->RegisterLogin(std::bind(CheckLogin, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
console->RegisterCall("api", 200, "api", std::bind(ConsoleApi, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
||||||
console->RegisterCall("ping", 50, "ping", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
||||||
console->RegisterCall("quit", 50, "quit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
||||||
console->RegisterCall("exit", 50, "exit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* EQEmulator: Everquest Server Emulator
|
|
||||||
* Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server)
|
|
||||||
*
|
|
||||||
* 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; version 2 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY except by those people which sell it, which
|
|
||||||
* are required to give you total support for your newly bought product;
|
|
||||||
* 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, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef EQEMU_API_ZONE_DATA_SERVICE_H
|
|
||||||
#define EQEMU_API_ZONE_DATA_SERVICE_H
|
|
||||||
|
|
||||||
#include "../common/json/json.h"
|
|
||||||
|
|
||||||
class EQEmuApiZoneDataService {
|
|
||||||
public:
|
|
||||||
static void get(Json::Value &response, const std::vector<std::string> &args);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //EQEMU_API_ZONE_DATA_SERVICE_H
|
|
||||||
14
zone/net.cpp
14
zone/net.cpp
@ -43,8 +43,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/spdat.h"
|
#include "../common/spdat.h"
|
||||||
#include "../common/eqemu_logsys.h"
|
#include "../common/eqemu_logsys.h"
|
||||||
#include "../common/eqemu_logsys_fmt.h"
|
#include "../common/eqemu_logsys_fmt.h"
|
||||||
#include "../common/net/console_server.h"
|
|
||||||
|
|
||||||
|
#include "api_service.h"
|
||||||
#include "zone_config.h"
|
#include "zone_config.h"
|
||||||
#include "masterentity.h"
|
#include "masterentity.h"
|
||||||
#include "worldserver.h"
|
#include "worldserver.h"
|
||||||
@ -93,9 +93,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#else
|
#else
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "../common/unix.h"
|
#include "../common/unix.h"
|
||||||
#include "../common/net/console_server.h"
|
|
||||||
#include "console.h"
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
volatile bool RunLoops = true;
|
volatile bool RunLoops = true;
|
||||||
@ -121,7 +118,6 @@ double frame_time = 0.0;
|
|||||||
|
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
extern void MapOpcodes();
|
extern void MapOpcodes();
|
||||||
void RegisterConsoleFunctions(std::unique_ptr<EQ::Net::ConsoleServer> &console);
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
RegisterExecutablePlatform(ExePlatformZone);
|
RegisterExecutablePlatform(ExePlatformZone);
|
||||||
@ -255,7 +251,7 @@ int main(int argc, char** argv) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Register Log System and Settings */
|
/* Register Log System and Settings */
|
||||||
LogSys.OnLogHookCallBackZone(&Zone::GMSayHookCallBackProcess);
|
LogSys.SetGMSayHandler(&Zone::GMSayHookCallBackProcess);
|
||||||
database.LoadLogSettings(LogSys.log_settings);
|
database.LoadLogSettings(LogSys.log_settings);
|
||||||
LogSys.StartFileLogs();
|
LogSys.StartFileLogs();
|
||||||
|
|
||||||
@ -463,7 +459,7 @@ int main(int argc, char** argv) {
|
|||||||
EQStreamInterface *eqsi;
|
EQStreamInterface *eqsi;
|
||||||
std::unique_ptr<EQ::Net::EQStreamManager> eqsm;
|
std::unique_ptr<EQ::Net::EQStreamManager> eqsm;
|
||||||
std::chrono::time_point<std::chrono::system_clock> frame_prev = std::chrono::system_clock::now();
|
std::chrono::time_point<std::chrono::system_clock> frame_prev = std::chrono::system_clock::now();
|
||||||
std::unique_ptr<EQ::Net::ConsoleServer> console;
|
std::unique_ptr<EQ::Net::WebsocketServer> ws_server;
|
||||||
|
|
||||||
auto loop_fn = [&](EQ::Timer* t) {
|
auto loop_fn = [&](EQ::Timer* t) {
|
||||||
//Advance the timer to our current point in time
|
//Advance the timer to our current point in time
|
||||||
@ -488,8 +484,8 @@ int main(int argc, char** argv) {
|
|||||||
Config->TelnetIP.c_str(),
|
Config->TelnetIP.c_str(),
|
||||||
Config->ZonePort
|
Config->ZonePort
|
||||||
);
|
);
|
||||||
console.reset(new EQ::Net::ConsoleServer(Config->TelnetIP, Config->ZonePort));
|
ws_server.reset(new EQ::Net::WebsocketServer(Config->TelnetIP, Config->ZonePort));
|
||||||
RegisterConsoleFunctions(console);
|
RegisterApiService(ws_server);
|
||||||
telnet_server_opened = true;
|
telnet_server_opened = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user