From 00ef2271281ebea7b1ae220ff24aa5020bb472d0 Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 16 Apr 2025 01:26:56 -0500 Subject: [PATCH] work on ServerModule --- include/pch.hpp | 1 + include/utils/App/App.h | 43 +++++-- include/utils/Events/Event.h | 4 +- modules/ServerModule/src/ServerEvents.h | 60 ++++++---- modules/ServerModule/src/ServerModule.cpp | 138 ++++++++++++++++++++++ modules/ServerModule/src/ServerModule.h | 18 ++- 6 files changed, 228 insertions(+), 36 deletions(-) diff --git a/include/pch.hpp b/include/pch.hpp index d76e4e1..d89cc5c 100644 --- a/include/pch.hpp +++ b/include/pch.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include diff --git a/include/utils/App/App.h b/include/utils/App/App.h index de3d83a..112befc 100644 --- a/include/utils/App/App.h +++ b/include/utils/App/App.h @@ -3,6 +3,7 @@ #include "pch.hpp" #include "utils/Module/Module.h" +#include "utils/Events/Event.h" namespace Archimedes { @@ -55,9 +56,22 @@ namespace Archimedes { bool isDone() const { return done; } - std::unordered_map getEventTypes() const { return eventTypes; } + void emitEvent(Event* e) { events.push_back(e); } - void addEventType(std::string type) { eventTypes[type] = nextEventType++; } + unsigned int getEventType(std::string event) { return eventTypes[event]; } + + void addEventType(std::string type) { + //only add each type once + if(eventTypes.find(type) == eventTypes.end()) + eventTypes[type] = nextEventType++; + } + + void removeEventType(std::string type) { + //only erase registered types + auto it = eventTypes.find(type); + if(it != eventTypes.end()) + eventTypes.erase(it); + } private: @@ -73,10 +87,25 @@ namespace Archimedes { std::unordered_map modules; std::unordered_map eventTypes; + std::list events; + std::list runOrder; std::list toClose; std::list> toOpen; + + void handleEvents() { + while(!events.empty()) { + for(auto it = runOrder.rbegin(); it != runOrder.rend(); it++) { + if(modules[*it]->onEvent(*events.front())) { + Event* e = events.front(); + events.pop_front(); + delete e; + break; + } + } + } + } virtual Module* dynamicLoad(std::string lib) { @@ -169,18 +198,12 @@ namespace Archimedes { if(modules.find(name) == modules.end()) return; -/* - //unload modules that depend on the one we are unloading - for(std::string s : runOrder) { - if(modules[s]->deps.find(name) != modules[s]->deps.end()) { - unload(s); - } - } -*/ + Module* m = modules[name]; void* h = m->getHandle(); modules[name] = nullptr; + modules.erase(name); runOrder.remove(name); diff --git a/include/utils/Events/Event.h b/include/utils/Events/Event.h index 782b2f7..e1a0bc1 100644 --- a/include/utils/Events/Event.h +++ b/include/utils/Events/Event.h @@ -9,7 +9,9 @@ namespace Archimedes { public: - unsigned int type; + virtual ~Event() {} + + virtual operator std::string() const = 0; }; } diff --git a/modules/ServerModule/src/ServerEvents.h b/modules/ServerModule/src/ServerEvents.h index ae9b448..29efc25 100644 --- a/modules/ServerModule/src/ServerEvents.h +++ b/modules/ServerModule/src/ServerEvents.h @@ -1,45 +1,59 @@ #include "Archimedes.h" -struct Client { - unsigned int id; -}; +#include +#include -class RecievedEvent : public Archimedes::Event { - public: - RecievedEvent(Client c, void* buf, unsigned int s) : client(c), buffer(buf), size(s) {} +namespace SMEvent { + class DataRecievedEvent : public Archimedes::Event { - Client client; + public: - void* buffer; + DataRecievedEvent() : clientID(0), buffer(nullptr), size(0) {} - unsigned int size; + DataRecievedEvent(unsigned int c, void* buf, unsigned int s) : clientID(c), buffer(buf), size(s) {} -}; + operator std::string() const { return "DataRecievedEvent"; } -class SentEvent : public Archimedes::Event { + unsigned int clientID; - public: + void* buffer; - SentEvent(Client c, void* buf, unsigned int s) : client(c), buffer(buf), size(s) {} + unsigned int size; - Client client; + }; - void* buffer; + class DataSentEvent : public Archimedes::Event { - unsigned int size; + public: -}; + DataSentEvent() : clientID(0), buffer(nullptr), size(0) {} -class ConnectionStatusChangedEvent : public Archimedes::Event { + DataSentEvent(unsigned int c, void* buf, unsigned int s) : clientID(c), buffer(buf), size(s) {} - public: + operator std::string() const { return "DataSentEvent"; } - ConnectionStatusChangedEvent(Client c, unsigned int s) : client(c), status(s) {} + unsigned int clientID; - Client client; + void* buffer; + + unsigned int size; + + }; + + class ConnectionStatusChangedEvent : public Archimedes::Event { + + public: + + ConnectionStatusChangedEvent() : info(nullptr) {} + + ConnectionStatusChangedEvent(SteamNetConnectionStatusChangedCallback_t* i) : info(i) {} + + operator std::string() const { return "ConnectionStatusChangedEvent"; } + + SteamNetConnectionStatusChangedCallback_t* info; + }; +} - unsigned int status; -}; diff --git a/modules/ServerModule/src/ServerModule.cpp b/modules/ServerModule/src/ServerModule.cpp index cfdf24d..979516c 100644 --- a/modules/ServerModule/src/ServerModule.cpp +++ b/modules/ServerModule/src/ServerModule.cpp @@ -5,10 +5,19 @@ ServerModule::ServerModule(Archimedes::App* a, void* h) : Archimedes::Module(a, } ServerModule::~ServerModule() { + app->removeEventType(SMEvent::DataRecievedEvent()); + app->removeEventType(SMEvent::DataSentEvent()); + app->removeEventType(SMEvent::ConnectionStatusChangedEvent()); + GameNetworkingSockets_Kill(); } void ServerModule::onLoad() { + + app->addEventType(SMEvent::DataRecievedEvent()); + app->addEventType(SMEvent::DataSentEvent()); + app->addEventType(SMEvent::ConnectionStatusChangedEvent()); + SteamDatagramErrMsg errMsg; if ( !GameNetworkingSockets_Init( nullptr, errMsg ) ) { @@ -51,3 +60,132 @@ void ServerModule::run() { port = -1; //just in case } } + +void ServerModule::OnSteamNetConnectionStatusChanged( SteamNetConnectionStatusChangedCallback_t *pInfo ) { + app->emitEvent(new SMEvent::ConnectionStatusChangedEvent(pInfo)); +} + +bool ServerModule::onEvent(const Archimedes::Event& event) { + + if(eventsToHandle & SMEventEnum::ConnectionStatusChanged && app->getEventType(event) == app->getEventType("ConnectionStatusChangedEvent")) { + + SMEvent::ConnectionStatusChangedEvent& e = (SMEvent::ConnectionStatusChangedEvent&) event; + + switch(e.info->m_info.m_eState) { + + case k_ESteamNetworkingConnectionState_None: + // NOTE: We will get callbacks here when we destroy connections. You can ignore these. + break; + + case k_ESteamNetworkingConnectionState_ClosedByPeer: + case k_ESteamNetworkingConnectionState_ProblemDetectedLocally: + { + // Ignore if they were not previously connected. (If they disconnected + // before we accepted the connection.) + if ( e.info->m_eOldState == k_ESteamNetworkingConnectionState_Connected ) + { + + // Locate the client. Note that it should have been found, because this + // is the only codepath where we remove clients (except on shutdown), + // and connection change callbacks are dispatched in queue order. + auto itClient = clients.find( e.info->m_hConn ); + assert( itClient != clients.end() ); + + // Select appropriate log messages + //const char *pszDebugLogAction; + if ( e.info->m_info.m_eState == k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) + { + //pszDebugLogAction = "problem detected locally"; + //sprintf( temp, "Alas, %s hath fallen into shadow. (%s)", itClient->second.name.c_str(), e.info->m_info.m_szEndDebug ); + } + else + { + // Note that here we could check the reason code to see if + // it was a "usual" connection or an "unusual" one. + //pszDebugLogAction = "closed by peer"; (void)pszDebugLogAction; + //sprintf( temp, "%s hath departed", itClient->second.name.c_str() ); + } + + // Spew something to our own log. Note that because we put their nick + // as the connection description, it will show up, along with their + // transport-specific data (e.g. their IP address) + /*Printf( "Connection %s %s, reason %d: %s\n", + e.info->m_info.m_szConnectionDescription, + pszDebugLogAction, + e.info->m_info.m_eEndReason, + e.info->m_info.m_szEndDebug + );*/ + + clients.erase( itClient ); + + // Send a message so everybody else knows what happened + //SendStringToAllClients( temp ); + } + else + { + assert( e.info->m_eOldState == k_ESteamNetworkingConnectionState_Connecting ); + } + + // Clean up the connection. This is important! + // The connection is "closed" in the network sense, but + // it has not been destroyed. We must close it on our end, too + // to finish up. The reason information do not matter in this case, + // and we cannot linger because it's already closed on the other end, + // so we just pass 0's. + interface->CloseConnection( e.info->m_hConn, 0, nullptr, false ); + break; + } + + case k_ESteamNetworkingConnectionState_Connecting: + { + // This must be a new connection + assert( clients.find( e.info->m_hConn ) == clients.end() ); + + //Printf( "Connection request from %s", e.info->m_info.m_szConnectionDescription ); + + // A client is attempting to connect + // Try to accept the connection. + if ( interface->AcceptConnection( e.info->m_hConn ) != k_EResultOK ) + { + // This could fail. If the remote host tried to connect, but then + // disconnected, the connection may already be half closed. Just + // destroy whatever we have on our side. + interface->CloseConnection( e.info->m_hConn, 0, nullptr, false ); + //Printf( "Can't accept connection. (It was already closed?)" ); + break; + } + + // Assign the poll group + if ( !interface->SetConnectionPollGroup( e.info->m_hConn, pollGroup ) ) + { + interface->CloseConnection( e.info->m_hConn, 0, nullptr, false ); + //Printf( "Failed to set poll group?" ); + break; + } + + // Add them to the client list, using std::map wacky syntax + clients[ e.info->m_hConn ] = ++numClients; + //SetClientNick( e.info->m_hConn, nick ); + break; + } + + case k_ESteamNetworkingConnectionState_Connected: + // We will get a callback immediately after accepting the connection. + // Since we are the server, we can ignore this, it's not news to us. + break; + + default: + // Silences -Wswitch + break; + } + return true; + + } else if(eventsToHandle & SMEventEnum::DataRecieved && app->getEventType(event) == app->getEventType("DataRecievedEvent")) { + return true; + } else if(eventsToHandle & SMEventEnum::DataSent && app->getEventType(event) == app->getEventType("DataSentEvent")) { + return true; + } + + return false; +} + diff --git a/modules/ServerModule/src/ServerModule.h b/modules/ServerModule/src/ServerModule.h index f3e9a46..4124b9a 100644 --- a/modules/ServerModule/src/ServerModule.h +++ b/modules/ServerModule/src/ServerModule.h @@ -17,7 +17,22 @@ class ServerModule : public Archimedes::Module { void startServer(int); void stopServer(); + enum SMEventEnum { + None = 0, + ConnectionStatusChanged = 1 << 0, + DataRecieved = 1 << 1, + DataSent = 1 << 2 + }; + + void shouldHandleEvents(unsigned int events) { eventsToHandle = events; } + + void sendData() {} + void pollIncomingData(); + private: + //handle all events by default + unsigned int eventsToHandle = SMEventEnum::ConnectionStatusChanged | SMEventEnum::DataSent | SMEventEnum::DataRecieved; + bool running = false; int port = -1; @@ -36,9 +51,8 @@ class ServerModule : public Archimedes::Module { void OnSteamNetConnectionStatusChanged( SteamNetConnectionStatusChangedCallback_t *pInfo ); - std::map clients; + std::map clients; - void PollIncomingMessages(); void PollConnectionStateChanges(); void PollLocalUserInput(); };