summaryrefslogtreecommitdiff
path: root/src/account-server/main-account.cpp
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <bjorn@lindeijer.nl>2023-04-21 11:36:47 +0200
committerThorbjørn Lindeijer <bjorn@lindeijer.nl>2023-05-05 16:03:23 +0000
commitee4624e7904f37c411fc7215d7c41d7a1b6b7d4f (patch)
tree0af36a4da03b7372dc34f9b08f7ee5ed958da90b /src/account-server/main-account.cpp
parentd9ae7419ab16d6e91469fcdc6a89dbc3afc5a030 (diff)
downloadmanaserv-ee4624e7904f37c411fc7215d7c41d7a1b6b7d4f.tar.gz
manaserv-ee4624e7904f37c411fc7215d7c41d7a1b6b7d4f.tar.bz2
manaserv-ee4624e7904f37c411fc7215d7c41d7a1b6b7d4f.tar.xz
manaserv-ee4624e7904f37c411fc7215d7c41d7a1b6b7d4f.zip
Added support for logging in with Stellar
* Added PAMSG_STELLAR_LOGIN / PAMSG_STELLAR_LOGIN_RESPONSE, which is used by the client to request a login token that can be signed using a Stellar wallet. * Added uWebSockets dependency, used to listen for a separate server that verifies signed tokens (the Stellar Bridge). When a token is verified, it is sent to manaserv-account, which then sends a APMSG_LOGIN_RESPONSE to the client matching the token. * Added RapidJSON dependency to parse the JSON WebSocket messages. * To keep everything in a single thread, uWebSockets is now driving the event loop. Processing of ENet hosts, writing stats and expired bans have been moved to uSocket timers. * C++ standard updated to C++17, as required by uWebSockets. When Stellar is used to login, the public key is used as the username. It might be better to introduce an explicit field for this, especially when we want to enable an account to feature both Stellar login as well as login with username / password.
Diffstat (limited to 'src/account-server/main-account.cpp')
-rw-r--r--src/account-server/main-account.cpp131
1 files changed, 105 insertions, 26 deletions
diff --git a/src/account-server/main-account.cpp b/src/account-server/main-account.cpp
index 73382f23..4d5bf02a 100644
--- a/src/account-server/main-account.cpp
+++ b/src/account-server/main-account.cpp
@@ -45,7 +45,6 @@
#include "utils/processorutils.h"
#include "utils/stringfilter.h"
#include "utils/time.h"
-#include "utils/timer.h"
#include <cstdlib>
#include <getopt.h>
@@ -54,6 +53,8 @@
#include <fstream>
#include <physfs.h>
#include <enet/enet.h>
+#include <rapidjson/document.h>
+#include <App.h>
using utils::Logger;
@@ -185,20 +186,26 @@ static void deinitializeServer()
/**
* Dumps statistics.
*/
-static void dumpStatistics(std::string accountAddress, int accountClientPort,
- int accountGamePort, int chatClientPort)
+static void dumpStatistics()
{
- std::ofstream os(statisticsFile.c_str());
+ std::ofstream os(statisticsFile);
+
os << "<statistics>\n";
+
// Print last heartbeat
os << "<heartbeat=\"" << utils::getCurrentDate() << "_"
- << utils::getCurrentTime() << "\" />\n";
+ << utils::getCurrentTime() << "\" />\n";
+
// Add account server information
- os << "<accountserver address=\"" << accountAddress << "\" clientport=\""
- << accountClientPort << "\" gameport=\"" << accountGamePort
- << "\" chatclientport=\"" << chatClientPort << "\" />\n";
+ auto accountHandler = AccountClientHandler::getConnectionHandler();
+ auto gameHandler = GameServerHandler::getConnectionHandler();
+ os << "<accountserver address=\"" << accountHandler->getHostname() << "\" clientport=\""
+ << accountHandler->getPort() << "\" gameport=\"" << gameHandler->getPort()
+ << "\" chatclientport=\"" << chatHandler->getPort() << "\" />\n";
+
// Add game servers information
GameServerHandler::dumpStatistics(os);
+
os << "</statistics>\n";
}
@@ -333,6 +340,8 @@ int main(int argc, char *argv[])
"localhost");
std::string accountListenToGameHost = Configuration::getValue("net_accountListenToGameHost",
accountHost);
+ std::string accountListenToStellarBridgeHost = Configuration::getValue("net_accountListenToStellarBridgeHost",
+ accountListenToGameHost);
// We separate the chat host as the chat server will be separated out
// from the account server.
@@ -350,6 +359,8 @@ int main(int argc, char *argv[])
options.port + 1);
int chatClientPort = Configuration::getValue("net_chatListenToClientPort",
options.port + 2);
+ int accountStellarBridgePort = Configuration::getValue("net_accountListenToStellarBridgePort",
+ options.port + 4);
bool debugNetwork = Configuration::getBoolValue("net_debugMode", false);
MessageOut::setDebugModeEnabled(debugNetwork);
@@ -363,13 +374,78 @@ int main(int argc, char *argv[])
return EXIT_NET_EXCEPTION;
}
- // Dump statistics every 10 seconds.
- utils::Timer statTimer(10000);
- // Check for expired bans every 30 seconds
- utils::Timer banTimer(30000);
-
- statTimer.start();
- banTimer.start();
+ struct us_loop_t *loop = (struct us_loop_t *) uWS::Loop::get();
+ struct us_timer_t *processTimer = us_create_timer(loop, 0, 0);
+ struct us_timer_t *statTimer = us_create_timer(loop, 0, 0);
+ struct us_timer_t *banTimer = us_create_timer(loop, 0, 0);
+
+ constexpr int processTimerIntervalMs = 1000 / 20; // 20 times per second
+ constexpr int statTimerIntervalMs = 10 * 1000; // dump statistics every 10 seconds
+ constexpr int banTimerIntervalMs = 30 * 1000; // check bans every 30 seconds
+
+ us_timer_set(processTimer, [](struct us_timer_t *) {
+ AccountClientHandler::getConnectionHandler()->process();
+ GameServerHandler::getConnectionHandler()->process();
+ chatHandler->process();
+ }, processTimerIntervalMs, processTimerIntervalMs);
+
+ us_timer_set(statTimer, [](struct us_timer_t *) {
+ dumpStatistics();
+ }, statTimerIntervalMs, statTimerIntervalMs);
+
+ us_timer_set(banTimer, [](struct us_timer_t *) {
+ storage->checkBannedAccounts();
+ }, banTimerIntervalMs, banTimerIntervalMs);
+
+ uWS::App wsApp;
+
+ struct PerSocketData {};
+ wsApp.ws<PerSocketData>("/stellar-auth", {
+ .maxPayloadLength = 1024, // we only expect small messages
+ .open = [](auto *ws) {
+ std::cout << "WebSocket opened " << ws->getRemoteAddressAsText() << std::endl;
+ },
+ .message = [](auto *ws, std::string_view message, uWS::OpCode /*opCode*/) {
+ rapidjson::Document d;
+ d.Parse(message.data(), message.size());
+ if (d.HasParseError()) {
+ LOG_ERROR("WebSocket: received JSON parse error");
+ return;
+ }
+ if (!d.IsObject()) {
+ std::cout << "not an object" << std::endl;
+ LOG_ERROR("WebSocket: received JSON not an object");
+ return;
+ }
+
+ const auto pubkeyMember = d.FindMember("pubkey");
+ const auto tokenMember = d.FindMember("token");
+
+ if (pubkeyMember == d.MemberEnd() || tokenMember == d.MemberEnd()) {
+ LOG_ERROR("WebSocket: received JSON missing pubkey or token");
+ return;
+ }
+ if (!pubkeyMember->value.IsString() || !tokenMember->value.IsString()) {
+ LOG_ERROR("WebSocket: received JSON pubkey or token not a string");
+ return;
+ }
+
+ const std::string pubkey = pubkeyMember->value.GetString();
+ const std::string token = tokenMember->value.GetString();
+
+ AccountClientHandler::handleStellarLogin(token, pubkey);
+ },
+ .close = [](auto *ws, int code, std::string_view message) {
+ std::cout << "WebSocket closed " << ws->getRemoteAddressAsText() << " with code " << code << ": " << message << std::endl;
+ }
+ }).listen(accountListenToStellarBridgeHost, accountStellarBridgePort, [](us_listen_socket_t *listen_socket) {
+ if (listen_socket) {
+ /* Note that us_listen_socket_t is castable to us_socket_t */
+ std::cout << "WebSocket: Thread " << std::this_thread::get_id() << " listening on port " << us_socket_local_port(1, (struct us_socket_t *) listen_socket) << std::endl;
+ } else {
+ std::cout << "WebSocket: Thread " << std::this_thread::get_id() << " failed to listen on port 3000" << std::endl;
+ }
+ });
// Write startup time to database as system world state variable
std::stringstream timestamp;
@@ -377,19 +453,22 @@ int main(int argc, char *argv[])
storage->setWorldStateVar("accountserver_startup", timestamp.str(),
Storage::SystemMap);
- while (running)
- {
- AccountClientHandler::process();
- GameServerHandler::process();
- chatHandler->process(50);
+ // Start a thread to close the sockets and timers when running is set to false
+ std::thread([&wsApp,processTimer,statTimer,banTimer] {
+ while (running) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
- if (statTimer.poll())
- dumpStatistics(accountHost, options.port, accountGamePort,
- chatClientPort);
+ LOG_INFO("Closing uWebSockets app...");
+ wsApp.close();
- if (banTimer.poll())
- storage->checkBannedAccounts();
- }
+ LOG_INFO("Closing uSockets timers...");
+ us_timer_close(processTimer);
+ us_timer_close(statTimer);
+ us_timer_close(banTimer);
+ }).detach();
+
+ wsApp.run();
LOG_INFO("Received: Quit signal, closing down...");
chatHandler->stopListen();