#include "ServerModule.h" ServerModule::ServerModule(Archimedes::App* a, void* h) : Archimedes::Module(a, h) { name = "ServerModule"; } ServerModule::~ServerModule() { if(app) { app->unregisterEvent(Archimedes::DataRecievedEvent()); app->unregisterEvent(Archimedes::DataSentEvent()); app->unregisterEvent(Archimedes::ConnectionStatusChangedEvent()); GameNetworkingSockets_Kill(); } } void ServerModule::onLoad() { app->registerEvent(Archimedes::DataRecievedEvent()); app->registerEvent(Archimedes::DataSentEvent()); app->registerEvent(Archimedes::ConnectionStatusChangedEvent()); SteamDatagramErrMsg errMsg; if ( !GameNetworkingSockets_Init( nullptr, errMsg ) ) { std::cerr << "GameNetworkingSockets_Init() Failed: " << errMsg << std::endl; } //SteamNetworkingUtils()->SetDebugOutputFunction( k_ESteamNetworkingSocketsDebugOutputType_Msg, DebugOutput ); interface = SteamNetworkingSockets(); } void ServerModule::startServer(int p) { if(!running) { port = p; SteamNetworkingIPAddr serverLocalAddr; serverLocalAddr.Clear(); serverLocalAddr.m_port = port; SteamNetworkingConfigValue_t opt; opt.SetPtr(k_ESteamNetworkingConfig_Callback_ConnectionStatusChanged, (void*)SteamNetConnectionStatusChangedCallback); listenSock = interface->CreateListenSocketIP( serverLocalAddr, 1, &opt ); if ( listenSock == k_HSteamListenSocket_Invalid ) std::cerr << "Failed to listen on port " << port << std::endl; pollGroup = interface->CreatePollGroup(); if ( pollGroup == k_HSteamNetPollGroup_Invalid ) std::cerr << "Failed to listen on port " << port << std::endl; std::cerr << "Server listening on port " << port << std::endl; running = true; } } void ServerModule::stopServer() { disconnectAllClients(); running = false; } void ServerModule::run() { if(running) { pollIncomingData(); PollConnectionStateChanges(); } else if(port >= 0) { interface->CloseListenSocket(listenSock); listenSock = k_HSteamListenSocket_Invalid; interface->DestroyPollGroup(pollGroup); pollGroup = k_HSteamNetPollGroup_Invalid; port = -1; } } bool ServerModule::onEvent(const Archimedes::Event& event) { unsigned int type = app->getEventType(event); if(eventsToHandle & SMEventEnum::ConnectionStatusChanged && type == app->getEventType(Archimedes::ConnectionStatusChangedEvent())) { Archimedes::ConnectionStatusChangedEvent& e = (Archimedes::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 ); std::cerr << "Connection Closed.\n"; 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; } std::cerr << "Connection Accepted!\n"; // 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 && type == app->getEventType(Archimedes::DataRecievedEvent())) { return true; } else if(eventsToHandle & SMEventEnum::DataSent && type == app->getEventType(Archimedes::DataSentEvent())) { return true; } return false; } void ServerModule::pollIncomingData() { while ( running ) { ISteamNetworkingMessage *pIncomingMsg = nullptr; int numMsgs = interface->ReceiveMessagesOnPollGroup( pollGroup, &pIncomingMsg, 1 ); if ( numMsgs == 0 ) break; if ( numMsgs < 0 ) std::cerr << "Error checking for messages" << std::endl; assert( numMsgs == 1 && pIncomingMsg ); auto itClient = clients.find( pIncomingMsg->m_conn ); assert( itClient != clients.end() ); app->emitEvent(new Archimedes::DataRecievedEvent(pIncomingMsg)); } }