diff options
author | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2023-04-21 11:36:47 +0200 |
---|---|---|
committer | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2023-05-05 16:03:23 +0000 |
commit | ee4624e7904f37c411fc7215d7c41d7a1b6b7d4f (patch) | |
tree | 0af36a4da03b7372dc34f9b08f7ee5ed958da90b /src | |
parent | d9ae7419ab16d6e91469fcdc6a89dbc3afc5a030 (diff) | |
download | manaserv-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')
-rw-r--r-- | src/CMakeLists.txt | 11 | ||||
-rw-r--r-- | src/account-server/accountclient.h | 1 | ||||
-rw-r--r-- | src/account-server/accounthandler.cpp | 186 | ||||
-rw-r--r-- | src/account-server/accounthandler.h | 10 | ||||
-rw-r--r-- | src/account-server/main-account.cpp | 131 | ||||
-rw-r--r-- | src/account-server/serverhandler.cpp | 5 | ||||
-rw-r--r-- | src/account-server/serverhandler.h | 5 | ||||
-rw-r--r-- | src/common/manaserv_protocol.h | 2 | ||||
-rw-r--r-- | src/net/connectionhandler.cpp | 21 | ||||
-rw-r--r-- | src/net/connectionhandler.h | 23 |
10 files changed, 323 insertions, 72 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b7e7c48a..26658271 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ find_package(LibXml2 REQUIRED) find_package(PhysFS REQUIRED) find_package(ZLIB REQUIRED) find_package(SigC++ REQUIRED) +find_package(RapidJSON REQUIRED) if(CMAKE_COMPILER_IS_GNUCXX) # Help getting compilation warnings @@ -46,7 +47,7 @@ set(FLAGS "${FLAGS} -DPACKAGE_VERSION=\\\"${VERSION}\\\"") set(FLAGS "${FLAGS} -DPKG_DATADIR=\\\"${PKG_DATADIR}/\\\"") set(FLAGS "${FLAGS} -DLOCALEDIR=\\\"${LOCALEDIR}/\\\"") -set(FLAGS "${FLAGS} -std=c++0x") +set(FLAGS "${FLAGS} -std=c++17") set(FLAGS "${FLAGS} -Woverloaded-virtual") # Prevent clashes with icu namespace @@ -312,6 +313,14 @@ set(PROGRAMS manaserv-account manaserv-game) add_executable(manaserv-game WIN32 ${SRCS} ${SRCS_MANASERVGAME}) add_executable(manaserv-account WIN32 ${SRCS} ${SRCS_MANASERVACCOUNT}) +target_include_directories(manaserv-account SYSTEM PRIVATE + ${CMAKE_SOURCE_DIR}/libs/uWebSockets/src + ${CMAKE_SOURCE_DIR}/libs/uWebSockets/uSockets/src + ${RAPIDJSON_INCLUDE_DIRS}) + +target_link_libraries(manaserv-account + ${CMAKE_SOURCE_DIR}/libs/uWebSockets/uSockets/uSockets.a) + foreach(program ${PROGRAMS}) target_link_libraries( ${program} diff --git a/src/account-server/accountclient.h b/src/account-server/accountclient.h index 3973d60b..5ccb0f1d 100644 --- a/src/account-server/accountclient.h +++ b/src/account-server/accountclient.h @@ -51,6 +51,7 @@ class AccountClient : public NetComputer AccountClientStatus status; int version; + std::string stellarToken; private: std::unique_ptr<Account> mAccount; diff --git a/src/account-server/accounthandler.cpp b/src/account-server/accounthandler.cpp index 2275e015..b13b4580 100644 --- a/src/account-server/accounthandler.cpp +++ b/src/account-server/accounthandler.cpp @@ -40,7 +40,6 @@ #include "utils/tokencollector.h" #include "utils/tokendispenser.h" #include "utils/sha256.h" -#include "utils/string.h" #include "utils/xml.h" using namespace ManaServ; @@ -68,6 +67,10 @@ public: void deletePendingConnect(int) {} /** + */ + void handleStellarLogin(const std::string &token, const std::string &pubKey); + + /** * Token collector for connecting a client coming from a game server * without having to provide username and password a second time. */ @@ -85,6 +88,7 @@ protected: private: void handleLoginRandTriggerMessage(AccountClient &client, MessageIn &msg); + void handleStellarLoginMessage(AccountClient &client, MessageIn &msg); void handleLoginMessage(AccountClient &client, MessageIn &msg); void handleLogoutMessage(AccountClient &client); void handleReconnectMessage(AccountClient &client, MessageIn &msg); @@ -97,7 +101,10 @@ private: void handleCharacterSelectMessage(AccountClient &client, MessageIn &msg); void handleCharacterDeleteMessage(AccountClient &client, MessageIn &msg); - void addServerInfo(MessageOut *msg); + std::unique_ptr<Account> createAccount(const std::string &username, + const std::string &password, + const std::string &email); + void addServerInfo(MessageOut &msg); /** List of all accounts which requested a random seed, but are not logged * yet. This list will be regularly remove (after timeout) old accounts @@ -239,9 +246,9 @@ void AccountClientHandler::deinitialize() accountHandler = nullptr; } -void AccountClientHandler::process() +ConnectionHandler *AccountClientHandler::getConnectionHandler() { - accountHandler->process(50); + return accountHandler; } void AccountClientHandler::prepareReconnect(const std::string &token, int id) @@ -249,6 +256,12 @@ void AccountClientHandler::prepareReconnect(const std::string &token, int id) accountHandler->mTokenCollector.addPendingConnect(token, id); } +void AccountClientHandler::handleStellarLogin(const std::string &token, const std::string &pubKey) +{ + accountHandler->handleStellarLogin(token, pubKey); +} + + NetComputer *AccountHandler::computerConnected(ENetPeer *peer) { return new AccountClient(peer); @@ -306,7 +319,7 @@ static void sendFullCharacterData(AccountClient *client, client->send(msg); } -static std::string getRandomString(int length) +static std::string getRandomData(int length) { std::string s; s.resize(length); @@ -318,9 +331,31 @@ static std::string getRandomString(int length) return s; } +/** + * Generates a random string containing lowercase and uppercase characters as + * well as numbers. + */ +static std::string getRandomString(int length) +{ + std::string s; + s.resize(length); + for (int i = 0; i < length; ++i) + { + int r = rand() % 62; + if (r < 10) + s[i] = '0' + r; + else if (r < 36) + s[i] = 'a' + r - 10; + else + s[i] = 'A' + r - 36; + } + + return s; +} + void AccountHandler::handleLoginRandTriggerMessage(AccountClient &client, MessageIn &msg) { - std::string salt = getRandomString(4); + std::string salt = getRandomData(4); std::string username = msg.readString(); if (auto acc = storage->getAccount(username)) @@ -333,6 +368,34 @@ void AccountHandler::handleLoginRandTriggerMessage(AccountClient &client, Messag client.send(reply); } +void AccountHandler::handleStellarLoginMessage(AccountClient &client, MessageIn &msg) +{ + client.version = msg.readInt32(); + + MessageOut reply(APMSG_STELLAR_LOGIN_RESPONSE); + + if (client.status != CLIENT_LOGIN) + { + reply.writeInt8(ERRMSG_FAILURE); + client.send(reply); + return; + } + + if (client.version < MIN_PROTOCOL_VERSION) + { + reply.writeInt8(LOGIN_INVALID_VERSION); + client.send(reply); + return; + } + + client.stellarToken = getRandomString(8); + + reply.writeInt8(ERRMSG_OK); + reply.writeString(client.stellarToken); + + client.send(reply); +} + void AccountHandler::handleLoginMessage(AccountClient &client, MessageIn &msg) { MessageOut reply(APMSG_LOGIN_RESPONSE); @@ -425,7 +488,7 @@ void AccountHandler::handleLoginMessage(AccountClient &client, MessageIn &msg) storage->updateLastLogin(*acc); reply.writeInt8(ERRMSG_OK); - addServerInfo(&reply); + addServerInfo(reply); const Characters &chars = acc->getCharacters(); @@ -534,24 +597,14 @@ void AccountHandler::handleRegisterMessage(AccountClient &client, } else { - std::unique_ptr<Account> acc { new Account }; - acc->setName(username); - acc->setPassword(sha256(password)); - // We hash email server-side for additional privacy - // we ask for it again when we need it and verify it - // through comparing it with the hash. - acc->setEmail(sha256(email)); - acc->setLevel(AL_PLAYER); - - // Set the date and time of the account registration, and the last login - time_t regdate; - time(®date); - acc->setRegistrationDate(regdate); - acc->setLastLogin(regdate); - - storage->addAccount(*acc); + // We hash email server-side for additional privacy we ask for it again + // when we need it and verify it through comparing it with the hash. + std::unique_ptr<Account> acc = createAccount(username, + sha256(password), + sha256(email)); + reply.writeInt8(ERRMSG_OK); - addServerInfo(&reply); + addServerInfo(reply); // Associate account with connection client.setAccount(std::move(acc)); @@ -960,6 +1013,26 @@ void AccountHandler::handleCharacterDeleteMessage(AccountClient &client, client.send(reply); } +std::unique_ptr<Account> AccountHandler::createAccount(const std::string &username, + const std::string &password, + const std::string &email) +{ + std::unique_ptr<Account> acc { new Account }; + acc->setName(username); + acc->setPassword(password); + acc->setEmail(email); + acc->setLevel(AL_PLAYER); + + // Set the date and time of the account registration, and the last login + time_t regdate; + time(®date); + acc->setRegistrationDate(regdate); + acc->setLastLogin(regdate); + + storage->addAccount(*acc); + return acc; +} + /** * Adds server specific info to the current message * @@ -968,11 +1041,11 @@ void AccountHandler::handleCharacterDeleteMessage(AccountClient &client, * (String) Client Data URL (or "") * (Byte) Number of maximum character slots (empty or not) */ -void AccountHandler::addServerInfo(MessageOut *msg) +void AccountHandler::addServerInfo(MessageOut &msg) { - msg->writeString(mUpdateHost); - msg->writeString(mDataUrl); - msg->writeInt8(mMaxCharacters); + msg.writeString(mUpdateHost); + msg.writeString(mDataUrl); + msg.writeInt8(mMaxCharacters); } void AccountHandler::tokenMatched(AccountClient *client, int accountID) @@ -1001,6 +1074,58 @@ void AccountHandler::deletePendingClient(AccountClient *client) // The client will be deleted when the disconnect event is processed } +void AccountHandler::handleStellarLogin(const std::string &token, const std::string &pubKey) +{ + auto it = std::find_if(clients.begin(), clients.end(), + [token](NetComputer *client) -> bool + { + auto c = static_cast<AccountClient *>(client); + return c->stellarToken == token; + }); + + if (it == clients.end()) + { + LOG_DEBUG("Stellar login: No client with token " << token); + return; + } + + auto client = static_cast<AccountClient*>(*it); + auto acc = storage->getAccount(pubKey); + if (acc) + { + // Set lastLogin date of the account. + time_t login; + time(&login); + acc->setLastLogin(login); + storage->updateLastLogin(*acc); + } + else if (!mRegistrationAllowed) + { + MessageOut reply(APMSG_REGISTER_RESPONSE); + reply.writeInt8(ERRMSG_FAILURE); + client->send(reply); + return; + } + else + { + // On-demand account creation for public keys + acc = createAccount(pubKey, std::string(), std::string()); + } + + MessageOut reply(APMSG_LOGIN_RESPONSE); + reply.writeInt8(ERRMSG_OK); + addServerInfo(reply); + + for (auto &charIt : acc->getCharacters()) + sendCharacterData(reply, charIt.second); + + client->send(reply); + + // Associate account with connection. + client->setAccount(std::move(acc)); + client->status = CLIENT_CONNECTED; +} + void AccountHandler::processMessage(NetComputer *comp, MessageIn &message) { AccountClient &client = *static_cast< AccountClient * >(comp); @@ -1017,6 +1142,11 @@ void AccountHandler::processMessage(NetComputer *comp, MessageIn &message) handleLoginMessage(client, message); break; + case PAMSG_STELLAR_LOGIN: + LOG_DEBUG("Received msg ... PAMSG_STELLAR_LOGIN"); + handleStellarLoginMessage(client, message); + break; + case PAMSG_LOGOUT: LOG_DEBUG("Received msg ... PAMSG_LOGOUT"); handleLogoutMessage(client); diff --git a/src/account-server/accounthandler.h b/src/account-server/accounthandler.h index f1ffcfe2..c27b00ed 100644 --- a/src/account-server/accounthandler.h +++ b/src/account-server/accounthandler.h @@ -23,6 +23,8 @@ #include <string> +class ConnectionHandler; + namespace AccountClientHandler { /** @@ -37,14 +39,18 @@ namespace AccountClientHandler void deinitialize(); /** + * Returns the connection handler. + */ + ConnectionHandler *getConnectionHandler(); + + /** * Prepares a connection for a client coming from a game server. */ void prepareReconnect(const std::string &token, int accountID); /** - * Processes messages received by the connection handler. */ - void process(); + void handleStellarLogin(const std::string &token, const std::string &pubKey); } #endif // ACCOUNTHANDLER_H 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(); diff --git a/src/account-server/serverhandler.cpp b/src/account-server/serverhandler.cpp index cc53eace..54754546 100644 --- a/src/account-server/serverhandler.cpp +++ b/src/account-server/serverhandler.cpp @@ -109,11 +109,12 @@ void GameServerHandler::deinitialize() { serverHandler->stopListen(); delete serverHandler; + serverHandler = nullptr; } -void GameServerHandler::process() +ConnectionHandler *GameServerHandler::getConnectionHandler() { - serverHandler->process(50); + return serverHandler; } NetComputer *ServerHandler::computerConnected(ENetPeer *peer) diff --git a/src/account-server/serverhandler.h b/src/account-server/serverhandler.h index 6f4c61fe..916a0224 100644 --- a/src/account-server/serverhandler.h +++ b/src/account-server/serverhandler.h @@ -27,6 +27,7 @@ #include "net/messagein.h" class CharacterData; +class ConnectionHandler; namespace GameServerHandler { @@ -57,9 +58,9 @@ namespace GameServerHandler void dumpStatistics(std::ostream &); /** - * Processes messages received by the connection handler. + * Returns the connection handler. */ - void process(); + ConnectionHandler *getConnectionHandler(); /** * Sends chat party information diff --git a/src/common/manaserv_protocol.h b/src/common/manaserv_protocol.h index ce41c723..77b8e5ac 100644 --- a/src/common/manaserv_protocol.h +++ b/src/common/manaserv_protocol.h @@ -79,6 +79,8 @@ enum { APMSG_LOGOUT_RESPONSE = 0x0014, // B error PAMSG_LOGIN_RNDTRGR = 0x0015, // S username APMSG_LOGIN_RNDTRGR_RESPONSE = 0x0016, // S random seed + PAMSG_STELLAR_LOGIN = 0x0017, // D version + APMSG_STELLAR_LOGIN_RESPONSE = 0x0018, // B error, S token PAMSG_CHAR_CREATE = 0x0020, // S name, B hair style, B hair color, B gender, B slot, {W stats}* APMSG_CHAR_CREATE_RESPONSE = 0x0021, // B error, on success: B slot, S name, B gender, B hair style, B hair color, // W character points, W correction points, B amount of items equipped, diff --git a/src/net/connectionhandler.cpp b/src/net/connectionhandler.cpp index 4edb1fd2..ee98a18b 100644 --- a/src/net/connectionhandler.cpp +++ b/src/net/connectionhandler.cpp @@ -36,16 +36,20 @@ #endif bool ConnectionHandler::startListen(enet_uint16 port, - const std::string &listenHost) + const std::string &hostname) { + // Remember unresolved host for debugging purposes. + this->hostname = hostname; + // Bind the server to the default localhost. + ENetAddress address; address.host = ENET_HOST_ANY; address.port = port; - if (!listenHost.empty()) - enet_address_set_host(&address, listenHost.c_str()); + if (!hostname.empty()) + enet_address_set_host(&address, hostname.c_str()); - LOG_INFO("Listening on " << listenHost << ":" << port << "..."); + LOG_INFO("Listening on " << hostname << ":" << port << "..."); #if defined(ENET_VERSION) && ENET_VERSION >= ENET_CUTOFF host = enet_host_create( &address /* the address to bind the server host to */, @@ -86,6 +90,11 @@ void ConnectionHandler::stopListen() // FIXME: memory leak on NetComputers } +enet_uint16 ConnectionHandler::getPort() const +{ + return host ? host->address.port : 0; +} + void ConnectionHandler::flush() { enet_host_flush(host); @@ -93,9 +102,11 @@ void ConnectionHandler::flush() void ConnectionHandler::process(enet_uint32 timeout) { + enet_host_service(host, nullptr, timeout); + ENetEvent event; // Process Enet events and do not block. - while (enet_host_service(host, &event, timeout) > 0) { + while (enet_host_check_events(host, &event) > 0) { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: { diff --git a/src/net/connectionhandler.h b/src/net/connectionhandler.h index e49d49a9..2d0a2a6a 100644 --- a/src/net/connectionhandler.h +++ b/src/net/connectionhandler.h @@ -41,11 +41,12 @@ class ConnectionHandler /** * Open the server socket. - * @param port the port to listen to - * @host the host IP to listen on, defaults to the default localhost + * + * @param port the port to listen to + * @param hostname the host IP to listen on, defaults to the default localhost */ bool startListen(enet_uint16 port, - const std::string &host = std::string()); + const std::string &hostname = std::string()); /** * Disconnect all the clients and close the server socket. @@ -53,13 +54,23 @@ class ConnectionHandler void stopListen(); /** + * Return the port the handler is listening on, or 0 if not listening. + */ + enet_uint16 getPort() const; + + /** + * Return the host the handler was asked to listen on. + */ + const std::string &getHostname() const { return hostname; } + + /** * Process outgoing messages and listen to the server socket for * incoming messages and new connections. * * @timeout an optional timeout in milliseconds to wait for something * to happen when there is nothing to do */ - virtual void process(enet_uint32 timeout = 0); + void process(enet_uint32 timeout = 0); /** * Process outgoing messages. @@ -82,8 +93,8 @@ class ConnectionHandler unsigned getClientCount() const; private: - ENetAddress address; /**< Includes the port to listen to. */ - ENetHost *host; /**< The host that listen for connections. */ + std::string hostname; /**< The unresolved host the handler was asked to listen on. */ + ENetHost *host; /**< The ENet host that listen for connections. */ protected: /** |