diff --git a/source/glest_game/game/game_constants.h b/source/glest_game/game/game_constants.h index 4f2d26ca2..bf868a366 100644 --- a/source/glest_game/game/game_constants.h +++ b/source/glest_game/game/game_constants.h @@ -89,6 +89,7 @@ public: static const int specialFactions = fpt_EndCount - 1; static const int maxPlayers= 8; static const int serverPort= 61357; + static const int serverAdminPort= 61355; //static const int updateFps= 40; //static const int cameraFps= 100; static int updateFps; diff --git a/source/glest_game/main/main.cpp b/source/glest_game/main/main.cpp index a94316892..6fdfd586e 100644 --- a/source/glest_game/main/main.cpp +++ b/source/glest_game/main/main.cpp @@ -2733,6 +2733,23 @@ int glestMain(int argc, char** argv) { } createDirectoryPaths(tempDataPath); + if(hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_MASTERSERVER_STATUS])) == true) { + Ip ip("localhost"); + int port = Config::getInstance().getInt("ServerAdminPort", intToStr(GameConstants::serverAdminPort).c_str()); + ClientSocket clientSocket; + clientSocket.setBlock(false); + clientSocket.connect(ip, port); + if(clientSocket.isConnected() == true) { + clientSocket.setBlock(true); + + char szBuf[8096]=""; + clientSocket.receive(&szBuf[0],8095,false); + std::cout << szBuf << std::endl; + } + + return 0; + } + if( hasCommandArgument(argc, argv,GAME_ARGS[GAME_ARG_DISABLE_SOUND]) == true || hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_MASTERSERVER_MODE])) == true) { config.setString("FactorySound","None"); diff --git a/source/glest_game/network/server_interface.cpp b/source/glest_game/network/server_interface.cpp index ef7e4fdfc..3635a95bd 100644 --- a/source/glest_game/network/server_interface.cpp +++ b/source/glest_game/network/server_interface.cpp @@ -25,6 +25,7 @@ #include "miniftpserver.h" #include "window.h" #include +#include #include "map_preview.h" #include "leak_dumper.h" @@ -72,6 +73,11 @@ ServerInterface::ServerInterface(bool publishEnabled) :GameNetworkInterface() { lastGlobalLagCheckTime = 0; masterserverAdminRequestLaunch = false; + serverSocketAdmin = new ServerSocket(true); + serverSocketAdmin->setBlock(false); + serverSocketAdmin->setBindPort(Config::getInstance().getInt("ServerAdminPort", intToStr(GameConstants::serverAdminPort).c_str())); + serverSocketAdmin->listen(5); + maxFrameCountLagAllowed = Config::getInstance().getInt("MaxFrameCountLagAllowed", intToStr(maxFrameCountLagAllowed).c_str()); maxFrameCountLagAllowedEver = Config::getInstance().getInt("MaxFrameCountLagAllowedEver", intToStr(maxFrameCountLagAllowedEver).c_str()); maxClientLagTimeAllowedEver = Config::getInstance().getInt("MaxClientLagTimeAllowedEver", intToStr(maxClientLagTimeAllowedEver).c_str()); @@ -180,6 +186,17 @@ ServerInterface::ServerInterface(bool publishEnabled) :GameNetworkInterface() { portNumber,GameConstants::maxPlayers,this); ftpServer->start(); } + + if(publishToMasterserverThread == NULL) { + if(needToRepublishToMasterserver == true || GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { + publishToMasterserverThread = new SimpleTaskThread(this,0,125); + publishToMasterserverThread->setUniqueID(__FILE__); + publishToMasterserverThread->start(); + + if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",__FILE__,__FUNCTION__,__LINE__,needToRepublishToMasterserver); + } + } + if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); } @@ -258,6 +275,8 @@ ServerInterface::~ServerInterface() { delete masterServerThreadAccessor; masterServerThreadAccessor = NULL; + delete serverSocketAdmin; + serverSocketAdmin = NULL; if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); } @@ -1732,12 +1751,14 @@ bool ServerInterface::launchGame(const GameSettings *gameSettings) { if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",__FILE__,__FUNCTION__,__LINE__,needToRepublishToMasterserver); - if(needToRepublishToMasterserver == true) { - publishToMasterserverThread = new SimpleTaskThread(this,0,125); - publishToMasterserverThread->setUniqueID(__FILE__); - publishToMasterserverThread->start(); + if(publishToMasterserverThread == NULL) { + if(needToRepublishToMasterserver == true || GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { + publishToMasterserverThread = new SimpleTaskThread(this,0,125); + publishToMasterserverThread->setUniqueID(__FILE__); + publishToMasterserverThread->start(); - if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",__FILE__,__FUNCTION__,__LINE__,needToRepublishToMasterserver); + if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] needToRepublishToMasterserver = %d\n",__FILE__,__FUNCTION__,__LINE__,needToRepublishToMasterserver); + } } if(ftpServer != NULL) { @@ -1865,6 +1886,7 @@ void ServerInterface::updateListen() { ++openSlotCount; } } + serverSocket.listen(openSlotCount); } @@ -2085,6 +2107,7 @@ std::map ServerInterface::publishToMasterserver() { void ServerInterface::simpleTask(BaseThread *callingThread) { MutexSafeWrapper safeMutex(masterServerThreadAccessor,CODE_AT_LINE); + if(difftime(time(NULL),lastMasterserverHeartbeatTime) >= MASTERSERVER_HEARTBEAT_GAME_STATUS_SECONDS) { if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line %d]\n",__FILE__,__FUNCTION__,__LINE__); @@ -2123,9 +2146,112 @@ void ServerInterface::simpleTask(BaseThread *callingThread) { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line %d] error during game status update: [%s]\n",__FILE__,__FUNCTION__,__LINE__,ex.what()); } } + if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { + DumpStatsToLog(false); + } + } + if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { + //printf("Attempt Accept\n"); + Socket *cli = serverSocketAdmin->accept(false); + if(cli != NULL) { + printf("Got status request connection, dumping info...\n"); + + string data = DumpStatsToLog(true); + cli->send(data.c_str(),data.length()); + cli->disconnectSocket(); + } } } +std::string ServerInterface::DumpStatsToLog(bool dumpToStringOnly) const { + string headlessLogFile = Config::getInstance().getString("HeadlessLogFile","headless.log"); + if(getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) != "") { + headlessLogFile = getGameReadWritePath(GameConstants::path_logs_CacheLookupKey) + headlessLogFile ; + } + else { + string userData = Config::getInstance().getString("UserData_Root",""); + if(userData != "") { + endPathWithSlash(userData); + } + headlessLogFile = userData + headlessLogFile ; + } + + ostringstream out; + out << "=========================================" << std::endl; + out << "Headless Server Current Game information:" << std::endl; + out << "=========================================" << std::endl; + + int connectedSlotCount = 0; + for(int i= 0; exitServer == false && i < GameConstants::maxPlayers; ++i) { + MutexSafeWrapper safeMutexSlot(slotAccessorMutexes[i],CODE_AT_LINE_X(i)); + ConnectionSlot *slot = slots[i]; + if(slot != NULL) { + connectedSlotCount++; + out << "Network connection for index: " << i << std::endl; + out << "------------------------------" << std::endl; + out << "Connected: " << boolToStr(slot->isConnected()) << std::endl; + out << "Handshake received: " << boolToStr(slot->getConnectHasHandshaked()) << std::endl; + if(slot->isConnected() == true) { + time_t connectTime = slot->getConnectedTime(); + struct tm *loctime = localtime (&connectTime); + char szBuf[8096]=""; + strftime(szBuf,100,"%Y-%m-%d %H:%M:%S",loctime); + + const int HOURS_IN_DAY = 24; + const int MINUTES_IN_HOUR = 60; + const int SECONDS_IN_MINUTE = 60; + int InSeconds = difftime(time(NULL),slot->getConnectedTime()); + // compute seconds + int seconds = InSeconds % SECONDS_IN_MINUTE ; + // throw away seconds used in previous statement and convert to minutes + int InMinutes = InSeconds / SECONDS_IN_MINUTE ; + // compute minutes + int minutes = InMinutes % MINUTES_IN_HOUR ; + + // throw away minutes used in previous statement and convert to hours + int InHours = InMinutes / MINUTES_IN_HOUR ; + // compute hours + int hours = InHours % HOURS_IN_DAY ; + + out << "Connected at: " << szBuf << std::endl; + out << "Connection duration: " << hours << " hours " << minutes << " minutes " << seconds << " seconds." << std::endl; + out << "Player Index: " << slot->getPlayerIndex() << std::endl; + out << "IP Address: " << slot->getIpAddress() << std::endl; + out << "Player name: " << slot->getName() << std::endl; + out << "Language: " << slot->getNetworkPlayerLanguage() << std::endl; + out << "Game Version: " << slot->getVersionString() << std::endl; + out << "Session id: " << slot->getSessionKey() << std::endl; + out << "Socket id: " << slot->getSocketId() << std::endl; + } + } + } + out << "Total Slot Count: " << connectedSlotCount << std::endl; + out << "=========================================" << std::endl; + + std::string result = out.str(); + + if(dumpToStringOnly == false) { + +#if defined(WIN32) && !defined(__MINGW32__) + FILE *fp = _wfopen(utf8_decode(headlessLogFile ).c_str(), L"w"); + std::ofstream logFile(fp); +#else + std::ofstream logFile; + logFile.open(headlessLogFile .c_str(), ios_base::out | ios_base::trunc); +#endif + logFile << result; + logFile.close(); +#if defined(WIN32) && !defined(__MINGW32__) + if(fp) { + fclose(fp); + } +#endif + } + + return result; +} + + void ServerInterface::notifyBadClientConnectAttempt(string ipAddress) { //printf("In [%s::%s Line: %d] ipAddress [%s]\n",__FILE__,__FUNCTION__,__LINE__,ipAddress.c_str()); diff --git a/source/glest_game/network/server_interface.h b/source/glest_game/network/server_interface.h index e2eae607c..72d71d680 100644 --- a/source/glest_game/network/server_interface.h +++ b/source/glest_game/network/server_interface.h @@ -88,6 +88,8 @@ private: map > badClientConnectIPList; + ServerSocket *serverSocketAdmin; + public: ServerInterface(bool publishEnabled); virtual ~ServerInterface(); @@ -197,6 +199,7 @@ public: virtual int isClientAllowedToGetFile(uint32 clientIp, const char *username, const char *filename); void notifyBadClientConnectAttempt(string ipAddress); + std::string DumpStatsToLog(bool dumpToStringOnly) const; private: void broadcastMessage(const NetworkMessage *networkMessage, int excludeSlot = -1, int lockedSlotIndex = -1); diff --git a/source/shared_lib/include/platform/posix/socket.h b/source/shared_lib/include/platform/posix/socket.h index 239391555..54b1eba28 100644 --- a/source/shared_lib/include/platform/posix/socket.h +++ b/source/shared_lib/include/platform/posix/socket.h @@ -247,8 +247,10 @@ protected: bool isBroadCastThreadRunning(); vector blockIPList; + bool basicMode; + public: - ServerSocket(); + ServerSocket(bool basicMode = false); virtual ~ServerSocket(); void bind(int port); void listen(int connectionQueueSize= SOMAXCONN); diff --git a/source/shared_lib/include/platform/sdl/platform_main.h b/source/shared_lib/include/platform/sdl/platform_main.h index ebdb3ab5b..f931d113e 100644 --- a/source/shared_lib/include/platform/sdl/platform_main.h +++ b/source/shared_lib/include/platform/sdl/platform_main.h @@ -31,6 +31,7 @@ const char *GAME_ARGS[] = { "--connecthost", "--starthost", "--headless-server-mode", + "--headless-server-status", "--use-ports", "--load-scenario", @@ -84,6 +85,7 @@ enum GAME_ARG_TYPE { GAME_ARG_CLIENT, GAME_ARG_SERVER, GAME_ARG_MASTERSERVER_MODE, + GAME_ARG_MASTERSERVER_STATUS, GAME_ARG_USE_PORTS, GAME_ARG_LOADSCENARIO, @@ -168,6 +170,8 @@ void printParameterHelp(const char *argv0, bool foundInvalidArgs) { printf("\n \t\tvps - which does NOT read commands from the"); printf("\n \t local console (for some vps's)."); + printf("\n%s\tCheck the current status of a headless server.",GAME_ARGS[GAME_ARG_MASTERSERVER_STATUS]); + printf("\n%s=x,y\t\t\tForce hosted games to listen internally on port",GAME_ARGS[GAME_ARG_USE_PORTS]); printf("\n\t\t\t\tx, externally on port y."); printf("\n \t\tWhere x is the internal port # on the local"); @@ -425,7 +429,8 @@ int mainSetup(int argc, char **argv) { if(hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_HELP])) == true || hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_VERSION])) == true || hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_SHOW_INI_SETTINGS])) == true || - hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_MASTERSERVER_MODE])) == true) { + hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_MASTERSERVER_MODE])) == true || + hasCommandArgument(argc, argv,string(GAME_ARGS[GAME_ARG_MASTERSERVER_STATUS]))) { // Use this for masterserver mode for timers like Chrono if(SDL_Init(SDL_INIT_TIMER) < 0) { std::cerr << "Couldn't initialize SDL: " << SDL_GetError() << "\n"; diff --git a/source/shared_lib/sources/platform/posix/socket.cpp b/source/shared_lib/sources/platform/posix/socket.cpp index 0b57cd2b4..f3458af37 100644 --- a/source/shared_lib/sources/platform/posix/socket.cpp +++ b/source/shared_lib/sources/platform/posix/socket.cpp @@ -2042,9 +2042,10 @@ void BroadCastClientSocketThread::execute() { // class ServerSocket // =============================================== -ServerSocket::ServerSocket() : Socket() { - if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); +ServerSocket::ServerSocket(bool basicMode) : Socket() { + if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d] basicMode = %d\n",__FILE__,__FUNCTION__,__LINE__,basicMode); + this->basicMode = basicMode; //printf("SERVER SOCKET CONSTRUCTOR\n"); //MutexSafeWrapper safeMutexUPNP(&ServerSocket::mutexUpnpdiscoverThread,CODE_AT_LINE); //ServerSocket::upnpdiscoverThread = NULL; @@ -2052,7 +2053,9 @@ ServerSocket::ServerSocket() : Socket() { portBound = false; broadCastThread = NULL; - UPNP_Tools::enabledUPNP = false; + if(this->basicMode == false) { + UPNP_Tools::enabledUPNP = false; + } if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); } @@ -2064,29 +2067,32 @@ ServerSocket::~ServerSocket() { stopBroadCastThread(); - //printf("In [%s::%s] Line: %d safeMutexUPNP\n",__FILE__,__FUNCTION__,__LINE__); - //printf("SERVER SOCKET DESTRUCTOR\n"); - MutexSafeWrapper safeMutexUPNP(&ServerSocket::mutexUpnpdiscoverThread,CODE_AT_LINE); - if(ServerSocket::upnpdiscoverThread != NULL) { - SDL_WaitThread(ServerSocket::upnpdiscoverThread, NULL); - ServerSocket::upnpdiscoverThread = NULL; - } - safeMutexUPNP.ReleaseLock(); - //printf("In [%s::%s] Line: %d safeMutexUPNP\n",__FILE__,__FUNCTION__,__LINE__); + if(this->basicMode == false) { + //printf("In [%s::%s] Line: %d safeMutexUPNP\n",__FILE__,__FUNCTION__,__LINE__); + //printf("SERVER SOCKET DESTRUCTOR\n"); + MutexSafeWrapper safeMutexUPNP(&ServerSocket::mutexUpnpdiscoverThread,CODE_AT_LINE); + if(ServerSocket::upnpdiscoverThread != NULL) { + SDL_WaitThread(ServerSocket::upnpdiscoverThread, NULL); + ServerSocket::upnpdiscoverThread = NULL; + } + safeMutexUPNP.ReleaseLock(); + //printf("In [%s::%s] Line: %d safeMutexUPNP\n",__FILE__,__FUNCTION__,__LINE__); - //printf("In [%s::%s] Line: %d UPNP_Tools::enabledUPNP = %d\n",__FILE__,__FUNCTION__,__LINE__,UPNP_Tools::enabledUPNP); - if (UPNP_Tools::enabledUPNP) { - UPNP_Tools::NETremRedirects(ServerSocket::externalPort); - //UPNP_Tools::enabledUPNP = false; + + //printf("In [%s::%s] Line: %d UPNP_Tools::enabledUPNP = %d\n",__FILE__,__FUNCTION__,__LINE__,UPNP_Tools::enabledUPNP); + if (UPNP_Tools::enabledUPNP) { + UPNP_Tools::NETremRedirects(ServerSocket::externalPort); + //UPNP_Tools::enabledUPNP = false; + } + + MutexSafeWrapper safeMutexUPNP1(&UPNP_Tools::mutexUPNP,CODE_AT_LINE); + if(urls.controlURL && urls.ipcondescURL && urls.controlURL_CIF) { + FreeUPNPUrls(&urls); + } + + if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); + safeMutexUPNP1.ReleaseLock(); } - - MutexSafeWrapper safeMutexUPNP1(&UPNP_Tools::mutexUPNP,CODE_AT_LINE); - if(urls.controlURL && urls.ipcondescURL && urls.controlURL_CIF) { - FreeUPNPUrls(&urls); - } - - if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); - safeMutexUPNP1.ReleaseLock(); } void ServerSocket::stopBroadCastThread() { @@ -2184,8 +2190,7 @@ void ServerSocket::bind(int port) { #endif int err= ::bind(sock, reinterpret_cast(&addr), sizeof(addr)); - if(err < 0) - { + if(err < 0) { char szBuf[1024]=""; sprintf(szBuf, "In [%s::%s] Error binding socket sock = %d, err = %d, error = %s\n",__FILE__,__FUNCTION__,sock,err,getLastSocketErrorFormattedText().c_str()); if(SystemFlags::getSystemSettingType(SystemFlags::debugNetwork).enabled) SystemFlags::OutputDebug(SystemFlags::debugNetwork,"%s",szBuf); @@ -2233,17 +2238,19 @@ void ServerSocket::listen(int connectionQueueSize) { disconnectSocket(); } - if(connectionQueueSize > 0) { - if(isBroadCastThreadRunning() == false) { - startBroadCastThread(); + if(this->basicMode == false) { + if(connectionQueueSize > 0) { + if(isBroadCastThreadRunning() == false) { + startBroadCastThread(); + } + else { + resumeBroadcast(); + } } else { - resumeBroadcast(); + pauseBroadcast(); } } - else { - pauseBroadcast(); - } } Socket *ServerSocket::accept(bool errorOnFail) {