diff --git a/engine/server/application/CMakeLists.txt b/engine/server/application/CMakeLists.txt index 2317195d..14baaa75 100644 --- a/engine/server/application/CMakeLists.txt +++ b/engine/server/application/CMakeLists.txt @@ -4,4 +4,5 @@ add_subdirectory(ChatServer) add_subdirectory(ConnectionServer) add_subdirectory(LoginServer) add_subdirectory(LogServer) +add_subdirectory(PlanetServer) add_subdirectory(TaskManager) diff --git a/engine/server/application/PlanetServer/CMakeLists.txt b/engine/server/application/PlanetServer/CMakeLists.txt new file mode 100644 index 00000000..dc09cf68 --- /dev/null +++ b/engine/server/application/PlanetServer/CMakeLists.txt @@ -0,0 +1,6 @@ + +cmake_minimum_required(VERSION 2.8) + +project(PlanetServer) + +add_subdirectory(src) diff --git a/engine/server/application/PlanetServer/src/CMakeLists.txt b/engine/server/application/PlanetServer/src/CMakeLists.txt new file mode 100644 index 00000000..bcf85189 --- /dev/null +++ b/engine/server/application/PlanetServer/src/CMakeLists.txt @@ -0,0 +1,114 @@ + +set(SHARED_SOURCES + shared/CentralServerConnection.cpp + shared/CentralServerConnection.h + shared/ConfigPlanetServer.cpp + shared/ConfigPlanetServer.h + shared/ConsoleManager.cpp + shared/ConsoleManager.h + shared/ConsoleCommandParser.cpp + shared/ConsoleCommandParser.h + shared/FirstPlanetServer.h + shared/GameServerConnection.cpp + shared/GameServerConnection.h + shared/GameServerData.cpp + shared/GameServerData.h + shared/PlanetProxyObject.cpp + shared/PlanetProxyObject.h + shared/PlanetServer.cpp + shared/PlanetServer.h + shared/PlanetServerMetricsData.cpp + shared/PlanetServerMetricsData.h + shared/PreloadManager.cpp + shared/PreloadManager.h + shared/QuadtreeNode.cpp + shared/QuadtreeNode.h + shared/Scene.cpp + shared/Scene.h + shared/TaskConnection.cpp + shared/TaskConnection.h + shared/WatcherConnection.cpp + shared/WatcherConnection.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/FirstPlanetServer.cpp + win32/WinMain.cpp + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/win32) +else() + set(PLATFORM_SOURCES + linux/main.cpp + ) + + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/linux) +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedCommandParser/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedCompression/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFile/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedLog/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMath/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMathArchive/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMessageDispatch/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetwork/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetworkMessages/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedRandom/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedThread/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedUtility/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverMetrics/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverNetworkMessages/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverUtility/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/archive/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/singleton/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicode/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicodeArchive/include/public +) + +link_directories(${STLPORT_LIBDIR}) + +add_executable(PlanetServer + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) + +target_link_libraries(PlanetServer + sharedCommandParser + sharedCompression + sharedDebug + sharedFile + sharedFoundation + sharedLog + sharedMath + sharedMemoryManager + sharedMessageDispatch + sharedNetwork + sharedNetworkMessages + sharedRandom + sharedSynchronization + sharedThread + sharedUtility + serverMetrics + serverNetworkMessages + serverUtility + archive + fileInterface + localization + localizationArchive + unicode + unicodeArchive + udplibrary + ${ZLIB_LIBRARY} +) + +if(WIN32) + target_link_libraries(PlanetServer mswsock ws2_32) +endif() diff --git a/engine/server/application/PlanetServer/src/linux/main.cpp b/engine/server/application/PlanetServer/src/linux/main.cpp new file mode 100644 index 00000000..d77747cf --- /dev/null +++ b/engine/server/application/PlanetServer/src/linux/main.cpp @@ -0,0 +1,70 @@ +#include "FirstPlanetServer.h" +#include "ConfigPlanetServer.h" +#include "PlanetServer.h" + +#include "sharedCompression/SetupSharedCompression.h" +#include "sharedDebug/SetupSharedDebug.h" +#include "sharedFile/SetupSharedFile.h" +#include "sharedFoundation/Os.h" +#include "sharedFoundation/SetupSharedFoundation.h" +#include "sharedLog/SetupSharedLog.h" +#include "sharedNetwork/SetupSharedNetwork.h" +#include "sharedNetworkMessages/SetupSharedNetworkMessages.h" +#include "sharedRandom/SetupSharedRandom.h" +#include "sharedThread/SetupSharedThread.h" +#include "sharedUtility/SetupSharedUtility.h" + +// ====================================================================== + +void dumpPid(const char * argv) +{ + pid_t p = getpid(); + char fileName[1024]; + sprintf(fileName, "%s.%d", argv, p); + FILE * f = fopen(fileName, "w+"); + fclose(f); //lint !e668 +} + +int main(int argc, char ** argv) +{ +// dumpPid(argv[0]); + + SetupSharedThread::install(); + SetupSharedDebug::install(1024); + + //-- setup foundation + SetupSharedFoundation::Data setupFoundationData(SetupSharedFoundation::Data::D_game); + setupFoundationData.lpCmdLine = ConvertCommandLine(argc,argv); + SetupSharedFoundation::install (setupFoundationData); + + SetupSharedCompression::install(); + SetupSharedFile::install(false); + + SetupSharedNetwork::SetupData networkSetupData; + SetupSharedNetwork::getDefaultServerSetupData(networkSetupData); + SetupSharedNetwork::install(networkSetupData); + + SetupSharedNetworkMessages::install(); + + SetupSharedRandom::install(time(NULL)); //lint !e732 loss of sign (of 0!) + + SetupSharedUtility::Data sharedUtilityData; + SetupSharedUtility::setupGameData (sharedUtilityData); + SetupSharedUtility::install (sharedUtilityData); + + Os::setProgramName("PlanetServer"); + ConfigPlanetServer::install(); + + char tmp[92]; + sprintf(tmp, "PlanetServer:%d", Os::getProcessId()); + SetupSharedLog::install(tmp); + +//-- run game + PlanetServer::run(); + + SetupSharedLog::remove(); + SetupSharedFoundation::remove(); + SetupSharedThread::remove(); + + return 0; +} diff --git a/engine/server/application/PlanetServer/src/shared/CentralServerConnection.cpp b/engine/server/application/PlanetServer/src/shared/CentralServerConnection.cpp new file mode 100644 index 00000000..e9866455 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/CentralServerConnection.cpp @@ -0,0 +1,80 @@ +// ====================================================================== +// +// CentralServerConnection.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "CentralServerConnection.h" + +#include "ConsoleManager.h" +#include "serverNetworkMessages/CentralPlanetServerConnect.h" +#include "sharedFoundation/NetworkId.h" +#include "sharedNetworkMessages/ConsoleChannelMessages.h" +#include "sharedNetwork/NetworkSetupData.h" +#include "sharedNetwork/Service.h" +#include "ConfigPlanetServer.h" +#include "PlanetServer.h" + +//----------------------------------------------------------------------- + +CentralServerConnection::CentralServerConnection(const std::string & a, const unsigned short p) : +ServerConnection(a, p, NetworkSetupData()) +{ +} + +//----------------------------------------------------------------------- + +CentralServerConnection::~CentralServerConnection() +{ +} + +//----------------------------------------------------------------------- + +void CentralServerConnection::onConnectionClosed() +{ + ServerConnection::onConnectionClosed(); + + static MessageConnectionCallback m("CentralConnectionClosed"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void CentralServerConnection::onConnectionOpened() +{ + ServerConnection::onConnectionOpened(); + + DEBUG_REPORT_LOG(true,("Connection to Central opened.\n")); + + CentralPlanetServerConnect msg(ConfigPlanetServer::getSceneID(), PlanetServer::getInstance().getGameService()->getBindAddress(), PlanetServer::getInstance().getGameServicePort()); + send(msg,true); + + PlanetServer::getInstance().onCentralConnected(this); + + static MessageConnectionCallback m("CentralConnectionOpened"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void CentralServerConnection::onReceive(const Archive::ByteStream & message) +{ + Archive::ReadIterator ri = message.begin(); + const GameNetworkMessage msg(ri); + ri = message.begin(); + + if(msg.isType("ConGenericMessage")) + { + const ConGenericMessage cm(ri); + std::string result; + ConsoleManager::processString(cm.getMsg(), cm.getMsgId(), result); + const ConGenericMessage response(result, cm.getMsgId()); + send(response, true); + } + + ServerConnection::onReceive(message); +} + +//----------------------------------------------------------------------- diff --git a/engine/server/application/PlanetServer/src/shared/CentralServerConnection.h b/engine/server/application/PlanetServer/src/shared/CentralServerConnection.h new file mode 100644 index 00000000..5784a14e --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/CentralServerConnection.h @@ -0,0 +1,35 @@ +// ====================================================================== +// +// CentralServerConnection.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_CentralServerConnection_H +#define INCLUDED_CentralServerConnection_H + +// ====================================================================== + +#include "serverUtility/ServerConnection.h" + +// ====================================================================== + +class CentralServerConnection : public ServerConnection +{ +public: + CentralServerConnection (const std::string & remoteAddress, const unsigned short port); + ~CentralServerConnection (); + + void onConnectionClosed (); + void onConnectionOpened (); + const std::string & getClusterName () const; + virtual void onReceive (const Archive::ByteStream & message); +private: + CentralServerConnection (const CentralServerConnection&); + CentralServerConnection& operator= (const CentralServerConnection&); + CentralServerConnection(); +}; + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/shared/ConfigPlanetServer.cpp b/engine/server/application/PlanetServer/src/shared/ConfigPlanetServer.cpp new file mode 100644 index 00000000..d8a62f13 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/ConfigPlanetServer.cpp @@ -0,0 +1,97 @@ +// ====================================================================== +// +// ConfigPlanetServer.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "ConfigPlanetServer.h" +#include "QuadtreeNode.h" +#include "serverUtility/ConfigServerUtility.h" +#include "sharedFoundation/ConfigFile.h" +#include "sharedFoundation/ExitChain.h" + +//----------------------------------------------------------------------- + +ConfigPlanetServer::Data * ConfigPlanetServer::data = 0; + +#define KEY_INT(a,b) (data->a = ConfigFile::getKeyInt("PlanetServer", #a, b)) +#define KEY_BOOL(a,b) (data->a = ConfigFile::getKeyBool("PlanetServer", #a, b)) +//#define KEY_FLOAT(a,b) (data->a = ConfigFile::getKeyFloat("PlanetServer", #a, b)) // commented out for Lint. Uncomment if you need this macro again +#define KEY_STRING(a,b) (data->a = ConfigFile::getKeyString("PlanetServer", #a, b)) + +// ====================================================================== + +void ConfigPlanetServer::install(void) +{ + + ConfigServerUtility::install(); + ExitChain::add(&remove, "ConfigPlanetServer::remove"); + + data = new ConfigPlanetServer::Data; + + KEY_STRING (centralServerAddress, "swo-dev1.station.sony.com"); + KEY_INT (centralServerPort, 44455); + KEY_STRING (sceneID, "default"); + KEY_INT (gameServicePort, 0); + KEY_INT (taskManagerPort, 60001); + KEY_INT (watcherServicePort, 60002); + KEY_INT (maxWatcherConnections, 1); + KEY_INT (watcherOverflowLimit, 1024 * 1024 * 8); // 8MB overflow for CS PlanetWatcher tool + KEY_BOOL (logObjectLoading, false); + KEY_INT (maxWatcherUpdatesPerMessage,500); // Max object updates to send to the watcher in a single message + KEY_STRING (gameServiceBindInterface, ""); + KEY_STRING (watcherServiceBindInterface, ""); + KEY_BOOL (loadWholePlanet, false); + KEY_BOOL (loadWholePlanetMultiserver, false); + KEY_BOOL (logPreloading, false); + KEY_INT (numTutorialServers, 1); + KEY_INT (maxInterestRadius, Node::getNodeSize()); + KEY_INT (populationCountTime, 60); // seconds + KEY_BOOL (logChunkLoading, false); + KEY_INT (preloadBailoutTime, 0); // seconds + KEY_STRING (preloadDataTableName, "datatables/planet_server/preload_list.iff"); + KEY_INT (authTransferSanityCheckTimeMs, 15000); + KEY_INT (gameServerRestartDelayTimeSeconds, 60); + KEY_BOOL (enableContentsChecking, false); + KEY_INT (maxGameServers, 0); + KEY_BOOL (enableStartupCreateProxies, true); + KEY_BOOL (requestDbSaveOnGameServerCrash, true); + KEY_BOOL (gameServerProfiling, false); + KEY_BOOL (gameServerDebugging, false); + KEY_INT (gameServerDebuggingPortBase, 0); + KEY_INT (maxTimeToWaitForGameServerStartSeconds, 5*60); // seconds +} + +//----------------------------------------------------------------------- + +bool ConfigPlanetServer::getGameServerProfiling() +{ + return data->gameServerProfiling; +} + +//----------------------------------------------------------------------- + +bool ConfigPlanetServer::getGameServerDebugging() +{ + return data->gameServerDebugging; +} + +//----------------------------------------------------------------------- + +void ConfigPlanetServer::remove(void) +{ + delete data; + data = 0; + ConfigServerUtility::remove(); +} + +//----------------------------------------------------------------------- + +int ConfigPlanetServer::getGameServerDebuggingPortBase() +{ + return data->gameServerDebuggingPortBase; +} + +//----------------------------------------------------------------------- diff --git a/engine/server/application/PlanetServer/src/shared/ConfigPlanetServer.h b/engine/server/application/PlanetServer/src/shared/ConfigPlanetServer.h new file mode 100644 index 00000000..5ccc0a63 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/ConfigPlanetServer.h @@ -0,0 +1,287 @@ +// ====================================================================== +// +// ConfigPlanetServer.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_ConfigPlanetServer_H +#define INCLUDED_ConfigPlanetServer_H + +// ====================================================================== + +class ConfigPlanetServer +{ +public: + struct Data + { + const char * centralServerAddress; + int centralServerPort; + const char * sceneID; + int gameServicePort; + int watcherServicePort; + int taskManagerPort; + int maxWatcherConnections; + int watcherOverflowLimit; + bool logObjectLoading; + int maxWatcherUpdatesPerMessage; + const char * gameServiceBindInterface; + const char * watcherServiceBindInterface; + bool loadWholePlanet; + bool loadWholePlanetMultiserver; + bool logPreloading; + int numTutorialServers; + int maxInterestRadius; + int populationCountTime; + bool logChunkLoading; + int preloadBailoutTime; + const char * preloadDataTableName; + int authTransferSanityCheckTimeMs; + int gameServerRestartDelayTimeSeconds; + bool enableContentsChecking; + int maxGameServers; + bool enableStartupCreateProxies; + bool requestDbSaveOnGameServerCrash; + bool gameServerDebugging; + bool gameServerProfiling; + int gameServerDebuggingPortBase; + int maxTimeToWaitForGameServerStartSeconds; + }; + + static void install (); + static void remove (); + + static const char * getCentralServerAddress (); + static const uint16 getCentralServerPort (); + static const char * getSceneID (); + static const uint16 getGameServicePort (); + static const uint16 getWatcherServicePort (); + static const uint16 getTaskManagerPort (); + static const int getMaxWatcherConnections(); + static const int getWatcherOverflowLimit (); + static const bool getLogObjectLoading (); + static const int getMaxWatcherUpdatesPerMessage(); + static const char * getGameServiceBindInterface(); + static const char * getWatcherServiceBindInterface(); + static const bool getLoadWholePlanet(); + static const bool getLoadWholePlanetMultiserver(); + static const bool getLogPreloading(); + static const int getNumTutorialServers(); + static const int getMaxInterestRadius(); + static const int getPopulationCountTime(); + static const bool getLogChunkLoading(); + static const int getPreloadBailoutTime(); + static const char * getPreloadDataTableName(); + static const int getAuthTransferSanityCheckTimeMs(); + static const unsigned int getGameServerRestartDelayTimeSeconds(); + static bool getEnableContentsChecking(); + static int getMaxGameServers(); + static bool getEnableStartupCreateProxies(); + static bool getRequestDbSaveOnGameServerCrash(); + static bool getGameServerDebugging(); + static bool getGameServerProfiling(); + static int getGameServerDebuggingPortBase(); + static int getMaxTimeToWaitForGameServerStartSeconds(); + private: + static Data * data; +}; + +// ====================================================================== + +inline const char * ConfigPlanetServer::getCentralServerAddress() +{ + return data->centralServerAddress; +} + +//----------------------------------------------------------------------- + +inline const uint16 ConfigPlanetServer::getCentralServerPort() +{ + return static_cast(data->centralServerPort); +} + +//----------------------------------------------------------------------- + +inline const char * ConfigPlanetServer::getSceneID() +{ + return data->sceneID; +} + +//----------------------------------------------------------------------- + +inline const uint16 ConfigPlanetServer::getGameServicePort() +{ + return static_cast(data->gameServicePort); +} + +//----------------------------------------------------------------------- + +inline const uint16 ConfigPlanetServer::getTaskManagerPort() +{ + return static_cast(data->taskManagerPort); +} + +// ---------------------------------------------------------------------- + +inline const uint16 ConfigPlanetServer::getWatcherServicePort() +{ + return static_cast(data->watcherServicePort); +} + +// ---------------------------------------------------------------------- + +inline const int ConfigPlanetServer::getMaxWatcherConnections() +{ + return data->maxWatcherConnections; +} + +//----------------------------------------------------------------------- + +inline const int ConfigPlanetServer::getWatcherOverflowLimit() +{ + return data->watcherOverflowLimit; +} + +// ---------------------------------------------------------------------- + +inline const bool ConfigPlanetServer::getLogObjectLoading() +{ + return data->logObjectLoading; +} + +// ---------------------------------------------------------------------- + +inline const int ConfigPlanetServer::getMaxWatcherUpdatesPerMessage() +{ + return data->maxWatcherUpdatesPerMessage; +} + +// ---------------------------------------------------------------------- + +inline const char * ConfigPlanetServer::getGameServiceBindInterface() +{ + return data->gameServiceBindInterface; +} + +// ---------------------------------------------------------------------- + +inline const char * ConfigPlanetServer::getWatcherServiceBindInterface() +{ + return data->watcherServiceBindInterface; +} + +// ---------------------------------------------------------------------- + +inline const bool ConfigPlanetServer::getLoadWholePlanet() +{ + return data->loadWholePlanet; +} + +// ---------------------------------------------------------------------- + +inline const bool ConfigPlanetServer::getLoadWholePlanetMultiserver() +{ + return data->loadWholePlanetMultiserver; +} + +// ---------------------------------------------------------------------- + +inline const bool ConfigPlanetServer::getLogPreloading() +{ + return data->logPreloading; +} + +// ---------------------------------------------------------------------- + +inline const int ConfigPlanetServer::getNumTutorialServers() +{ + return data->numTutorialServers; +} + +// ---------------------------------------------------------------------- + +inline const int ConfigPlanetServer::getMaxInterestRadius() +{ + return data->maxInterestRadius; +} + +// ---------------------------------------------------------------------- + +inline const int ConfigPlanetServer::getPopulationCountTime() +{ + return data->populationCountTime; +} + +// ---------------------------------------------------------------------- + +inline const bool ConfigPlanetServer::getLogChunkLoading() +{ + return data->logChunkLoading; +} + +// ---------------------------------------------------------------------- + +inline const int ConfigPlanetServer::getPreloadBailoutTime() +{ + return data->preloadBailoutTime; +} + +// ---------------------------------------------------------------------- + +inline const char * ConfigPlanetServer::getPreloadDataTableName() +{ + return data->preloadDataTableName; +} + +// ---------------------------------------------------------------------- + +inline const int ConfigPlanetServer::getAuthTransferSanityCheckTimeMs() +{ + return data->authTransferSanityCheckTimeMs; +} + +// ---------------------------------------------------------------------- + +inline const unsigned int ConfigPlanetServer::getGameServerRestartDelayTimeSeconds() +{ + return static_cast(data->gameServerRestartDelayTimeSeconds); +} + +// ---------------------------------------------------------------------- + +inline bool ConfigPlanetServer::getEnableContentsChecking() +{ + return data->enableContentsChecking; +} + +// ---------------------------------------------------------------------- + +inline int ConfigPlanetServer::getMaxGameServers() +{ + return data->maxGameServers; +} + +// ---------------------------------------------------------------------- + +inline bool ConfigPlanetServer::getEnableStartupCreateProxies() +{ + return data->enableStartupCreateProxies; +} + +// ---------------------------------------------------------------------- + +inline bool ConfigPlanetServer::getRequestDbSaveOnGameServerCrash() +{ + return data->requestDbSaveOnGameServerCrash; +} + +// ---------------------------------------------------------------------- + +inline int ConfigPlanetServer::getMaxTimeToWaitForGameServerStartSeconds() +{ + return data->maxTimeToWaitForGameServerStartSeconds; +} + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/shared/ConsoleCommandParser.cpp b/engine/server/application/PlanetServer/src/shared/ConsoleCommandParser.cpp new file mode 100644 index 00000000..b5839b87 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/ConsoleCommandParser.cpp @@ -0,0 +1,66 @@ +// ConsoleCommandParser.cpp +// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +//----------------------------------------------------------------------- + +#include "FirstPlanetServer.h" +#include "sharedFoundation/NetworkId.h" +#include "sharedLog/Log.h" +#include "ConsoleCommandParser.h" +#include "UnicodeUtils.h" +#include +#include "Unicode.h" + +#pragma warning(disable:4355) // Passing "this" as a parameter to the base class constructor +//----------------------------------------------------------------------- + +namespace CommandNames +{ +#define MAKE_COMMAND(a) const char * const a = #a +#undef MAKE_COMMAND +} + +const CommandParser::CmdInfo cmds[] = +{ + {"planet", 0, "", ""}, + {"", 0, "", ""} // this must be last +}; + + +//----------------------------------------------------------------------- + +ConsoleCommandParser::ConsoleCommandParser() : +CommandParser ("", 0, "...", "console commands", NULL) +{ + createDelegateCommands(cmds); +} + +//----------------------------------------------------------------------- + +ConsoleCommandParser::~ConsoleCommandParser() +{ +} + +//----------------------------------------------------------------------- + +bool ConsoleCommandParser::performParsing(const NetworkId & /* track */, const StringVector_t & argv, const String_t &, String_t & result, const CommandParser *) +{ + bool successResult = false; + + if(isCommand(argv[0], "planet")) + { + if(argv.size() > 2) + { + if(isCommand(argv[2], "runState")) + { + successResult = true; + result += Unicode::narrowToWide("running"); + } + } + } + + return successResult; +} + +//----------------------------------------------------------------------- diff --git a/engine/server/application/PlanetServer/src/shared/ConsoleCommandParser.h b/engine/server/application/PlanetServer/src/shared/ConsoleCommandParser.h new file mode 100644 index 00000000..4ebf6387 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/ConsoleCommandParser.h @@ -0,0 +1,29 @@ +// ConsoleCommandParser.h +// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +#ifndef _INCLUDED_ConsoleCommandParser_H +#define _INCLUDED_ConsoleCommandParser_H + +//----------------------------------------------------------------------- + +#include "sharedCommandParser/CommandParser.h" + +//----------------------------------------------------------------------- + +class ConsoleCommandParser : public CommandParser +{ +public: + ConsoleCommandParser(); + ~ConsoleCommandParser(); + virtual bool performParsing(const NetworkId & userId, const StringVector_t & argv,const String_t & originalCommand,String_t & result,const CommandParser * node); + +private: + ConsoleCommandParser & operator = (const ConsoleCommandParser & rhs); + ConsoleCommandParser(const ConsoleCommandParser & source); +}; + +//----------------------------------------------------------------------- + +#endif // _INCLUDED_ConsoleCommandParser_H + diff --git a/engine/server/application/PlanetServer/src/shared/ConsoleManager.cpp b/engine/server/application/PlanetServer/src/shared/ConsoleManager.cpp new file mode 100644 index 00000000..2326cbf2 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/ConsoleManager.cpp @@ -0,0 +1,58 @@ +// ConsoleManager.cpp +// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +//----------------------------------------------------------------------- + +#include "FirstPlanetServer.h" +#include "sharedFoundation/NetworkId.h" +#include "ConsoleManager.h" +#include "ConsoleCommandParser.h" +#include "UnicodeUtils.h" + +//----------------------------------------------------------------------- + +namespace ConsoleManagerNamespace +{ + ConsoleCommandParser * s_consoleCommandParserRoot = NULL; +} + +using namespace ConsoleManagerNamespace; + +//----------------------------------------------------------------------- + +ConsoleManager::ConsoleManager() +{ +} + +//----------------------------------------------------------------------- + +ConsoleManager::~ConsoleManager() +{ +} + +//----------------------------------------------------------------------- + +void ConsoleManager::install() +{ + s_consoleCommandParserRoot = new ConsoleCommandParser; +} + +//----------------------------------------------------------------------- + +void ConsoleManager::remove() +{ + delete s_consoleCommandParserRoot; +} + +//----------------------------------------------------------------------- + +void ConsoleManager::processString(const std::string & msg, int const track, std::string & result) +{ + Unicode::String wideResult; + IGNORE_RETURN(s_consoleCommandParserRoot->parse(NetworkId(static_cast(track)), Unicode::narrowToWide(msg), wideResult)); + result = Unicode::wideToNarrow(wideResult); +} + +//----------------------------------------------------------------------- + diff --git a/engine/server/application/PlanetServer/src/shared/ConsoleManager.h b/engine/server/application/PlanetServer/src/shared/ConsoleManager.h new file mode 100644 index 00000000..04f79c00 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/ConsoleManager.h @@ -0,0 +1,29 @@ +// ConsoleManager.h +// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +#ifndef _INCLUDED_ConsoleManager_H +#define _INCLUDED_ConsoleManager_H + +#include "sharedFoundation/NetworkId.h" + +#include + +//----------------------------------------------------------------------- + +class ConsoleManager +{ +public: + static void install(); + static void remove(); + static void processString(const std::string & msg, int track, std::string & result); +private: + ConsoleManager & operator = (const ConsoleManager & rhs); + ConsoleManager(const ConsoleManager & source); + ConsoleManager(); + ~ConsoleManager(); +}; + +//----------------------------------------------------------------------- + +#endif // _INCLUDED_ConsoleManager_H diff --git a/engine/server/application/PlanetServer/src/shared/FirstPlanetServer.h b/engine/server/application/PlanetServer/src/shared/FirstPlanetServer.h new file mode 100644 index 00000000..d4d14d3b --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/FirstPlanetServer.h @@ -0,0 +1,24 @@ +// ====================================================================== +// +// FirstPlanetServer.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstPlanetServer_H +#define INCLUDED_FirstPlanetServer_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" +#include "sharedDebug/FirstSharedDebug.h" +#include "sharedMemoryManager/FirstSharedMemoryManager.h" + +#include "sharedFoundation/NetworkIdArchive.h" + +#include "PlanetServer.h" +#include "sharedNetworkMessages/GameNetworkMessage.h" +// ====================================================================== + +#endif + diff --git a/engine/server/application/PlanetServer/src/shared/GameServerConnection.cpp b/engine/server/application/PlanetServer/src/shared/GameServerConnection.cpp new file mode 100644 index 00000000..82eb4d35 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/GameServerConnection.cpp @@ -0,0 +1,177 @@ +// ====================================================================== +// +// GameServerConnection.cpp +// +// Copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "GameServerConnection.h" + +#include "ConfigPlanetServer.h" +#include "PlanetProxyObject.h" +#include "Scene.h" +#include "sharedLog/Log.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" + +// ====================================================================== + +GameServerConnection::GameServerConnection(UdpConnectionMT *u, TcpClient *t) : + ServerConnection(u, t), + m_preloadNumber(0), + m_forwardCounts(), + m_forwardDestinationServers(), + m_forwardMessages() +{ +} + +// ---------------------------------------------------------------------- + +GameServerConnection::~GameServerConnection() +{ +} + +// ---------------------------------------------------------------------- + +void GameServerConnection::onConnectionClosed() +{ + ServerConnection::onConnectionClosed(); + + static MessageConnectionCallback m("GameConnectionClosed"); + emitMessage(m); +} + +// ---------------------------------------------------------------------- + +void GameServerConnection::onConnectionOpened() +{ + ServerConnection::onConnectionOpened(); + + LOG("PlanetServerConnections", ("%s got a connection from a GameServer", ConfigPlanetServer::getSceneID())); + + static MessageConnectionCallback m("GameConnectionOpened"); + emitMessage(m); +} + +// ---------------------------------------------------------------------- + +void GameServerConnection::onReceive(Archive::ByteStream const &message) +{ + Archive::ReadIterator ri = message.begin(); + GameNetworkMessage msg(ri); + ri = message.begin(); + + if (!m_forwardDestinationServers.empty()) + { + if (isMessageForwardable(msg.getType())) + { + m_forwardMessages.push_back(std::make_pair(message, m_forwardDestinationServers.back())); + return; + } + else if (msg.isType("EndForward")) + { + if (--m_forwardCounts.back() == 0) + { + m_forwardCounts.pop_back(); + m_forwardDestinationServers.pop_back(); + if (m_forwardDestinationServers.empty()) + pushAndClearObjectForwarding(); + } + return; + } + else if (msg.isType("BeginForward")) + { + GenericValueTypeMessage > const beginForwardMessage(ri); + if (beginForwardMessage.getValue() == m_forwardDestinationServers.back()) + ++m_forwardCounts.back(); + else + { + m_forwardCounts.push_back(1); + m_forwardDestinationServers.push_back(beginForwardMessage.getValue()); + } + return; + } + } + + if (msg.isType("BeginForward")) + { + GenericValueTypeMessage > const beginForwardMessage(ri); + + m_forwardCounts.push_back(1); + m_forwardDestinationServers.push_back(beginForwardMessage.getValue()); + return; + } + + ServerConnection::onReceive(message); +} + +// ---------------------------------------------------------------------- + +void GameServerConnection::pushAndClearObjectForwarding() +{ + GameNetworkMessage const endForwardMessage("EndForward"); + std::vector > > centralForwardMessages; + + { + for (std::vector > >::const_iterator i = m_forwardMessages.begin(); i != m_forwardMessages.end(); ++i) + { + Archive::ByteStream const &msg = (*i).first; + std::vector const &destinationServers = (*i).second; + std::vector centralForwardPids; + + for (std::vector::const_iterator j = destinationServers.begin(); j != destinationServers.end(); ++j) + { + GameServerConnection * const conn = PlanetServer::getInstance().getGameServerConnection(*j); + if (conn) + conn->Connection::send(msg, true); + else + centralForwardPids.push_back(*j); + } + + if (!centralForwardPids.empty()) + centralForwardMessages.push_back(std::make_pair(msg, centralForwardPids)); + } + } + + if (!centralForwardMessages.empty()) + { + ServerConnection * const conn = PlanetServer::getInstance().getCentralServerConnection(); + + if (conn) + { + std::vector const *lastDestinationServers = &(*centralForwardMessages.begin()).second; + bool subBlock = false; + + GenericValueTypeMessage > const beginForwardMessage("BeginForward", *lastDestinationServers); + conn->send(beginForwardMessage, true); + + for (std::vector > >::const_iterator i = centralForwardMessages.begin(); i != centralForwardMessages.end(); ++i) + { + Archive::ByteStream const &msg = (*i).first; + std::vector const &destinationServers = (*i).second; + + if (destinationServers != *lastDestinationServers) + { + if (subBlock) + conn->send(endForwardMessage, true); + GenericValueTypeMessage > const beginForwardMessage("BeginForward", destinationServers); + conn->send(beginForwardMessage, true); + subBlock = true; + lastDestinationServers = &destinationServers; + } + + conn->Connection::send(msg, true); + } + + if (subBlock) + conn->send(endForwardMessage, true); + conn->send(endForwardMessage, true); + } + } + + m_forwardMessages.clear(); +} + +// ====================================================================== + diff --git a/engine/server/application/PlanetServer/src/shared/GameServerConnection.h b/engine/server/application/PlanetServer/src/shared/GameServerConnection.h new file mode 100644 index 00000000..2ab78da3 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/GameServerConnection.h @@ -0,0 +1,59 @@ +// ====================================================================== +// +// GameServerConnection.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_GameServerConnection_H +#define INCLUDED_GameServerConnection_H + +// ====================================================================== + +#include "serverUtility/ServerConnection.h" +#include "sharedFoundation/NetworkId.h" + +// ====================================================================== + +class GameServerConnection: public ServerConnection +{ +public: + GameServerConnection(UdpConnectionMT *, TcpClient *); + virtual ~GameServerConnection(); + virtual void onConnectionClosed(); + virtual void onConnectionOpened(); + virtual void onReceive(Archive::ByteStream const &message); + + void setPreloadNumber(int preloadNumber); + int getPreloadNumber() const; + +private: + GameServerConnection(GameServerConnection const &); + GameServerConnection &operator=(GameServerConnection const &); + GameServerConnection(); + + void pushAndClearObjectForwarding(); + + int m_preloadNumber; + std::vector m_forwardCounts; + std::vector > m_forwardDestinationServers; + std::vector > > m_forwardMessages; +}; + +//----------------------------------------------------------------------- + +inline void GameServerConnection::setPreloadNumber(int preloadNumber) +{ + m_preloadNumber = preloadNumber; +} + +//----------------------------------------------------------------------- + +inline int GameServerConnection::getPreloadNumber() const +{ + return m_preloadNumber; +} + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/shared/GameServerData.cpp b/engine/server/application/PlanetServer/src/shared/GameServerData.cpp new file mode 100644 index 00000000..58a57f31 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/GameServerData.cpp @@ -0,0 +1,103 @@ +// ====================================================================== +// +// GameServerData.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "GameServerData.h" + +#include "ConfigPlanetServer.h" +#include "GameServerConnection.h" +#include "PlanetServer.h" +#include "sharedLog/Log.h" + +// ====================================================================== + +GameServerData::GameServerData(GameServerConnection *connection) : + m_connection(connection), + m_objectCount(0), + m_interestObjectCount(0), + m_interestCreatureObjectCount(0), + m_serverStatus(SS_unready) +{ +} + +// ---------------------------------------------------------------------- + +GameServerData::GameServerData(const GameServerData &rhs) : + m_connection(NULL), // connection pointer is not copied when we copy GameServerDatas + m_objectCount(rhs.m_objectCount), + m_interestObjectCount(rhs.m_interestObjectCount), + m_interestCreatureObjectCount(rhs.m_interestCreatureObjectCount), + m_serverStatus(rhs.m_serverStatus) +{ +} + +// ---------------------------------------------------------------------- + +GameServerData::GameServerData() : + m_connection(NULL), + m_objectCount(0), + m_interestObjectCount(0), + m_interestCreatureObjectCount(0), + m_serverStatus(SS_unready) +{ +} + +// ---------------------------------------------------------------------- + +GameServerData &GameServerData::operator=(const GameServerData &rhs) +{ + if (&rhs == this) + return *this; + + m_connection=NULL; // connection pointer is not copied when we copy GameServerDatas + m_objectCount=rhs.m_objectCount; + m_interestObjectCount=rhs.m_interestObjectCount; + m_interestCreatureObjectCount=rhs.m_interestCreatureObjectCount; + m_serverStatus=rhs.m_serverStatus; + return *this; +} + +// ---------------------------------------------------------------------- + +void GameServerData::universeLoaded() +{ + WARNING_DEBUG_FATAL(m_serverStatus != SS_readyForObjects,("Got UniverseLoaded for server %d when it was not in the \"readyForObjects\" state.",getProcessId())); + m_serverStatus = SS_loadedUniverseObjects; +} + +// ---------------------------------------------------------------------- + +void GameServerData::ready() +{ + WARNING_DEBUG_FATAL(m_serverStatus != SS_unready,("Got GameServerReady for server %d when it was not in the \"unready\" state.",getProcessId())); + m_serverStatus = SS_readyForObjects; +} + +// ---------------------------------------------------------------------- + +void GameServerData::preloadComplete() +{ + WARNING_DEBUG_FATAL(PlanetServer::getInstance().getEnablePreload() && (m_serverStatus != SS_loadedUniverseObjects),("Got prelaod complete for server %d when it was not in the \"loadedUniverseObjects\" state.",getProcessId())); + m_serverStatus = SS_running; +} + +// ---------------------------------------------------------------------- + +uint32 GameServerData::getProcessId() const +{ + NOT_NULL(m_connection); + return m_connection->getProcessId(); +} + +// ---------------------------------------------------------------------- + +void GameServerData::debugOutputData() const +{ + LOG("LoadBalancing", ("Server %d: %i objects, %i interest objects",getProcessId(),getObjectCount(),getInterestObjectCount())); +} + +// ====================================================================== diff --git a/engine/server/application/PlanetServer/src/shared/GameServerData.h b/engine/server/application/PlanetServer/src/shared/GameServerData.h new file mode 100644 index 00000000..f25d3340 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/GameServerData.h @@ -0,0 +1,119 @@ +// ====================================================================== +// +// GameServerData.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_GameServerData_H +#define INCLUDED_GameServerData_H + +// ====================================================================== + +class GameServerConnection; + +// ====================================================================== + +class GameServerData +{ +public: + explicit GameServerData(GameServerConnection *connection); + GameServerData(const GameServerData &rhs); + GameServerData(); + GameServerData &operator=(const GameServerData &rhs); + + public: + enum ServerStatus {SS_unready, SS_readyForObjects, SS_loadedUniverseObjects, SS_running}; + + public: + GameServerConnection * getConnection (); + int getObjectCount () const; + int getInterestObjectCount () const; + int getInterestCreatureObjectCount() const; + ServerStatus getServerStatus () const; + uint32 getProcessId () const; + void debugOutputData () const; + + void adjustObjectCount (int adjustment); + void adjustInterestObjectCount (int adjustment); + void adjustInterestCreatureObjectCount (int adjustment); + void universeLoaded (); + void ready (); + void preloadComplete (); + + bool isRunning () const; + +private: + GameServerConnection * m_connection; + int m_objectCount; + int m_interestObjectCount; + int m_interestCreatureObjectCount; + ServerStatus m_serverStatus; +}; + +// ====================================================================== + +inline GameServerConnection *GameServerData::getConnection() +{ + return m_connection; +} + +// ---------------------------------------------------------------------- + +inline int GameServerData::getObjectCount() const +{ + return m_objectCount; +} + +// ---------------------------------------------------------------------- + +inline void GameServerData::adjustObjectCount(int adjustment) +{ + m_objectCount+=adjustment; +} + +// ---------------------------------------------------------------------- + +inline void GameServerData::adjustInterestObjectCount(int adjustment) +{ + m_interestObjectCount+=adjustment; +} + +// ---------------------------------------------------------------------- + +inline GameServerData::ServerStatus GameServerData::getServerStatus() const +{ + return m_serverStatus; +} + +// ---------------------------------------------------------------------- + +inline int GameServerData::getInterestObjectCount() const +{ + return m_interestObjectCount; +} + +// ---------------------------------------------------------------------- + +inline bool GameServerData::isRunning() const +{ + return m_serverStatus == SS_running; +} + +// ---------------------------------------------------------------------- + +inline int GameServerData::getInterestCreatureObjectCount() const +{ + return m_interestCreatureObjectCount; +} + +// ---------------------------------------------------------------------- + +inline void GameServerData::adjustInterestCreatureObjectCount (int adjustment) +{ + m_interestCreatureObjectCount+=adjustment; +} + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/shared/PlanetProxyObject.cpp b/engine/server/application/PlanetServer/src/shared/PlanetProxyObject.cpp new file mode 100644 index 00000000..0fff2eb7 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/PlanetProxyObject.cpp @@ -0,0 +1,701 @@ +// ====================================================================== +// +// PlanetProxyObject.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "PlanetProxyObject.h" + +#include "ConfigPlanetServer.h" +#include "GameServerData.h" +#include "PlanetServer.h" +#include "QuadtreeNode.h" +#include "Scene.h" +#include "WatcherConnection.h" +#include "serverNetworkMessages/LoadObjectMessage.h" +#include "serverNetworkMessages/SetAuthoritativeMessage.h" +#include "serverNetworkMessages/UnloadProxyMessage.h" +#include "sharedFoundation/Clock.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedFoundation/MemoryBlockManager.h" +#include "sharedFoundation/Tag.h" +#include "sharedLog/Log.h" +#include "sharedMathArchive/TransformArchive.h" + +#include + +// ====================================================================== + +MemoryBlockManager* PlanetProxyObject::memoryBlockManager; + +// ====================================================================== + +PlanetProxyObject::PlanetProxyObject (const NetworkId &objectId) : + m_objectId(objectId), + m_containedBy(NetworkId::cms_invalid), + m_x(0), + m_y(0), + m_z(0), + m_authoritativeServer(0), + m_lastReportedServer(0), + m_interestRadius(0), + m_quadtreeNode(NULL), + m_authTransferTimeMs(Clock::timeMs()), + m_contents(NULL), + m_level(0), + m_hibernating(false), + m_templateCrc(0), + m_aiActivity(-1), + m_creationType(-1) +{ +} + +// ---------------------------------------------------------------------- + +PlanetProxyObject::~PlanetProxyObject () +{ + if (PlanetServer::getInstance().isWatcherPresent()) + { + outputStatusToAll(true); + } + m_quadtreeNode=0; + + if (m_contents) + { + for (std::vector::const_iterator i=m_contents->begin(); i!=m_contents->end(); ++i) + WARNING(true,("Object %s was deleted without removing contained object %s first",m_objectId.getValueString().c_str(), i->getValueString().c_str())); + delete m_contents; + m_contents=0; + } +} + +// ---------------------------------------------------------------------- + +/** + * Update an object with new data sent from a game server. + * Checks to see what changed, then takes appropriate actions. + */ +void PlanetProxyObject::update(int x, int y, int z, NetworkId containedBy, uint32 authoritativeServer, int interestRadius, int objectTypeTag, int const level, bool const hibernating, uint32 const templateCrc, int const aiActivity, int const creationType) +{ + WARNING_DEBUG_FATAL(m_objectId==NetworkId::cms_invalid,("Object ID was 0.")); + bool watcherUpdateNeeded = false; + + m_level = level; + m_hibernating = hibernating; + m_templateCrc = templateCrc; + m_aiActivity = aiActivity; + m_creationType = creationType; + + if ((m_authoritativeServer != 0 ) && (authoritativeServer != m_authoritativeServer) && (containedBy == NetworkId::cms_invalid)) + { + static unsigned long authTransferSanityCheckTimeMs = ConfigPlanetServer::getAuthTransferSanityCheckTimeMs(); + if (Clock::timeMs()-m_authTransferTimeMs > authTransferSanityCheckTimeMs) + { + WARNING(true, ("Resending auth transfer for %s to %lu due to receiving a stale position update.", m_objectId.getValueString().c_str(), authoritativeServer)); + sendAuthorityChange(authoritativeServer, m_authoritativeServer, false); + return; + } + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Ignoring update for %s from %lu because server was not authoritative.",m_objectId.getValueString().c_str(),authoritativeServer)); + return; + } + + if (interestRadius>ConfigPlanetServer::getMaxInterestRadius()) + interestRadius = ConfigPlanetServer::getMaxInterestRadius(); + + if (interestRadius>0 && PlanetServer::getInstance().isInTutorialMode()) + interestRadius = 1; + + if (containedBy!=NetworkId::cms_invalid) + { + // get coordinates from container + PlanetProxyObject *container = Scene::getInstance().findObjectByID(containedBy); + if (container) + { + x=container->getX(); + y=container->getY(); + z=container->getZ(); + } + else + { + LOG("PlanetUpdate",("Game server said object %s was contained by object %s, but we could not find the container.",m_objectId.getValueString().c_str(),containedBy.getValueString().c_str())); + containedBy = NetworkId::cms_invalid; // so that we'll try again when the object moves again, in case we just got objects out-of-order from the game server + } + } + + std::vector oldProxyList; + + Node *newNode=Scene::getInstance().findNodeByPosition(x,z); + NOT_NULL(newNode); + if (newNode!=m_quadtreeNode || m_interestRadius != interestRadius || m_authoritativeServer != authoritativeServer || m_containedBy != containedBy) + { + watcherUpdateNeeded = true; + + // For debugging, update the contents of the containers + if (ConfigPlanetServer::getEnableContentsChecking() && (m_containedBy!=containedBy)) + updateContentsTracking(containedBy); + + bool const firstUpdate = (m_quadtreeNode == NULL); + + if (m_quadtreeNode!=NULL) // if this is the first update for this object, m_quadtreeNode will be NULL because it hasn't been placed anywhere yet + { + removeServerStatistics(); + + // remember old list of proxies + getServers(oldProxyList); + + // remove from the old location + m_quadtreeNode->removeObject(this); + + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading", ("unsubscribing nodes for object id=[%s]", m_objectId.getValueString().c_str())); + unsubscribeSurroundingNodes(false); + } + + // update data + m_quadtreeNode=newNode; + m_x=x; + m_y=y; + m_z=z; + m_interestRadius=interestRadius; + m_objectTypeTag = objectTypeTag; + m_authoritativeServer=authoritativeServer; + m_containedBy=containedBy; + + // make sure the destination is loaded + if (!newNode->isLoaded()) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Loading node %s because object %s entered it.",newNode->getDebugNodeString().c_str(), m_objectId.getValueString().c_str())); + newNode->load(); + } + + // check if we're on the right server + if (!isAuthorityOk()) + { + uint32 preferredServer = m_quadtreeNode->getPreferredServer(); + if (m_containedBy==NetworkId::cms_invalid) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Changing authority for object %s from %lu to %lu because node %s is authoritative on that server.", + m_objectId.getValueString().c_str(),m_authoritativeServer,preferredServer,m_quadtreeNode->getDebugNodeString().c_str())); + changeAuthority(preferredServer, false, false); // changes m_authoritativeServer and sends message + + // we received the object for the first time, and we had to + // transfer the object's authority, so we need to make sure + // to unload the object on the current authoritative server, + // if necessary + if (firstUpdate) + oldProxyList.push_back(authoritativeServer); + } + else + { + WARNING(true,("Programmer bug: Object %s is in container %s, which is node %s. The object should be authoritative on server %i but it is authoritative on server %i.", + m_objectId.getValueString().c_str(),m_containedBy.getValueString().c_str(),m_quadtreeNode->getDebugNodeString().c_str(), m_authoritativeServer, preferredServer)); + } + } + + // place in the new location + newNode->addObject(this); + + if (interestRadius > 0) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading", ("subscribing nodes for object id=[%s]", m_objectId.getValueString().c_str())); + subscribeSurroundingNodes(); + } + + // check against new interested servers, and send proxy/unproxy as needed + if (m_containedBy==NetworkId::cms_invalid) // we don't manage proxies for containers + { + std::vector newProxyList; + getServers(newProxyList); + changeProxies(oldProxyList,newProxyList); + } + + addServerStatistics(); + } + else + { + if (m_interestRadius > 0 || (abs(m_x - x) > 25) || (abs(m_z - z) > 25)) + watcherUpdateNeeded = true; + } + + if (PlanetServer::getInstance().isWatcherPresent() && watcherUpdateNeeded) + { + m_x=x; + m_y=y; + m_z=z; + m_objectTypeTag = objectTypeTag; + + outputStatusToAll(false); + } +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::unsubscribeSurroundingNodes(bool clearZeroSubscriptions) +{ + if (m_interestRadius <= 0) + return; + + Scene::NodeListType nodelist; + Scene::getInstance().findIntersection(nodelist,m_x,m_z,m_interestRadius); + + for (Scene::NodeListType::iterator i=nodelist.begin(); i!=nodelist.end(); ++i) + { + (*i)->unsubscribeServer(m_authoritativeServer, 1, clearZeroSubscriptions); + } +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::subscribeSurroundingNodes() +{ + if (m_interestRadius <= 0) + return; + + NOT_NULL(m_quadtreeNode); + Scene::NodeListType nodelist; + Scene::getInstance().findIntersection(nodelist,m_x,m_z,m_interestRadius); + + for (Scene::NodeListType::iterator i=nodelist.begin(); i!=nodelist.end(); ++i) + { + (*i)->subscribeServer(m_authoritativeServer, 1); + uint32 serverId = (*i)->getPreferredServer(); + if (serverId != m_authoritativeServer) + m_quadtreeNode->subscribeServer(serverId,0); // make sure if we can see them, they can see us + } +} + +// ---------------------------------------------------------------------- + +/** + * Restore the object's authority to the specified game server. + */ +void PlanetProxyObject::restoreAuthority(uint32 gameServer) +{ + m_authoritativeServer = gameServer; + m_lastReportedServer = gameServer; + + // Send queued messages + PlanetServer::getInstance().sendQueuedMessagesForObject(*this); +} + +// ---------------------------------------------------------------------- + +/** + * Make the object authortiative on a different game server. Sends a + * message to the game servers to change authority and updates + * m_authoritativeServer. + */ +void PlanetProxyObject::changeAuthority(uint32 newGameServer, bool handlingCrash, bool forcedByGame) +{ + if (handlingCrash) + sendAuthorityChange(newGameServer, newGameServer, true); + else if (!forcedByGame) + sendAuthorityChange(m_authoritativeServer, newGameServer, false); + m_authoritativeServer = newGameServer; +} + +// ---------------------------------------------------------------------- + +/** + * Make the object authoritative on a different game server and adjust the + * subscription counts. + */ +void PlanetProxyObject::changeAuthorityAndSubscriptions(uint32 newGameServer) +{ + unsubscribeSurroundingNodes(false); + changeAuthority(newGameServer, false, false); + subscribeSurroundingNodes(); +} + +// ---------------------------------------------------------------------- + +/** + * Called when an object should be unloaded from the world. The object + * should be removed from the active game servers, but not deleted from + * the database. + * + * This function also removes the PlanetProxyObject from all data structures. + * On exit, it is legal to delete the PlanetProxyObject. + * + * @param sendToGameServer Set to true if ForceUnloadObjectMessage should be + * sent to the game server. In some cases there is no need to tell the game + * server to unload the object (such as when handling a crash, for example). + */ +void PlanetProxyObject::unload(bool tellGameServer) +{ + if (m_quadtreeNode) + m_quadtreeNode->removeObject(this); + + Scene::getInstance().removeObjectFromMap(m_objectId,tellGameServer); + + if (tellGameServer) + { + PlanetServer::getInstance().unloadObject(m_objectId,m_authoritativeServer); + } + + removeServerStatistics(); + + if (ConfigPlanetServer::getEnableContentsChecking() && m_containedBy!=NetworkId::cms_invalid) + { + PlanetProxyObject *container = Scene::getInstance().findObjectByID(m_containedBy); + if (container) + container->removeContainedObject(m_objectId); + else + WARNING(true,("Unloading object %s in container %s, but the container could not be found.",m_objectId.getValueString().c_str(), m_containedBy.getValueString().c_str())); + } +} + +// ---------------------------------------------------------------------- + +/** + * Output the object's position to a watcher. + * @param conn the watcher + * @param deleteObject if true, tell the watcher to delete this object. + */ +void PlanetProxyObject::outputStatus(WatcherConnection &conn, bool deleteObject) const +{ + conn.addObjectUpdate(m_objectId, m_x, m_z, m_authoritativeServer, m_interestRadius, deleteObject, m_objectTypeTag, m_level, m_hibernating, m_templateCrc, m_aiActivity, m_creationType); +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::outputStatusToAll(bool deleteObject) const +{ + const PlanetServer::WatcherList &connections = PlanetServer::getInstance().getWatchers(); + for (PlanetServer::WatcherList::const_iterator i= connections.begin(); i!=connections.end(); ++i) + { + outputStatus(**i, deleteObject); + } +} + +// ---------------------------------------------------------------------- + +/** + * Update the GameServerData with statistics about this object. + */ +void PlanetProxyObject::addServerStatistics() const +{ + GameServerData *data=PlanetServer::getInstance().getGameServerData(m_authoritativeServer); + if (data) + { + data->adjustObjectCount(1); + if (m_interestRadius!=0) + { + data->adjustInterestObjectCount(1); + if (isCreature()) + data->adjustInterestCreatureObjectCount(1); + } + } +} + +// ---------------------------------------------------------------------- + +/** + * Remove statistics about this object from the GameServerData. + * Should exactly undo addServerStatistics(). + */ +void PlanetProxyObject::removeServerStatistics() const +{ + GameServerData *data=PlanetServer::getInstance().getGameServerData(m_authoritativeServer); + if (data) + { + data->adjustObjectCount(-1); + if (m_interestRadius!=0) + { + data->adjustInterestObjectCount(-1); + if (isCreature()) + data->adjustInterestCreatureObjectCount(-1); + } + } +} + +// ---------------------------------------------------------------------- + +/** + * Set up memory pool + */ +void PlanetProxyObject::install() +{ + memoryBlockManager = new MemoryBlockManager("PlanetProxyObject::memoryBlockManager", true, sizeof(PlanetProxyObject), 0, 0, 0); + ExitChain::add(&remove, "PlanetProxyObject::remove"); +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::remove() +{ + WARNING_DEBUG_FATAL(!memoryBlockManager, ("PlanetProxyObject is not installed")); + delete memoryBlockManager; + memoryBlockManager = 0; +} + +// ---------------------------------------------------------------------- + +void * PlanetProxyObject::operator new(size_t size) +{ + UNREF(size); + + WARNING_DEBUG_FATAL(!memoryBlockManager, ("PlanetProxyObject is not installed")); + + // do not try to alloc a descendant class with this allocator + WARNING_DEBUG_FATAL(size != sizeof(PlanetProxyObject), ("bad size")); + + return memoryBlockManager->allocate(); +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::operator delete(void* pointer) +{ + WARNING_DEBUG_FATAL(!memoryBlockManager, ("PlanetProxyObject is not installed")); + memoryBlockManager->free(pointer); +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::sendAuthorityChange(uint32 currentAuthServer, uint32 newAuthServer, bool handlingCrash) +{ + DEBUG_WARNING(!isAuthorityClean(), ("Object %s attempted to change authority before previous authority change was complete.",getObjectId().getValueString().c_str())); + + SetAuthoritativeMessage const auth(getObjectId(), newAuthServer, false, handlingCrash, NetworkId::cms_invalid, Transform::identity, false); + PlanetServer::getInstance().sendToGameServer(currentAuthServer, auth); + m_authTransferTimeMs = Clock::timeMs(); +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::sendAddProxy(uint32 proxyServer) +{ + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Gameserver %lu needs a proxy of object %s",proxyServer,m_objectId.getValueString().c_str())); + if (isAuthorityClean()) + { + LoadObjectMessage const loadMsg(getObjectId(), proxyServer); + PlanetServer::getInstance().sendToGameServer(m_authoritativeServer, loadMsg); + } + else + { + LoadObjectMessage * const loadMsg = new LoadObjectMessage(getObjectId(), proxyServer); + PlanetServer::getInstance().queueMessageForObject(getObjectId(), loadMsg); + } +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::sendRemoveProxy(uint32 proxyServer) +{ + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Gameserver %lu no longer needs a proxy of object %s",proxyServer,m_objectId.getValueString().c_str())); + if (isAuthorityClean()) + { + UnloadProxyMessage msg(getObjectId(),proxyServer); + PlanetServer::getInstance().sendToGameServer(m_authoritativeServer, msg); + } + else + { + UnloadProxyMessage *msg = new UnloadProxyMessage(getObjectId(),proxyServer); + PlanetServer::getInstance().queueMessageForObject(getObjectId(), msg); + } +} + +// ---------------------------------------------------------------------- + +/** + * Called when we receieve a message about the object from a game server. + * Used to keep track of which server is sending us updates, and to send + * any messages that were waiting for an authority change. + */ +void PlanetProxyObject::onReceivedMessageFromServer(uint32 server) +{ + if (m_lastReportedServer != m_authoritativeServer && server == m_authoritativeServer) + // we've received a message from the new authoritative server. Send queued messages: + PlanetServer::getInstance().sendQueuedMessagesForObject(*this); + + m_lastReportedServer = server; +} + +// ---------------------------------------------------------------------- + +/** + * Get the list of servers that should have proxies of this object. + */ +void PlanetProxyObject::getServers(std::vector &serverList) const +{ + Node *effectiveNode; + if (m_containedBy == NetworkId::cms_invalid) + { + effectiveNode = m_quadtreeNode; + } + else + { + PlanetProxyObject *container=Scene::getInstance().findObjectByID(m_containedBy); + WARNING_STRICT_FATAL(!container,("Object %s is in container %s, which couldn't be found",m_objectId.getValueString().c_str(),m_containedBy.getValueString().c_str())); + if (container) + effectiveNode = container->getNode(); + else + effectiveNode = m_quadtreeNode; + } + + if (effectiveNode) + effectiveNode->getServers(serverList); +} + +// ---------------------------------------------------------------------- + +/** + * Given a list of servers that have a proxy and a list of servers + * that should have a proxy, send messages to update the servers. + * Note: O(n^2), assumes both lists are small, otherwise it would be better + * to sort them first. + */ +void PlanetProxyObject::changeProxies(const std::vector &oldServers, const std::vector &newServers) +{ + { + for (std::vector::const_iterator i=oldServers.begin(); i!=oldServers.end(); ++i) + { + if (*i != m_authoritativeServer) + { + bool removeNeeded = true; + for (std::vector::const_iterator j=newServers.begin(); j!=newServers.end(); ++j) + { + if (*i == *j) + { + removeNeeded = false; + break; + } + } + if (removeNeeded) + sendRemoveProxy(*i); + } + } + } + { + for (std::vector::const_iterator i=newServers.begin(); i!=newServers.end(); ++i) + { + if (*i != m_authoritativeServer) + { + bool addNeeded = true; + for (std::vector::const_iterator j=oldServers.begin(); j!=oldServers.end(); ++j) + { + if (*i == *j) + { + addNeeded = false; + break; + } + } + if (addNeeded) + sendAddProxy(*i); + } + } + } +} + +// ---------------------------------------------------------------------- + +/** + * Check whether we are on a server that's acceptable for where we are. + */ +bool PlanetProxyObject::isAuthorityOk() const +{ + if (m_authoritativeServer == m_quadtreeNode->getPreferredServer()) + return true; + if (m_quadtreeNode->isBorderNode() && m_quadtreeNode->isServerSubscribed(m_authoritativeServer)) + return true; + else + return false; +} + +// ---------------------------------------------------------------------- + +/** + * Check for whether a desired server would be OK for where we are. + * The server is OK if it is the preferred server. If the object is + * a creature, the server is also OK if the node is a border node and + * the server has a subscription to the node. Non-creatures must always + * be on the preferred server. + */ +bool PlanetProxyObject::wouldAuthorityBeOk(uint32 possibleServer) const +{ + if (possibleServer == m_quadtreeNode->getPreferredServer()) + return true; + if (isCreature() && m_quadtreeNode->isBorderNode() && m_quadtreeNode->isServerSubscribed(possibleServer)) + return true; + else + return false; +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::addContainedObject(const NetworkId &theObject) +{ + if (!m_contents) + { + m_contents = new std::vector; + } + for (std::vector::const_iterator i=m_contents->begin(); i!=m_contents->end(); ++i) + { + if (*i==theObject) + WARNING(true,("Object %s was placed in container %s twice.", theObject.getValueString().c_str(), m_objectId.getValueString().c_str())); + } + m_contents->push_back(theObject); +} + +// ---------------------------------------------------------------------- + +void PlanetProxyObject::removeContainedObject(const NetworkId &theObject) +{ + if (!m_contents) + { + WARNING(true,("Attempted to remove object %s from object %s, but that object has no contents.", theObject.getValueString().c_str(), m_objectId.getValueString().c_str())); + return; + } + std::vector::iterator newEnd=std::remove(m_contents->begin(), m_contents->end(), theObject); + if (newEnd==m_contents->end()) + { + WARNING(true,("Attempted to remove object %s from object %s, but it was not in the container.", theObject.getValueString().c_str(), m_objectId.getValueString().c_str())); + return; + } + m_contents->erase(newEnd, m_contents->end()); +} + +// ---------------------------------------------------------------------- + +/** + * Helper function to update the contents tracking lists, in order to + * debug various authority problems + */ +void PlanetProxyObject::updateContentsTracking(const NetworkId &newContainedBy) +{ + if (m_containedBy!=NetworkId::cms_invalid) + { + PlanetProxyObject *container = Scene::getInstance().findObjectByID(m_containedBy); + if (container) + container->removeContainedObject(m_objectId); + else + WARNING(true,("Removing object %s from container %s, but the container could not be found.",m_objectId.getValueString().c_str(), m_containedBy.getValueString().c_str())); + } + + if (newContainedBy!=NetworkId::cms_invalid) + { + PlanetProxyObject *container = Scene::getInstance().findObjectByID(newContainedBy); + if (container) + container->addContainedObject(m_objectId); + else + WARNING(true,("Adding object %s to container %s, but the container could not be found.",m_objectId.getValueString().c_str(), newContainedBy.getValueString().c_str())); + } +} + +// ---------------------------------------------------------------------- + +bool PlanetProxyObject::isCreature() const +{ + return m_objectTypeTag==TAG(C,R,E,O); +} + +// ====================================================================== diff --git a/engine/server/application/PlanetServer/src/shared/PlanetProxyObject.h b/engine/server/application/PlanetServer/src/shared/PlanetProxyObject.h new file mode 100644 index 00000000..d48d81ff --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/PlanetProxyObject.h @@ -0,0 +1,237 @@ +// ====================================================================== +// +// PlanetProxyObject.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_PlanetProxyObject_H +#define INCLUDED_PlanetProxyObject_H + +// ====================================================================== + +#include "sharedFoundation/NetworkId.h" +#include "sharedMath/Vector.h" + +class Node; +class MemoryBlockManager; +class WatcherConnection; + +// ====================================================================== + +/** + * Lightweight proxy of an object, containing just the data the planet server + * cares about. + * + * Coordinates are made into integers because none of the Planet Server's + * algorithms care about sub-meter distances. + */ +class PlanetProxyObject +{ + public: + explicit PlanetProxyObject (const NetworkId &objectId); + ~PlanetProxyObject (); + + public: + void addServerStatistics () const; + void changeAuthority (uint32 newGameServer, bool handlingCrash, bool forcedByGame); + void changeAuthorityAndSubscriptions(uint32 newGameServer); + void onReceivedMessageFromServer (uint32 server); + void outputStatus (WatcherConnection &conn, bool deleteObject) const; + void outputStatusToAll (bool deleteObject) const; + void removeServerStatistics () const; + void restoreAuthority (uint32 gameServer); + void sendAddProxy (uint32 proxyServer); + void sendRemoveProxy (uint32 proxyServer); + void setNode (Node *node); + void subscribeSurroundingNodes (); + void unload (bool tellGameServer); + void unsubscribeSurroundingNodes (bool clearZeroSubscriptions); + void update (int x, int y, int z, NetworkId containedBy, uint32 authoritativeServer, int interestRadius, int objectTypeTag, int level, bool hibernating, uint32 templateCrc, int aiActivity, int creationType); + + public: + int getX () const; + int getY () const; + int getZ () const; + uint32 getAuthoritativeServer () const; + int getInterestRadius () const; + bool isContained () const; + bool isAuthorityClean () const; + const NetworkId & getObjectId () const; + const NetworkId & getContainedBy () const; + Node * getNode () const; + bool wouldAuthorityBeOk (uint32 possibleServer) const; + bool isCreature () const; + int getLevel () const; + bool getHibernating () const; + uint32 getTemplateCrc () const; + int getAiActivity () const; + int getCreationType () const; + + public: + static void * operator new (size_t size); + static void operator delete (void* pointer); + static void install (); + static void remove (); + + private: + void getServers (std::vector &serverList) const; + void changeProxies (const std::vector &oldServers, const std::vector &newServers); + void sendAuthorityChange (uint32 currentAuthServer, uint32 newAuthServer, bool handlingCrash); + bool isAuthorityOk () const; + void addContainedObject (const NetworkId & theObject); + void removeContainedObject (const NetworkId & theObject); + void updateContentsTracking (const NetworkId & newContainedBy); + + private: + typedef stdvector > MessageListType; + + private: + const NetworkId m_objectId; + NetworkId m_containedBy; + int m_x; + int m_y; + int m_z; + uint32 m_authoritativeServer; + uint32 m_lastReportedServer; + int m_interestRadius; + int m_objectTypeTag; + Node * m_quadtreeNode; + unsigned long m_authTransferTimeMs; + stdvector::fwd * m_contents; + int m_level; + bool m_hibernating; + uint32 m_templateCrc; + int m_aiActivity; + int m_creationType; + + static MemoryBlockManager* memoryBlockManager; + + private: + PlanetProxyObject(); //disable + PlanetProxyObject &operator=(const PlanetProxyObject&); //disable + PlanetProxyObject(const PlanetProxyObject&); //disable +}; + +// ====================================================================== + +inline const NetworkId &PlanetProxyObject::getObjectId() const +{ + return m_objectId; +} + +// ---------------------------------------------------------------------- + +inline int PlanetProxyObject::getX() const +{ + return m_x; +} + +// ---------------------------------------------------------------------- + +inline int PlanetProxyObject::getY() const +{ + return m_y; +} + +// ---------------------------------------------------------------------- + +inline int PlanetProxyObject::getZ() const +{ + return m_z; +} + +// ---------------------------------------------------------------------- + +inline int PlanetProxyObject::getLevel() const +{ + return m_level; +} + +// ---------------------------------------------------------------------- + +inline bool PlanetProxyObject::getHibernating() const +{ + return m_hibernating; +} + +// ---------------------------------------------------------------------- + +inline uint32 PlanetProxyObject::getTemplateCrc() const +{ + return m_templateCrc; +} + +// ---------------------------------------------------------------------- + +inline int PlanetProxyObject::getAiActivity() const +{ + return m_aiActivity; +} + +// ---------------------------------------------------------------------- + +inline int PlanetProxyObject::getCreationType() const +{ + return m_creationType; +} + +// ---------------------------------------------------------------------- + +inline uint32 PlanetProxyObject::getAuthoritativeServer() const +{ + return m_authoritativeServer; +} + +// ---------------------------------------------------------------------- + +inline int PlanetProxyObject::getInterestRadius() const +{ + return m_interestRadius; +} + +// ---------------------------------------------------------------------- + +inline void PlanetProxyObject::setNode(Node *node) +{ + m_quadtreeNode=node; +} + +// ---------------------------------------------------------------------- + +/** + * Returns true if the object's authority is "clean", i.e. the Game Servers + * think it is authoritative on the same server the Planet Server does. + * This returns false when we've changed the object's authority until the + * newly authoritative server sends us an update, at which point we know + * that it thinks it is authoritative. + */ +inline bool PlanetProxyObject::isAuthorityClean() const +{ + return m_lastReportedServer == m_authoritativeServer; +} + +// ---------------------------------------------------------------------- + +inline Node *PlanetProxyObject::getNode() const +{ + return m_quadtreeNode; +} //lint !e1763 + +// ---------------------------------------------------------------------- + +inline const NetworkId &PlanetProxyObject::getContainedBy() const +{ + return m_containedBy; +} + +// ---------------------------------------------------------------------- + +inline bool PlanetProxyObject::isContained() const +{ + return (m_containedBy != NetworkId::cms_invalid); +} + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/shared/PlanetServer.cpp b/engine/server/application/PlanetServer/src/shared/PlanetServer.cpp new file mode 100644 index 00000000..a1714f6f --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/PlanetServer.cpp @@ -0,0 +1,1523 @@ +// ====================================================================== +// +// PlanetServer.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "PlanetServer.h" + +#include "CentralServerConnection.h" +#include "ConfigPlanetServer.h" +#include "ConsoleManager.h" +#include "GameServerConnection.h" +#include "GameServerData.h" +#include "PlanetProxyObject.h" +#include "PlanetServerMetricsData.h" +#include "QuadtreeNode.h" +#include "Scene.h" +#include "TaskConnection.h" +#include "WatcherConnection.h" +#include "serverMetrics/MetricsManager.h" +#include "serverNetworkMessages/ChunkCompleteMessage.h" +#include "serverNetworkMessages/ExcommunicateGameServerMessage.h" +#include "serverNetworkMessages/FactionalSystemMessage.h" +#include "serverNetworkMessages/FirstPlanetGameServerIdMessage.h" +#include "serverNetworkMessages/GameGameServerMessages.h" // we use the GameGameServerConnect message +#include "serverNetworkMessages/GameServerForLoginMessage.h" +#include "serverNetworkMessages/GameServerForceChangeAuthorityMessage.h" +#include "serverNetworkMessages/GameServerReadyMessage.h" +#include "serverNetworkMessages/GameServerUniverseLoadedMessage.h" +#include "serverNetworkMessages/LoadStructureMessage.h" +#include "serverNetworkMessages/LoadStructureMessage.h" +#include "serverNetworkMessages/LocateStructureMessage.h" +#include "serverNetworkMessages/LocationRequest.h" +#include "serverNetworkMessages/LocationResponse.h" +#include "serverNetworkMessages/PersistedPlayerMessage.h" +#include "serverNetworkMessages/PlanetLoadCharacterMessage.h" +#include "serverNetworkMessages/PreloadFinishedMessage.h" +#include "serverNetworkMessages/PreloadRequestCompleteMessage.h" +#include "serverNetworkMessages/ProfilerOperationMessage.h" +#include "serverNetworkMessages/RequestChunkMessage.h" +#include "serverNetworkMessages/RequestGameServerForLoginMessage.h" +#include "serverNetworkMessages/RestartServerMessage.h" +#include "serverNetworkMessages/SceneTransferMessages.h" +#include "serverNetworkMessages/TaskProcessDiedMessage.h" +#include "serverNetworkMessages/TaskSpawnProcess.h" +#include "serverNetworkMessages/UnloadObjectMessage.h" +#include "serverNetworkMessages/UnloadProxyMessage.h" +#include "serverNetworkMessages/UnloadedPlayerMessage.h" +#include "serverNetworkMessages/UpdateObjectOnPlanetMessage.h" +#include "serverUtility/PopulationList.h" +#include "serverUtility/SetupServerUtility.h" +#include "sharedDebug/Profiler.h" +#include "sharedFoundation/CalendarTime.h" +#include "sharedFoundation/Clock.h" +#include "sharedFoundation/ConfigFile.h" +#include "sharedFoundation/Os.h" +#include "sharedFoundation/Timer.h" +#include "sharedLog/Log.h" +#include "sharedMathArchive/VectorArchive.h" +#include "sharedNetwork/Connection.h" +#include "sharedNetwork/NetworkSetupData.h" +#include "sharedNetwork/Service.h" +#include "sharedNetworkMessages/FrameEndMessage.h" +#include "sharedNetworkMessages/GalaxyLoopTimesResponse.h" +#include "sharedNetworkMessages/GameServerStatus.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" +#include "sharedNetworkMessages/ServerInfo.h" +#include "sharedUtility/LocationManager.h" +#include "unicodeArchive/UnicodeArchive.h" + +#include +#include +#include + +// ====================================================================== + +static const std::string loginTrace("TRACE_LOGIN"); + +// ====================================================================== + +struct CharacterFindInfo +{ + unsigned int sequence; + NetworkId containerId; + Vector coords; + uint32 stationId; + int pendingResponseCount; + bool forCtsSourceCharacter; +}; + +namespace PlanetServerNamespace +{ + std::vector m_pendingChunkRequests; + std::vector > m_pendingPreloadRequestCompletes; +} +using namespace PlanetServerNamespace; + +// ====================================================================== + +PlanetServer::PlanetServer() : + Singleton(), + MessageDispatch::Receiver(), + m_pendingCentralServerConnection(NULL), + m_centralServerConnection(NULL), + m_gameService(NULL), + m_watcherService(NULL), + m_gameServers(), + m_taskConnection(NULL), + m_done(false), + m_roundRobinGameServer(0), + m_pendingServerStarts(new std::map()), + m_startingGameServers(new std::map >()), + m_firstGameServer(0), + m_tutorialMode(false), + m_spaceMode(false), + m_messagesWaitingForGameServer(), + m_metricsData(0), + m_taskManagerConnection(NULL), + m_sceneTransferChunkLoads(new std::list), + m_pendingCharacterSaves(new std::map), + m_watchers(), + m_watcherIsPresent(false), + m_queuedMessages(), + m_characterFindMap(new std::map), + m_characterFindSequence(0), + m_waitForSaveCounter(0), + m_lastSaveCounter(0) +{ + SetupServerUtility::install(); + ConsoleManager::install(); + // set up central server connection + m_pendingCentralServerConnection = new CentralServerConnection(ConfigPlanetServer::getCentralServerAddress(), ConfigPlanetServer::getCentralServerPort()); + + // connect to the task manager + // jrandall, set on connection + m_taskConnection = new TaskConnection("127.0.0.1", ConfigPlanetServer::getTaskManagerPort()); + + // set up game server connection + NetworkSetupData setup; + setup.port = ConfigPlanetServer::getGameServicePort(); + setup.bindInterface = ConfigPlanetServer::getGameServiceBindInterface(); + setup.maxConnections = 100; + m_gameService = new Service(ConnectionAllocator(), setup); + + // set up watcher connection + if (ConfigPlanetServer::getMaxWatcherConnections() > 0) + { + setup.port = ConfigPlanetServer::getWatcherServicePort(); + setup.maxConnections = 1; + setup.bindInterface = ConfigPlanetServer::getWatcherServiceBindInterface(); + m_watcherService = new Service(ConnectionAllocator(), setup); + } + + connectToMessage("CentralConnectionClosed"); + connectToMessage("CentralConnectionOpened"); + connectToMessage("ChunkCompleteMessage"); + connectToMessage("DatabaseSaveComplete"); + connectToMessage("DenySetAuthoritativeMessage"); + connectToMessage("ExcommunicateGameServerMessage"); + connectToMessage("FactionalSystemMessage"); + connectToMessage("MessageToPlayersOnPlanet"); + connectToMessage("FindAuthObjectResponse"); + connectToMessage("FrameEndMessage"); + connectToMessage("GameConnectionClosed"); + connectToMessage("GameGameServerConnect"); + connectToMessage("GameServerForceChangeAuthorityMessage"); + connectToMessage("GameServerReadyMessage"); + connectToMessage("GameServerUniverseLoadedMessage"); + connectToMessage("LocateStructureMessage"); + connectToMessage("LocationRequest"); + connectToMessage("PersistedPlayerMessage"); + connectToMessage("PreloadListMessage"); + connectToMessage("PreloadRequestCompleteMessage"); + connectToMessage("ProfilerOperationMessage"); + connectToMessage("RequestAuthTransfer"); + connectToMessage("RequestGameServerForLoginMessage"); + connectToMessage("RequestSameServer"); + connectToMessage("RequestSceneTransfer"); + connectToMessage("RestartServerByRoleMessage"); + connectToMessage("RestartServerMessage"); + connectToMessage("SAMGS"); + connectToMessage("ShutdownMessage"); + connectToMessage("TaskConnectionOpened"); + connectToMessage("TaskProcessDiedMessage"); + connectToMessage("UnloadedPlayerMessage"); + connectToMessage("WatcherConnectionClosed"); + connectToMessage("WatcherConnectionOpened"); + + m_metricsData = new PlanetServerMetricsData; + std::string sceneName = ConfigPlanetServer::getSceneID() ; + MetricsManager::install(m_metricsData, false, "PlanetServer" , sceneName, 0); + if (sceneName == "tutorial") + { + DEBUG_REPORT_LOG(true,("Running in tutorial mode\n")); + m_tutorialMode=true; // tutorial mode can't be set in the config file because all planet servers have the same config file + } + if (strncmp(sceneName.c_str(),"space_",6)==0) + { + DEBUG_REPORT_LOG(true,("Running in space mode\n")); + m_spaceMode=true; // space mode can't be set in the config file because all planet servers have the same config file + } + + PlanetProxyObject::install(); + PreloadManager::install(static_cast(0)); + Node::install(); + Scene::install(static_cast(0)); // this syntax is required by MSVC to identify the template function +} + +//----------------------------------------------------------------------- + +PlanetServer::~PlanetServer() +{ + ConsoleManager::remove(); + MetricsManager::remove(); + + m_centralServerConnection = 0; + m_gameService = 0; + m_metricsData = 0; + m_pendingCentralServerConnection = 0; + m_taskConnection = 0; + m_taskManagerConnection = 0; + m_watcherService = 0; + + delete m_pendingServerStarts; + m_pendingServerStarts = 0; + delete m_startingGameServers; + m_startingGameServers = 0; + + std::vector::iterator t; + for(t = m_messagesWaitingForGameServer.begin(); t != m_messagesWaitingForGameServer.end();++t) + { + delete *t; //lint !e605 // deleting const + *t = 0; + } + + m_messagesWaitingForGameServer.clear(); + + while (m_sceneTransferChunkLoads->begin() != m_sceneTransferChunkLoads->end()) + { + delete *m_sceneTransferChunkLoads->begin(); //lint !e605 // deleting const + IGNORE_RETURN(m_sceneTransferChunkLoads->erase(m_sceneTransferChunkLoads->begin())); + } + delete m_sceneTransferChunkLoads; + m_sceneTransferChunkLoads = 0; + delete m_pendingCharacterSaves; + m_pendingCharacterSaves=0; + + { + GameServerMapType::iterator i=m_gameServers.begin(); + for (; i!=m_gameServers.end(); ++i) + { + delete i->second; + } + } + + delete m_characterFindMap; + m_characterFindMap = 0; + +} + +//----------------------------------------------------------------------- + +const Service * PlanetServer::getGameService() const +{ + return m_gameService; +} + +//----------------------------------------------------------------------- + +const unsigned short PlanetServer::getGameServicePort() const +{ + unsigned short result = 0; + if(m_gameService) + result = m_gameService->getBindPort(); + return result; +} + +// ---------------------------------------------------------------------- + +/** + * This function exists to provide a static function for the engine's + * callback. + * It converts between an engine-style singleton (all functions static) + * and the patterns-style singleton (one instance). + */ + +void PlanetServer::run(void) +{ + NetworkHandler::install(); + LOG("ServerStartup",("PlanetServer starting on %s", NetworkHandler::getHostName().c_str())); + + PlanetServer::getInstance().mainLoop(); + NetworkHandler::remove(); +} + +// ---------------------------------------------------------------------- + +void PlanetServer::mainLoop(void) +{ + Scene::getInstance().setSceneId(ConfigPlanetServer::getSceneID()); // also insures Scene is constructed + + Timer loadingLogTimer(10); + Timer preloadBailoutTimer(static_cast(ConfigPlanetServer::getPreloadBailoutTime())); + + bool hasLoggedPreloadCompletion = false; + + while (!m_done) + { + PROFILER_AUTO_BLOCK_DEFINE("main loop"); + + if (!Os::update()) + setDone("Os condition (Parent pid change)"); + + float updateTime = Clock::frameTime(); + + { + PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::update"); + NetworkHandler::update(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::dispatch"); + NetworkHandler::dispatch(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("MetricsManager::update"); + MetricsManager::update(updateTime * 1000); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("Os::sleep"); + Os::sleep(1); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("Scene::update"); + Scene::getInstance().update(updateTime); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("Send chunk requests"); + if (m_waitForSaveCounter==0) // Don't send load requests during crash recovery + { + if (!m_pendingChunkRequests.empty()) + { + RequestChunkMessage const msg(m_pendingChunkRequests, Scene::getInstance().getSceneId()); + PlanetServer::getInstance().sendToCentral(msg, true); + m_pendingChunkRequests.clear(); + } + + if (!m_pendingPreloadRequestCompletes.empty()) + { + for(std::vector >::const_iterator i=m_pendingPreloadRequestCompletes.begin(); i!=m_pendingPreloadRequestCompletes.end(); ++i) + { + PreloadRequestCompleteMessage const msg(i->first, i->second); + PlanetServer::getInstance().sendToCentral(msg, true); + } + m_pendingPreloadRequestCompletes.clear(); + } + } + } + + for (WatcherList::const_iterator i = m_watchers.begin(); i != m_watchers.end(); ++i) + { + PROFILER_AUTO_BLOCK_DEFINE("WatcherConnection::flushQueuedData"); + WatcherConnection * w = (*i); + w->flushQueuedData(); + } + + //--Log preloading status, bail out if preloading hangs + { + if (loadingLogTimer.updateSubtract(updateTime)) + { + if (hasLoggedPreloadCompletion && !PreloadManager::getInstance().isPreloadComplete()) + hasLoggedPreloadCompletion = false; + if (!hasLoggedPreloadCompletion) + { + std::string message; + PreloadManager::getInstance().getDebugString(message); + LOG("Preload",("%s %s",Scene::getInstance().getSceneId().c_str(),message.c_str())); + if (PreloadManager::getInstance().isPreloadComplete()) + hasLoggedPreloadCompletion = true; + } + } + if (ConfigPlanetServer::getPreloadBailoutTime()!=0) + { + if (PreloadManager::getInstance().isPreloadComplete()) + preloadBailoutTimer.reset(); + else + { + if (preloadBailoutTimer.updateZero(updateTime)) + FATAL(TRUE,("Preload did not finish on Planet Server %s within a reasonable amount of time",Scene::getInstance().getSceneId().c_str())); + } + } + } + + NetworkHandler::clearBytesThisFrame(); + } + + m_watcherIsPresent = false; // so that we don't try to send to the watcher while shutting down + m_watchers.clear(); // UDP library will delete the objects +} + +// ---------------------------------------------------------------------- + +void PlanetServer::receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message) +{ + if(message.isType("CentralConnectionOpened")) + { + DEBUG_REPORT_LOG(true,("Connected to Central.\n")); + + //handle preloading cities -- the preload list will determine how many servers we spawn + PreloadManager::getInstance().handlePreloadList(); + } + else if (message.isType("ShutdownMessage")) + { + REPORT_LOG(true, ("Instructed to shutdown the server\n")); + setDone("Received ShutdownMessage"); + } + else if(message.isType("CentralConnectionClosed")) + { + DEBUG_REPORT_LOG(true, ("Central server closed connection. Exiting.\n")); + ServerConnection const *serverConnection = dynamic_cast(&source); + setDone("CentralConnectionClosed : %s", serverConnection ? serverConnection->getDisconnectReason().c_str() : ""); + m_centralServerConnection = 0; //lint !e423 // object is deleted elsewhere + m_pendingCentralServerConnection = 0; //lint !e423 // object is deleted elsewhere + } + else if (message.isType("GameConnectionClosed")) + { + GameServerConnection *gameServer=const_cast(dynamic_cast(&source)); + std::set::iterator f = m_pendingGameServerDisconnects.find(gameServer); + + WARNING_DEBUG_FATAL(gameServer==0,("Source was NULL or source was not a GameServerConnection.")); + DEBUG_REPORT_LOG(true, ("removing crashed gameserver\n")); + uint32 id = gameServer->getProcessId(); + GameServerMapType::iterator i=m_gameServers.find(id); + if (i!=m_gameServers.end()) + { + delete i->second; + m_gameServers.erase(i); + } + + // must call this before removeGameServer() to get the preload number + // for the dead game server; we'll need that in order to start a replacement + PreloadServerId preloadServerId = PreloadManager::getInstance().getPreloadServerId(id); + + // if the game server hadn't gotten far enough to be assigned a preload + // number, use the preload number that was assigned to it when it was started + if (preloadServerId == 0) + preloadServerId = gameServer->getPreloadNumber(); + + PreloadManager::getInstance().removeGameServer(id); + if(f == m_pendingGameServerDisconnects.end()) + { + std::set startGameServerList; + IGNORE_RETURN(startGameServerList.insert(preloadServerId)); + + startGameServer(startGameServerList, ConfigPlanetServer::getGameServerRestartDelayTimeSeconds()); + } + else + { + m_pendingGameServerDisconnects.erase(f); + } + // advise watchers that the game server has closed its connection + ServerInfo info; + info.ipAddress = gameServer->getRemoteAddress(); + info.serverId = id; + info.systemPid = gameServer->getOsProcessId(); + GameServerStatus statusMsg(false, info); + std::vector::iterator w; + std::vector::iterator begin = m_watchers.begin(); + std::vector::iterator end = m_watchers.end(); + + for(w = begin; w != end; ++w) + { + (*w)->send(statusMsg, true); + } + + if (m_firstGameServer == id) + { + if (m_gameServers.size() == 0) + m_firstGameServer = 0; + else + { + GameServerMapType::const_iterator j=m_gameServers.begin(); + m_firstGameServer = j->first; + FirstPlanetGameServerIdMessage const msg(m_firstGameServer); + for (; j!=m_gameServers.end(); ++j) + j->second->getConnection()->send(msg, true); + } + } + PreloadFinishedMessage const preloadFinishedMessage(PreloadManager::getInstance().isPreloadComplete()); + sendToCentral(preloadFinishedMessage, true); + } + + else if(message.isType("GameGameServerConnect")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + GameGameServerConnect msg(ri); + GameServerConnection * conn = const_cast(safe_cast(&source)); + + std::map >::iterator f = m_startingGameServers->find(msg.getSpawnCookie()); + if(f != m_startingGameServers->end()) + m_startingGameServers->erase(f); + + uint32 id = msg.getProcessId(); + m_gameServers[id]=new GameServerData(conn); + conn->setProcessId(id); + conn->setPreloadNumber(msg.getPreloadNumber()); + LOG("PlanetServerConnections",("Game Server %lu (preload %d) connected on scene %s. m_startingGameServers is now %i", id, msg.getPreloadNumber(), ConfigPlanetServer::getSceneID(),m_startingGameServers->size())); + + DEBUG_REPORT_LOG(true,("Game Server %lu (preload %d) connected on scene %s.\n", id, msg.getPreloadNumber(), ConfigPlanetServer::getSceneID())); + + ServerInfo info; + info.ipAddress = conn->getRemoteAddress(); + info.serverId = id; + info.systemPid = conn->getOsProcessId(); + GameServerStatus statusMsg(true, info); + std::vector::iterator i; + std::vector::iterator begin = m_watchers.begin(); + std::vector::iterator end = m_watchers.end(); + + for(i = begin; i != end; ++i) + { + (*i)->send(statusMsg, true); + } + + if (m_firstGameServer == 0) + { + WARNING_STRICT_FATAL(m_gameServers.size() > 1, ("m_firstGameServer was not set but there were Game Server connections in the list.")); + m_firstGameServer = id; + } + + FirstPlanetGameServerIdMessage const fpgsim(m_firstGameServer); + conn->send(fpgsim, true); + } + else if(message.isType("GameServerReadyMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const GameServerReadyMessage msg(ri); + + GameServerConnection * conn = const_cast(dynamic_cast(&source)); + if (conn) + { + uint32 id = conn->getProcessId(); + GameServerMapType::iterator mapIter = m_gameServers.find(id); + if (mapIter==m_gameServers.end()) + { + WARNING_STRICT_FATAL(true,("GameServer %lu wasn't in the map, but we got GameServerReadyMessage from it.",id)); + return; + } + Scene::getInstance().setMapSize(msg.getMapWidth()); + mapIter->second->ready(); + } + } + else if(message.isType("GameServerUniverseLoadedMessage")) + { + GameServerConnection * conn = const_cast(dynamic_cast(&source)); + if (conn) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const GameServerUniverseLoadedMessage msg(ri); + + uint32 id = conn->getProcessId(); + LOG("PlanetServerConnections", ("Game Server %lu (preload %d) has loaded the universe objects.", conn->getProcessId(), conn->getPreloadNumber())); + + GameServerMapType::iterator mapIter = m_gameServers.find(id); + WARNING_STRICT_FATAL(mapIter==m_gameServers.end(),("GameServer %s wasn't in the map, but we got UniverseLoadedMessage from it.",id)); + if (mapIter==m_gameServers.end()) + return; + mapIter->second->universeLoaded(); + if(! PreloadManager::getInstance().onGameServerReady(id, static_cast(conn->getPreloadNumber()))) + { + m_pendingGameServerDisconnects.insert(conn); + conn->disconnect(); + } + } + flushWaitingMessages(); + } + else if (message.isType("PreloadRequestCompleteMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const PreloadRequestCompleteMessage msg(ri); + + PreloadManager::getInstance().preloadCompleteOnServer(msg.getGameServerId()); + GameServerData *data = getGameServerData(msg.getGameServerId()); + if (data) + data->preloadComplete(); + } + else if (message.isType("RequestGameServerForLoginMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const RequestGameServerForLoginMessage *msg = new RequestGameServerForLoginMessage(ri); + + handleRequestGameServerForLoginMessage(msg); //this function handles memory management for msg. msg is INVALID after this call. + msg = 0; + } + else if (message.isType("RequestSceneTransfer")) + { + //This message comes from Central in response to a GameServer sending central a request scene transfer message. + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + RequestSceneTransfer const *msg = new RequestSceneTransfer(ri); + + handleRequestSceneTransfer(msg); //this function handles memory management for msg. msg is INVALID after this call. + msg = 0; + } + else if (message.isType("ChunkCompleteMessage")) + { + GameServerConnection * conn = const_cast(dynamic_cast(&source)); + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ChunkCompleteMessage msg(ri); + handleChunkComplete(msg, conn); + } + else if (message.isType("WatcherConnectionOpened")) + { + WatcherConnection *newConn = const_cast(dynamic_cast(&source)); + if (newConn) + { + // advertise status of all game servers to new watcher + GameServerMapType::iterator i; + GameServerMapType::iterator begin = m_gameServers.begin(); + GameServerMapType::iterator end = m_gameServers.end(); + for(i = begin; i != end; ++i) + { + ServerInfo info; + info.ipAddress = (*i).second->getConnection()->getRemoteAddress(); + info.serverId = (*i).second->getConnection()->getProcessId(); + info.systemPid = (*i).second->getConnection()->getOsProcessId(); + info.sceneId = ConfigPlanetServer::getSceneID(); + GameServerStatus const status(true, info); + newConn->send(status, true); + } + + if (static_cast(m_watchers.size() + 1) > ConfigPlanetServer::getMaxWatcherConnections()) + { + DEBUG_REPORT_LOG(true,("Dropping previous watcher connection.\n")); + WARNING_DEBUG_FATAL(m_watchers.size()==0,("MaxWatcherConnections must be at least 1.")); + m_watchers[0]->disconnect(); + } + m_watchers.push_back(newConn); + m_watcherIsPresent=true; + Scene::getInstance().outputStatus(*newConn); + } + else + WARNING_STRICT_FATAL(true,("Got WatcherConnectionOpened from something that wasn't a WatcherConnection.")); + } + else if (message.isType("WatcherConnectionClosed")) + { + for (WatcherList::iterator i=m_watchers.begin(); i!= m_watchers.end(); ) + { + if ((*i)==&source) + i=m_watchers.erase(i); + else + ++i; + } + if (m_watchers.empty()) + { + m_watcherIsPresent=false; + DEBUG_REPORT_LOG(true,("No more watchers connected.\n")); + } + } + else if (message.isType("TaskConnectionOpened")) + { + std::set startGameServerList; + startGameServer(startGameServerList, 0); // flush any pending game server start requests + } + else if(message.isType("ProfilerOperationMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ProfilerOperationMessage msg(ri); + unsigned int processId = msg.getProcessId(); + if (!processId) + Profiler::handleOperation(msg.getOperation().c_str()); + } + else if(message.isType("FrameEndMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + FrameEndMessage const msg(ri); + for (WatcherList::const_iterator i = m_watchers.begin(); i != m_watchers.end(); ++i) + (*i)->send(msg, true); + } + else if (message.isType("UnloadedPlayerMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + UnloadedPlayerMessage msg(ri); + + GameServerConnection *gameServer=const_cast(dynamic_cast(&source)); + WARNING_DEBUG_FATAL(gameServer==0,("Source was NULL or source was not a GameServerConnection.")); + uint32 gameServerId = gameServer->getProcessId(); + + (*m_pendingCharacterSaves)[msg.getPlayerId()]=gameServerId; + } + else if (message.isType("PersistedPlayerMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + PersistedPlayerMessage msg(ri); + + m_pendingCharacterSaves->erase(msg.getPlayerId()); + } + else if (message.isType("ExcommunicateGameServerMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + ExcommunicateGameServerMessage msg(ri); + + LOG("GameGameConnect",("Planet Server %s was told to drop connection to %lu by Central",Scene::getInstance().getSceneId().c_str(),msg.getServerId())); + + GameServerConnection *conn =getGameServerConnection(msg.getServerId()); + if (conn) + conn->disconnect(); + } + else if (message.isType("FactionalSystemMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + FactionalSystemMessage msg(ri); + + // forward message to all game servers on this planet for processing + for (GameServerMapType::iterator i = m_gameServers.begin(); i != m_gameServers.end(); ++i) + (*i).second->getConnection()->send(msg, true); + } + else if (message.isType("MessageToPlayersOnPlanet")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage >, std::pair >, std::pair > > const msg(ri); + + // forward message to all game servers on this planet for processing + for (GameServerMapType::iterator i = m_gameServers.begin(); i != m_gameServers.end(); ++i) + (*i).second->getConnection()->send(msg, true); + } + else if (message.isType("RequestSameServer")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage > msg(ri); + + bool result=Scene::getInstance().requestSameServer(msg.getValue().first, msg.getValue().second); + + GameServerConnection * const gameServer = const_cast(dynamic_cast(&source)); + + if (gameServer) + { + GenericValueTypeMessage const rssReply("RequestSameServerReply", result); + gameServer->send(rssReply, true); + } + } + else if (message.isType("RequestAuthTransfer")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage > msg(ri); + + bool result=Scene::getInstance().requestAuthTransfer(msg.getValue().first, msg.getValue().second); + + GameServerConnection * const gameServer = const_cast(dynamic_cast(&source)); + + if (gameServer) + { + GenericValueTypeMessage const ratReply("RequestAuthTransferReply", result); + gameServer->send(ratReply, true); + } + } + else if (message.isType("GameServerForceChangeAuthorityMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + GameServerForceChangeAuthorityMessage msg(ri); + GameServerData * fromServer = PlanetServer::getGameServerData(msg.getFromProcess()); + GameServerData * toServer = PlanetServer::getGameServerData(msg.getToProcess()); + PlanetProxyObject * object = Scene::getInstance().findObjectByID(msg.getId()); + if (fromServer == NULL || toServer == NULL || object == NULL) + { + if (fromServer == NULL) + { + WARNING(true, ("Message GameServerForceChangeAuthorityMessage: no " + "from server %lu", msg.getFromProcess())); + } + if (toServer == NULL) + { + WARNING(true, ("Message GameServerForceChangeAuthorityMessage: no " + "to server %lu", msg.getToProcess())); + } + if (object == NULL) + { + WARNING(true, ("Message GameServerForceChangeAuthorityMessage: no " + "object for id %s", msg.getId().getValueString().c_str())); + } + } + else if (Scene::getInstance().getGameServerForObject(msg.getId()) != msg.getFromProcess()) + { + WARNING(true, ("Message GameServerForceChangeAuthorityMessage: object " + "%s was supposed to be on process %lu, but we think it is on %lu", + msg.getId().getValueString().c_str(), msg.getFromProcess(), + Scene::getInstance().getGameServerForObject(msg.getId()))); + } + else + { + object->changeAuthority(msg.getToProcess(), false, true); + } + } + else if (message.isType("FindAuthObjectResponse")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage, bool> > msg(ri); + GameServerConnection const *gameServer = dynamic_cast(&source); + NOT_NULL(gameServer); + handleFindAuthObjectResponse(msg.getValue().first.first, msg.getValue().first.second, msg.getValue().second, gameServer->getProcessId()); + } + else if (message.isType("TaskProcessDiedMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + TaskProcessDiedMessage died(ri); + // verify it was a game server + const std::string & proc = died.getProcessName(); + if(proc.find("SwgGameServer") != proc.npos) + { + // pull cookie and preload server id from the command line of the process that died + size_t start = proc.find("spawnCookie"); + size_t preloadServerIdIndex = proc.find("preloadNumber"); + if ((start != proc.npos) && (preloadServerIdIndex != proc.npos)) + { + start += std::string("spawnCookie=").length(); + size_t end = proc.find(' ', start); + std::string cookieName = proc.substr(start, end); + int i = atoi(cookieName.c_str()); + std::map >::iterator f = m_startingGameServers->find(i); + if(f != m_startingGameServers->end()) + { + m_startingGameServers->erase(f); + + preloadServerIdIndex += std::string("preloadNumber=").length(); + end = proc.find(' ', preloadServerIdIndex); + std::string preloadServerIdStr = proc.substr(preloadServerIdIndex, end); + PreloadServerId preloadServerId = static_cast(atoi(preloadServerIdStr.c_str())); + + std::set startGameServerList; + IGNORE_RETURN(startGameServerList.insert(preloadServerId)); + + startGameServer(startGameServerList, ConfigPlanetServer::getGameServerRestartDelayTimeSeconds()); + + LOG("TaskProcessDied", ("TaskManager advises the PlanetServer that a GameServer %u for scene %s started with cookie %i is no longer running", preloadServerId, ConfigPlanetServer::getSceneID(), i)); + } + } + } + } + else if (message.isType ("LocationRequest")) + { + Archive::ReadIterator readIterator = static_cast (message).getByteStream ().begin (); + + //-- received a location request message + LocationRequest const locationRequest (readIterator); + + //-- ask the location manager if the location is valid + float x = locationRequest.getSearchX (); + float z = locationRequest.getSearchZ (); + bool const valid = LocationManager::requestLocation (x, z, locationRequest.getSearchRadius (), locationRequest.getLocationReservationRadius (), locationRequest.getCheckWater (), locationRequest.getCheckSlope (), x, z); + + //-- sent result back to gameserver that requested it + LocationResponse const locationResponse (locationRequest.getNetworkId (), valid, locationRequest.getLocationId (), x, z, locationRequest.getLocationReservationRadius ()); + PlanetServer::getInstance ().sendToGameServer (locationRequest.getProcessId (), locationResponse); + } + else if (message.isType ("DatabaseSaveComplete")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage msg(ri); + handleDatabaseSaveComplete(msg.getValue()); + } + else if (message.isType("LocateStructureMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + LocateStructureMessage msg(ri); + LoadStructureMessage lsm(msg.getStructureId(), msg.getWhoRequested()); + + uint32 gameServerId = Scene::getInstance().findNodeByPosition(msg.getX(), msg.getZ())->getPreferredServer(); + PlanetServer::getInstance().sendToGameServer(gameServerId, lsm); + } + else if (message.isType("DenySetAuthoritativeMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage > const msg(ri); + + // restore the authority of the object + PlanetProxyObject *object = Scene::getInstance().findObjectByID(msg.getValue().first); + if (object) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading", + ("Set authoritative denied by game server: restoring authority for object %s to server %lu", + msg.getValue().first.getValueString().c_str(),msg.getValue().second)); + + object->restoreAuthority(msg.getValue().second); + } + } + else if (message.isType("RestartServerMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + RestartServerMessage const msg(ri); + + Node const * const node = Scene::getInstance().findNodeByPositionConst(msg.getX(), msg.getZ()); + uint32 const serverId = NON_NULL(node)->getPreferredServer(); + + if (serverId != 0) + { + ExcommunicateGameServerMessage exmsg(serverId, 0, ""); + sendToCentral(exmsg, true); + } + } + else if (message.isType("SAMGS")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage const startAnyMissingGameServer(ri); + UNREF(startAnyMissingGameServer); + + LOG("PlanetServer", ("startanymissinggameserver:Planet %s checking for any game servers that are not started, and starting them.", Scene::getInstance().getSceneId().c_str())); + if (m_centralServerConnection) + { + // if it's been "awhile" since we requested to restart the GameServer, + // then assume that something has gone wrong, and try the restart again + time_t const timeNow = ::time(NULL); + + for (std::map >::iterator iter = m_startingGameServers->begin(); iter != m_startingGameServers->end(); ++iter) + { + if ((iter->second.second + static_cast(ConfigPlanetServer::getMaxTimeToWaitForGameServerStartSeconds())) < timeNow) + { + LOG("CentralServer", ("startanymissinggameserver:Starting missing game server (%s) because it has been %s since we requested it to be started, which has exceeded the %s limit.", iter->second.first.c_str(), CalendarTime::convertSecondsToMS(static_cast(timeNow - iter->second.second)).c_str(), CalendarTime::convertSecondsToMS(static_cast(ConfigPlanetServer::getMaxTimeToWaitForGameServerStartSeconds())).c_str())); + iter->second.second = timeNow; + TaskSpawnProcess spawn("any", "SwgGameServer", iter->second.first); + m_centralServerConnection->send(spawn,true); + } + else + { + LOG("PlanetServer", ("startanymissinggameserver:It's only been %s since we requested for game server (%s) to be started, and we must wait %s before we can start it again.", CalendarTime::convertSecondsToMS(static_cast(timeNow - iter->second.second)).c_str(), iter->second.first.c_str(), CalendarTime::convertSecondsToMS(static_cast(ConfigPlanetServer::getMaxTimeToWaitForGameServerStartSeconds())).c_str())); + } + } + } + else + { + LOG("PlanetServer", ("startanymissinggameserver:Planet %s ignoring request because there is no central server connection.", Scene::getInstance().getSceneId().c_str())); + } + } + else if (message.isType("RestartServerByRoleMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage > msg(ri); + + uint32 serverId = PreloadManager::getInstance().getRealServerId(msg.getValue().second); + if (serverId != 0) + { + ExcommunicateGameServerMessage exmsg(serverId, 0, ""); + sendToCentral(exmsg, true); + } + } +} + +// ---------------------------------------------------------------------- +/** + * Send a message to the Central server. + */ +void PlanetServer::sendToCentral(const GameNetworkMessage & message, const bool reliable) +{ + if (m_centralServerConnection) + { + m_centralServerConnection->send(message,reliable); + } + else + { + DEBUG_REPORT_LOG(true,("Central is not connected.\n")); + } +} + +//----------------------------------------------------------------------- + +void PlanetServer::sendToGameServer(const uint32 id, const GameNetworkMessage & msg) +{ + GameServerConnection *conn=getGameServerConnection(id); + if (conn) + conn->send(msg, true); + else + DEBUG_REPORT_LOG(true,("Not sending message to server %lu because we are not connected.\n",id)); +} + +// ---------------------------------------------------------------------- + +/** + * Send a message to a game server to force it to unload an object. + * @param object The id of the object + * @param authServer The id of the server that is authoritative for the object + */ +void PlanetServer::unloadObject(const NetworkId &object, uint32 authServer) +{ + UnloadObjectMessage msg(object); + GameServerConnection *conn=getGameServerConnection(authServer); + if (conn) + conn->send(msg,true); + else + { + // We should either have a connection or have handled the crash and removed all of that server's objects + WARNING(true,("Couldn't send UnloadObjectMessage for object %s, because we did not have a connection to game server %i.",object.getValueString().c_str(),authServer)); + } +} + +// ---------------------------------------------------------------------- + +GameServerConnection *PlanetServer::getGameServerConnection(uint32 serverId) +{ + GameServerMapType::iterator i=m_gameServers.find(serverId); + if (i!=m_gameServers.end()) + return (*i).second->getConnection(); + else + return NULL; +} + +// ---------------------------------------------------------------------- + +ServerConnection *PlanetServer::getCentralServerConnection() +{ + return m_centralServerConnection; +} + +// ---------------------------------------------------------------------- + +/** + * Get a game server for a character that wants to log in, and load the + * character on that game server. + * 1) If the building the player is in is already loaded, pick the + * authoritative server for that building. + * 2) Otherwise, pick a good game server for that location + * 3) If the chunk is not loaded, load it + * 4) Put a lock on the chunk to prevent it from being unloaded + * while waiting for the player to load + * 5) Tell the database to load the character onto the selected gameserver. + * 6) Tell the connection server which game server to use + */ + +void PlanetServer::handleRequestGameServerForLoginMessage(const RequestGameServerForLoginMessage*& msg) +{ + NOT_NULL(msg); + + if (!PreloadManager::getInstance().isPreloadComplete()) + { + DEBUG_REPORT_LOG(true, ("Waiting for game server for login\n")); + m_messagesWaitingForGameServer.push_back(msg); + return; + } + + std::map::iterator pendingSave=m_pendingCharacterSaves->find(msg->getCharacterId()); + if (pendingSave!=m_pendingCharacterSaves->end()) + { + uint32 gameServerId = pendingSave->second; + if (getGameServerConnection(pendingSave->second)) //make sure it's still up + { + LOG(loginTrace,("Reconnecting character %s who logged out but has not yet been saved. Game server is %lu",msg->getCharacterId().getValueString().c_str(),gameServerId)); + + GameServerForLoginMessage const reply(msg->getStationId(), gameServerId, msg->getCharacterId()); + sendToCentral(reply, true); + delete msg; //lint !e605 Deleting const pointer + msg = 0; + m_pendingCharacterSaves->erase(pendingSave); + return; + } + } + + findOrLoadCharacter(msg->getCharacterId(), msg->getContainerId(), msg->getCoordinates(), msg->getStationId(), msg->getForCtsSourceCharacter()); +} + +// ---------------------------------------------------------------------- + +void PlanetServer::findOrLoadCharacter(NetworkId const &characterId, NetworkId const &containerId, Vector const &coords, uint32 stationId, bool forCtsSourceCharacter) +{ + // put in map along with expected response count + CharacterFindInfo &cfi = (*m_characterFindMap)[characterId]; + cfi.sequence = ++m_characterFindSequence; + cfi.containerId = containerId; + cfi.coords = coords; + cfi.stationId = stationId; + cfi.pendingResponseCount = static_cast(m_gameServers.size()); + cfi.forCtsSourceCharacter = forCtsSourceCharacter; + + // message all game servers for this planet asking if they have this auth character + GenericValueTypeMessage > const msg("FindAuthObject", std::make_pair(characterId, cfi.sequence)); + for (GameServerMapType::iterator i = m_gameServers.begin(); i != m_gameServers.end(); ++i) + (*i).second->getConnection()->send(msg, true); +} + +// ---------------------------------------------------------------------- + +void PlanetServer::handleFindAuthObjectResponse(NetworkId const &characterId, unsigned int sequence, bool found, uint32 gameServerId) +{ + std::map::iterator i = m_characterFindMap->find(characterId); + if (i != m_characterFindMap->end() && (*i).second.sequence == sequence) + { + if (found) + { + LOG(loginTrace, ("Send GameServerForLogin(%s) to CentralServer for object found on game server", characterId.getValueString().c_str())); + GameServerForLoginMessage const msg((*i).second.stationId, gameServerId, characterId); + sendToCentral(msg, true); + m_characterFindMap->erase(i); + } + else if (--((*i).second.pendingResponseCount) <= 0) + { + forceLoadCharacter(characterId, (*i).second.containerId, (*i).second.coords, (*i).second.stationId, (*i).second.forCtsSourceCharacter); + m_characterFindMap->erase(i); + } + } +} + +// ---------------------------------------------------------------------- + +void PlanetServer::forceLoadCharacter(NetworkId const &characterId, NetworkId const &containerId, Vector const &coords, uint32 stationId, bool forCtsSourceCharacter) +{ + Node *node = Scene::getInstance().findNodeByPosition(static_cast(coords.x), static_cast(coords.z)); + NOT_NULL(node); + + uint32 gameServerId=Scene::getInstance().getGameServerForObject(containerId); + if (gameServerId==0) + { + if (!node->isLoaded()) + node->load(); + gameServerId=node->getPreferredServer(); + WARNING_DEBUG_FATAL(gameServerId==0,("Programmer bug: getPreferredGameServer returned 0 even after we forced the node to be loaded.")); + } + + if (gameServerId == 0) //lint !e774 // boolean is always false (only in debug mode) + { + DEBUG_REPORT_LOG(true, ("Waiting for game server for login\n")); + m_messagesWaitingForGameServer.push_back( + new RequestGameServerForLoginMessage( + stationId, + characterId, + containerId, + ConfigPlanetServer::getSceneID(), + coords, + forCtsSourceCharacter)); + } + else + { + if (!node->isLoaded()) + node->load(); + + loadCharacterForLogin(characterId, coords, gameServerId); + + LOG(loginTrace, ("Send GameServerForLogin(%s) to CentralServer for object being loaded", characterId.getValueString().c_str())); + GameServerForLoginMessage const msg(stationId, gameServerId, characterId); + sendToCentral(msg, true); + } +} + +// ------------------------------------------------------------------------------- + +void PlanetServer::loadCharacterForLogin(NetworkId const &characterId, Vector const &coords, unsigned long gameServerId) +{ + LOG(loginTrace, ("Character not loaded, sending PlanetLoadCharacter(%s, gs %d) to CentralServer", characterId.getValueString().c_str(), gameServerId)); + + // unload if we think it is loaded + PlanetProxyObject *object = Scene::getInstance().findObjectByID(characterId); + if (object) + object->unload(true); + + Node *node = Scene::getInstance().findNodeByPosition(static_cast(coords.x), static_cast(coords.z)); + NOT_NULL(node); + + PlanetLoadCharacterMessage const msg(characterId, gameServerId); + sendToCentral(msg, true); +} + +// ------------------------------------------------------------------------------- + +void PlanetServer::handleRequestSceneTransfer(const RequestSceneTransfer *& msg) +{ + NOT_NULL(msg); + + if (!PreloadManager::getInstance().isPreloadComplete()) + { + DEBUG_REPORT_LOG(true, ("Waiting for game server for login\n")); + m_messagesWaitingForGameServer.push_back(msg); + return; + } + + const Vector &coords = msg->getPosition_w(); + Node *node = Scene::getInstance().findNodeByPosition(static_cast(coords.x), static_cast(coords.z)); + NOT_NULL(node); + + uint32 gameServerId=Scene::getInstance().getGameServerForObject(msg->getContainerId()); + + if (!node->isLoaded()) + { + node->load(); + m_sceneTransferChunkLoads->push_back(msg); + } + else + { + if (gameServerId == 0) + gameServerId=node->getPreferredServer(); + WARNING_DEBUG_FATAL(gameServerId==0,("Programmer bug: getPreferredGameServer returned 0 even after we forced the node to be loaded.")); + + SceneTransferMessage const reply(*msg, gameServerId); + sendToCentral(reply, true); + delete msg; //lint !e605 Deleting const pointer + msg = 0; + } +} + +// ------------------------------------------------------------------------------- + +void PlanetServer::handleChunkComplete(const ChunkCompleteMessage &msg, const GameServerConnection *conn) +{ + NOT_NULL(conn); + + std::vector > const & chunks = msg.getChunks(); + for (std::vector >::const_iterator chunk=chunks.begin(); chunk!=chunks.end(); ++chunk) + { + int nodeX = chunk->first; + int nodeZ = chunk->second; + + std::list::iterator i = m_sceneTransferChunkLoads->begin(); + while (i != m_sceneTransferChunkLoads->end()) + { + const RequestSceneTransfer *transferMsg = *i; + const Vector &coords = transferMsg->getPosition_w(); + if ( Node::roundToNode(static_cast(coords.x)) == nodeX + && Node::roundToNode(static_cast(coords.z)) == nodeZ) + { + SceneTransferMessage const reply(*transferMsg, conn->getProcessId()); + sendToCentral(reply, true); + delete transferMsg; //lint !e605 Deleting const pointer + IGNORE_RETURN(m_sceneTransferChunkLoads->erase(i++)); + } + else + ++i; + } + } +} + +// ------------------------------------------------------------------------------- + +/** + * Start game servers. If there are previous requests to start game servers that + * haven't been handled yet, try to start those, too. If the game servers can't + * be started right now, queue them for later. + */ +void PlanetServer::startGameServer(const std::set & preloadServerId, const GameServerSpawnDelaySeconds spawnDelay) +{ + if(m_taskManagerConnection && m_centralServerConnection) + { + for (std::set::const_iterator it = preloadServerId.begin(); it != preloadServerId.end(); ++it) + (*m_pendingServerStarts)[*it] = spawnDelay; + + for (std::map::const_iterator i=m_pendingServerStarts->begin(); i!=m_pendingServerStarts->end(); ++i) + { + static int cookie = 0; + static char numberBuf[32]; + // build the spawn command for the new game server + std::string options = "-s GameServer centralServerAddress="; + options += m_centralServerConnection->getRemoteAddress(); + + static int portBase = ConfigPlanetServer::getGameServerDebuggingPortBase(); + portBase++; + // activate remote debugging in the spawn request if the planetserver + // is configured to start game servers with java debugging enabled + if(ConfigPlanetServer::getGameServerDebugging()) + { + snprintf(numberBuf, sizeof(numberBuf), "%d", 58000 + portBase); + options += " javaDebugPort="; + options += numberBuf; + options += " useRemoteDebugJava=true"; + } + // only profiling or debugging is allowed, not both + else if(ConfigPlanetServer::getGameServerProfiling()) + { + snprintf(numberBuf, sizeof(numberBuf), "%d", portBase); + options += " javaOptions=-Xrunhprof:cpu=times,thread=y,cutoff=0,depth=10,format=a,file=scriptprofile_"; + options += numberBuf; + options += ".txt"; + options += " javaOptions=-Djava.compiler=NONE"; + } + + options += " preloadNumber="; + snprintf(numberBuf, sizeof(numberBuf), "%lu", i->first); + options += numberBuf; + + options += " sceneID="; + options += Scene::getInstance().getSceneId(); + + options += " groundScene=terrain/"; + options += Scene::getInstance().getSceneId(); + options += ".trn"; + + options += " -s ServerUtility spawnCookie="; + snprintf(numberBuf, sizeof(numberBuf), "%i", cookie); + options += numberBuf; + + TaskSpawnProcess spawn("any", "SwgGameServer", options, i->second); + if (m_centralServerConnection) + { + (*m_startingGameServers)[cookie] = std::make_pair(options, ::time(NULL)); + ++cookie; + m_centralServerConnection->send(spawn,true); + DEBUG_REPORT_LOG(true, ("Sent start gameserver request for scene %s\n", ConfigPlanetServer::getSceneID())); + LOG("PlanetServerConnections", ("Sent start gameserver request for scene %s", ConfigPlanetServer::getSceneID())); + } + else + { + FATAL(true, ("Cannot start up without central server connection")); + } + } + + m_pendingServerStarts->clear(); + } + else + { + DEBUG_REPORT_LOG(true, ("Adding pending server on scene %s\n", ConfigPlanetServer::getSceneID())); + + for (std::set::const_iterator it = preloadServerId.begin(); it != preloadServerId.end(); ++it) + (*m_pendingServerStarts)[*it] = spawnDelay; + } +} + +// ---------------------------------------------------------------------- + +GameServerData *PlanetServer::getGameServerData(uint32 serverId) const +{ + GameServerMapType::const_iterator i = m_gameServers.find(serverId); + if (i!=m_gameServers.end()) + return i->second; + else + return NULL; +} + +// ---------------------------------------------------------------------- + +void PlanetServer::preloadCompleteOnAllServers() +{ + flushWaitingMessages(); + Scene::getInstance().checkServerAssignments(); + PreloadFinishedMessage const msg(true); + sendToCentral(msg, true); +} + +// ---------------------------------------------------------------------- + +/** + * Send all messages that were queued waiting for game servers to be ready. + */ +void PlanetServer::flushWaitingMessages() +{ + if (!PreloadManager::getInstance().isPreloadComplete()) + return; // don't handle any logins until preloading is finished + + DEBUG_REPORT_LOG(true,("Handling pending logins and transfers.\n")); + std::vector::iterator t; + for(t = m_messagesWaitingForGameServer.begin(); t != m_messagesWaitingForGameServer.end();) + { + const GameNetworkMessage * msg = *t; + t = m_messagesWaitingForGameServer.erase(t); + const RequestSceneTransfer * sceneTransfer = dynamic_cast(msg); + if (sceneTransfer) + handleRequestSceneTransfer(sceneTransfer); + else + { + const RequestGameServerForLoginMessage * loginMessage = dynamic_cast(msg); + if (loginMessage) + handleRequestGameServerForLoginMessage(loginMessage); + else + { + WARNING_STRICT_FATAL(true, ("Invalid message type in pending message queue for Game Server")); + delete msg; //lint !e605 Deleting const pointer + } + } + } +} + +// ---------------------------------------------------------------------- + +int PlanetServer::getObjectCountForServer(uint32 serverId) const +{ + GameServerMapType::const_iterator i=m_gameServers.find(serverId); + if (i!=m_gameServers.end()) + return i->second->getObjectCount(); + else + return 0; +} + +// ---------------------------------------------------------------------- + +int PlanetServer::getInterestObjectCountForServer(uint32 serverId) const +{ + GameServerMapType::const_iterator i=m_gameServers.find(serverId); + if (i!=m_gameServers.end()) + return i->second->getInterestObjectCount(); + else + return 0; +} + +// ---------------------------------------------------------------------- + +int PlanetServer::getInterestCreatureObjectCountForServer(uint32 serverId) const +{ + GameServerMapType::const_iterator i=m_gameServers.find(serverId); + if (i!=m_gameServers.end()) + return i->second->getInterestCreatureObjectCount(); + else + return 0; +} + +// ---------------------------------------------------------------------- + +void PlanetServer::getObjectCountsForAllServers(std::map &counts) const +{ + for (GameServerMapType::const_iterator i=m_gameServers.begin(); i!=m_gameServers.end(); ++i) + counts[i->first]=*(i->second); +} + +// ---------------------------------------------------------------------- + +void PlanetServer::queueMessageForObject(const NetworkId &networkId, GameNetworkMessage *theMessage) +{ + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Queueing message for object %s because authority change is in progress.",networkId.getValueString().c_str())); + std::vector & v = m_queuedMessages[networkId]; + v.push_back(theMessage); +} + +// ---------------------------------------------------------------------- + +void PlanetServer::sendQueuedMessagesForObject(const PlanetProxyObject &theObject) +{ + uint32 server = theObject.getAuthoritativeServer(); + QueuedMessagesType::iterator i=m_queuedMessages.find(theObject.getObjectId()); + if (i!=m_queuedMessages.end()) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Sending queued message for object %s.",theObject.getObjectId().getValueString().c_str())); + std::vector & v = i->second; + for (std::vector::iterator j=v.begin(); j!=v.end(); ++j) + { + sendToGameServer(server,**j); + delete *j; + } + + m_queuedMessages.erase(i); + } +} + +// ---------------------------------------------------------------------- + +void PlanetServer::setDone(char const *reasonfmt, ...) +{ + if (!m_done) + { + char reason[1024]; + va_list ap; + va_start(ap, reasonfmt); + _vsnprintf(reason, sizeof(reason), reasonfmt, ap); + reason[sizeof(reason)-1] = '\0'; + + LOG( + "ServerShutdown", + ( + "PlanetServer (pid %d) shutdown, reason: %s", + static_cast(Os::getProcessId()), + reason)); + + REPORT_LOG( + true, + ( + "PlanetServer (pid %d) shutdown, reason: %s\n", + static_cast(Os::getProcessId()), + reason)); + + m_done = true; + } +} + +// ---------------------------------------------------------------------- + +void PlanetServer::handleDatabaseSaveComplete(int newSaveCounter) +{ + WARNING_DEBUG_FATAL(m_lastSaveCounter>newSaveCounter,("The database sent a save complete message with a counter that was lower than the previous value.")); + m_lastSaveCounter = newSaveCounter; + if ((m_waitForSaveCounter!=0) && (m_lastSaveCounter >= m_waitForSaveCounter)) + m_waitForSaveCounter = 0; +} + +// ---------------------------------------------------------------------- + +void PlanetServer::sendChunkRequest(uint32 server, int x, int z) +{ + m_pendingChunkRequests.push_back(RequestChunkMessage::Chunk(server,x,z)); +} + +// ---------------------------------------------------------------------- + +void PlanetServer::sendPreloadRequestCompleteMessage(uint32 realServerId, uint32 preloadServerId) +{ + m_pendingPreloadRequestCompletes.push_back(std::make_pair(realServerId, preloadServerId)); +} + +// ---------------------------------------------------------------------- + +/** + * Request a database save and delay all new loads until it finishes. + */ +void PlanetServer::requestAndWaitForSave() +{ + m_waitForSaveCounter = m_lastSaveCounter+1; + GameNetworkMessage const msg("PlanetRequestSave"); + sendToCentral(msg, true); +} + +// ---------------------------------------------------------------------- + +bool PlanetServer::getEnablePreload() const +{ + return (ConfigPlanetServer::getLoadWholePlanetMultiserver() || ConfigPlanetServer::getLoadWholePlanet() || isInSpaceMode()); +} + +// ====================================================================== diff --git a/engine/server/application/PlanetServer/src/shared/PlanetServer.h b/engine/server/application/PlanetServer/src/shared/PlanetServer.h new file mode 100644 index 00000000..cf457544 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/PlanetServer.h @@ -0,0 +1,190 @@ +// ====================================================================== +// +// PlanetServer.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_PlanetServer_H +#define INCLUDED_PlanetServer_H + +// ====================================================================== + +#include +#include +#include + +#include "sharedMessageDispatch/Receiver.h" +#include "Singleton/Singleton.h" + +#include "PreloadManager.h" +#include "TaskConnection.h" + +class CentralServerConnection; +class ChunkCompleteMessage; +class GameNetworkMessage; +class GameServerConnection; +class GameServerData; +class PlanetProxyObject; +class PlanetServerMetricsData; +class PreloadListData; +class RequestGameServerForLoginMessage; +class RequestSceneTransfer; +class Service; +class Vector; +class WatcherConnection; +struct CharacterFindInfo; + +//----------------------------------------------------------------------- + +class PlanetServer : public Singleton, public MessageDispatch::Receiver +{ +public: + typedef std::vector WatcherList; + typedef unsigned int GameServerSpawnDelaySeconds; + +public: + PlanetServer(); + ~PlanetServer(); + + static void run(void); + void mainLoop(void); + void setDone(char const *reasonfmt, ...); + + const unsigned short getGameServicePort() const; + const Service * getGameService() const; + int getNumberOfGameServers() const; + int getObjectCountForServer(uint32 serverId) const; + int getInterestObjectCountForServer(uint32 serverId) const; + int getInterestCreatureObjectCountForServer(uint32 serverId) const; + void getObjectCountsForAllServers(stdmap::fwd &counts) const; + virtual void receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message); + void sendToCentral(const GameNetworkMessage & message, const bool reliable); + void sendToGameServer(const uint32 processId, const GameNetworkMessage & message); + void setTaskManager(TaskConnection*); + void startGameServer(const std::set & preloadServerId, GameServerSpawnDelaySeconds spawnDelay); + GameServerData * getGameServerData(uint32 serverId) const; + void preloadCompleteOnAllServers(); + int getCurrentServerInterestObjectLimit() const; + void queueMessageForObject(const NetworkId &networkId, GameNetworkMessage *theMessage); + void sendQueuedMessagesForObject(const PlanetProxyObject &theObject); + void unloadObject (const NetworkId &object, uint32 authServer); + void sendChunkRequest(uint32 server, int x, int z); + void sendPreloadRequestCompleteMessage(uint32 realServerId, uint32 preloadServerId); + void requestAndWaitForSave(); + void onCentralConnected(CentralServerConnection *connection); + + const WatcherList & getWatchers() const; + bool isWatcherPresent() const; + bool isInTutorialMode() const; + bool getEnablePreload() const; + bool isInSpaceMode() const; + + GameServerConnection *getGameServerConnection(uint32 serverId); + ServerConnection *getCentralServerConnection(); + +private: + typedef std::map GameServerMapType; + +private: + void handleRequestGameServerForLoginMessage (const RequestGameServerForLoginMessage *&msg); + void loadCharacterForLogin(NetworkId const &characterId, Vector const &coords, unsigned long gameServerId); + void handleRequestSceneTransfer(const RequestSceneTransfer *& msg); + void handleChunkComplete(const ChunkCompleteMessage &msg, const GameServerConnection *conn); + void flushWaitingMessages(); + void findOrLoadCharacter(NetworkId const &characterId, NetworkId const &containerId, Vector const &coords, uint32 stationId, bool forCtsSourceCharacter); + void handleFindAuthObjectResponse(NetworkId const &characterId, unsigned int sequence, bool found, uint32 gameServerId); + void forceLoadCharacter(NetworkId const &characterId, NetworkId const &containerId, Vector const &coords, uint32 stationId, bool forCtsSourceCharacter); + void handleDatabaseSaveComplete(int newSaveCounter); + +private: + CentralServerConnection * m_pendingCentralServerConnection; + CentralServerConnection * m_centralServerConnection; + Service * m_gameService; + Service * m_watcherService; + GameServerMapType m_gameServers; + TaskConnection * m_taskConnection; + bool m_done; + int m_roundRobinGameServer; + stdmap::fwd * m_pendingServerStarts; // number of game servers to start when central & task manager are ready + stdmap >::fwd * m_startingGameServers; // number of game servers we've started that haven't connected to us yet + uint32 m_firstGameServer; + bool m_tutorialMode; + bool m_spaceMode; + std::set m_pendingGameServerDisconnects; + + std::vector m_messagesWaitingForGameServer; + PlanetServerMetricsData* m_metricsData; + TaskConnection* m_taskManagerConnection; + stdlist::fwd *m_sceneTransferChunkLoads; + stdmap::fwd *m_pendingCharacterSaves; + + WatcherList m_watchers; + bool m_watcherIsPresent; + + typedef std::map > QueuedMessagesType; + QueuedMessagesType m_queuedMessages; + stdmap::fwd *m_characterFindMap; + unsigned int m_characterFindSequence; + int m_waitForSaveCounter; + int m_lastSaveCounter; + +private: + //disable: + PlanetServer(const PlanetServer&); + PlanetServer &operator=(const PlanetServer&); +}; + +// ---------------------------------------------------------------------- + +inline int PlanetServer::getNumberOfGameServers() const +{ + return static_cast(m_gameServers.size()); +} + +// ---------------------------------------------------------------------- + +inline void PlanetServer::onCentralConnected(CentralServerConnection *connection) +{ + m_pendingCentralServerConnection=NULL; + m_centralServerConnection=connection; +} + +// ---------------------------------------------------------------------- + +inline void PlanetServer::setTaskManager(TaskConnection* con) +{ + m_taskManagerConnection = con; +} + +// ---------------------------------------------------------------------- + +inline bool PlanetServer::isWatcherPresent() const +{ + return m_watcherIsPresent; +} + +// ---------------------------------------------------------------------- + +inline const PlanetServer::WatcherList & PlanetServer::getWatchers() const +{ + return m_watchers; +} + +// ---------------------------------------------------------------------- + +inline bool PlanetServer::isInTutorialMode() const +{ + return m_tutorialMode; +} + +// ---------------------------------------------------------------------- + +inline bool PlanetServer::isInSpaceMode() const +{ + return m_spaceMode; +} + +// ====================================================================== + +#endif //_CentralServer_H diff --git a/engine/server/application/PlanetServer/src/shared/PlanetServerMetricsData.cpp b/engine/server/application/PlanetServer/src/shared/PlanetServerMetricsData.cpp new file mode 100644 index 00000000..1dec17ed --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/PlanetServerMetricsData.cpp @@ -0,0 +1,102 @@ +//PlanetServerMetricsData.cpp +//Copyright 2002 Sony Online Entertainment + +#include "FirstPlanetServer.h" +#include "PlanetServerMetricsData.h" + +#include "PlanetServer.h" +#include "ConfigPlanetServer.h" +#ifndef WIN32 +#include "MonAPI2/MonitorData.h" +#endif +#include "serverNetworkMessages/MetricsDataMessage.h" +#include "sharedFoundation/Os.h" +#include "sharedNetworkMessages/GameNetworkMessage.h" + + +//----------------------------------------------------------------------- + +PlanetServerMetricsData::PlanetServerMetricsData() : +MetricsData(), +m_numGameServers(0), +m_watcherPort(0), +m_gameServerLoadTime() +{ + MetricsPair p; + + ADD_METRICS_DATA(numGameServers, 0, false); //lint !e713 loss of precision, couldn't figure out a cast that made Lint happy + ADD_METRICS_DATA(watcherPort, ConfigPlanetServer::getWatcherServicePort(), false); + + std::string label = NetworkHandler::getHostName(); + + for (std::string::iterator i = label.begin(); i != label.end(); ++i) + { + if (*i == '.') + { + *i = '+'; + } + } + + label += ":"; + char tmp[256]; + label += _itoa(Os::getProcessId(), tmp, 10); + + m_data[m_watcherPort].m_description = label; +} + +//----------------------------------------------------------------------- + +PlanetServerMetricsData::~PlanetServerMetricsData() +{ +} + +//----------------------------------------------------------------------- + +void PlanetServerMetricsData::updateData() +{ + MetricsData::updateData(); + m_data[m_numGameServers].m_value = PlanetServer::getInstance().getNumberOfGameServers(); //lint !e732 !e713 + +#ifndef WIN32 + // if all game servers for planet has not finished loading, + // set value to "loading" to cause the planet node to appear + // yellow to help figure out which planet is still loading + if (!PreloadManager::getInstance().isPreloadComplete()) + m_data[m_watcherPort].m_value = STATUS_LOADING; + else +#endif + m_data[m_watcherPort].m_value = ConfigPlanetServer::getWatcherServicePort(); + + // report load time of each game server + PreloadManager::ServerMapType const * serverMap = PreloadManager::getInstance().getServerMap(); + if (serverMap && !serverMap->empty()) + { + std::map::const_iterator iter2; + MetricsPair p; + char buffer[128]; + for (PreloadManager::ServerMapType::const_iterator iter = serverMap->begin(); iter != serverMap->end(); ++iter) + { + iter2 = m_gameServerLoadTime.find(iter->first); + if (iter2 == m_gameServerLoadTime.end()) + { + // create a node to report load time for this game server + snprintf(buffer, sizeof(buffer)-1, "loadTime_%s_%lu", ConfigPlanetServer::getSceneID(), iter->first); + buffer[sizeof(buffer)-1] = '\0'; + + p.m_label = buffer; + p.m_value = std::max(static_cast(0), static_cast((iter->second.m_loadTime != -1) ? iter->second.m_loadTime : (time(0) - iter->second.m_timeLoadStarted))); + p.m_description = "seconds"; + p.m_persistData = true; + p.m_summary = false; + m_gameServerLoadTime[iter->first] = m_data.size(); + m_data.push_back(p); + } + else + { + m_data[iter2->second].m_value = std::max(static_cast(0), static_cast((iter->second.m_loadTime != -1) ? iter->second.m_loadTime : (time(0) - iter->second.m_timeLoadStarted))); + } + } + } +} + +//----------------------------------------------------------------------- diff --git a/engine/server/application/PlanetServer/src/shared/PlanetServerMetricsData.h b/engine/server/application/PlanetServer/src/shared/PlanetServerMetricsData.h new file mode 100644 index 00000000..ab6b7b19 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/PlanetServerMetricsData.h @@ -0,0 +1,38 @@ +//PlanetServerMetricsData.h +//Copyright 2002 Sony Online Entertainment + + +#ifndef _PlanetServerMetricsData_H +#define _PlanetServerMetricsData_H + +//----------------------------------------------------------------------- + +#include "serverMetrics/MetricsData.h" + +//----------------------------------------------------------------------- + +class PlanetServerMetricsData : public MetricsData +{ +public: + PlanetServerMetricsData(); + ~PlanetServerMetricsData(); + + virtual void updateData(); + +private: + unsigned long m_numGameServers; + unsigned long m_watcherPort; + + // used for reporting load time of each game server + stdmap::fwd m_gameServerLoadTime; + +private: + + // Disabled. + PlanetServerMetricsData(const PlanetServerMetricsData&); + PlanetServerMetricsData &operator =(const PlanetServerMetricsData&); +}; + + +//----------------------------------------------------------------------- +#endif diff --git a/engine/server/application/PlanetServer/src/shared/PreloadManager.cpp b/engine/server/application/PlanetServer/src/shared/PreloadManager.cpp new file mode 100644 index 00000000..5677eac0 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/PreloadManager.cpp @@ -0,0 +1,492 @@ +// ====================================================================== +// +// PreloadManager.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "PreloadManager.h" + +#include "ConfigPlanetServer.h" +#include "GameServerData.h" +#include "PlanetProxyObject.h" +#include "QuadtreeNode.h" +#include "Scene.h" +#include "serverUtility/PopulationList.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedUtility/DataTable.h" +#include "sharedUtility/DataTableManager.h" + +#include +#include +#include +#include + +// ====================================================================== + +PreloadManager::PreloadManager() : + Singleton2(), + m_serverMap(new ServerMapType), + m_preloadList(new PreloadListType), + m_serversWaiting(new ServersWaitingType), + m_listReceived(false) +{ + ExitChain::add(&remove, "PreloadManager::remove"); +} + +// ---------------------------------------------------------------------- + +PreloadManager::~PreloadManager() +{ + delete m_serverMap; + delete m_preloadList; + delete m_serversWaiting; + + m_serverMap=0; + m_preloadList=0; + m_serversWaiting=0; +} + +// ---------------------------------------------------------------------- + +/** + * Set up the preload list, based on the scene and config options. + */ +void PreloadManager::handlePreloadList() +{ + m_listReceived=true; + + WARNING(ConfigPlanetServer::getLoadWholePlanet() && ConfigPlanetServer::getLoadWholePlanetMultiserver(),("You have specified both loadWholePlanet and loadWholePlanetMultiserver. LoadWholePlanet (single-server) will take precedence.")); + + if (ConfigPlanetServer::getLoadWholePlanet() || PlanetServer::getInstance().isInSpaceMode() || !PlanetServer::getInstance().getEnablePreload()) + { + // Set up a single server to handle the whole planet + PreloadListData fakeBeacon; + fakeBeacon.m_topLeftChunkX = 0; + fakeBeacon.m_topLeftChunkZ = 0; + fakeBeacon.m_bottomRightChunkX = 100; + fakeBeacon.m_bottomRightChunkZ = 100; + fakeBeacon.m_cityServerId = 1; + fakeBeacon.m_wildernessServerId = 1; + + m_preloadList->clear(); + m_preloadList->push_back(fakeBeacon); + + m_serversWaiting->insert(1); + PreloadServerInformation & psi = (*m_serverMap)[1]; + psi.m_actualServerId = 0; + psi.m_timeLoadStarted = time(0); + psi.m_loadTime = -1; + } + // WTF is this ... the tutorial planet bypasses the preload multiserver setting in favor of this pie slice algorithm + // We should not need this anymore with the NPE tutorial + // NPE will use the preload tables for multiserver and the 'NewbieTutorial' manager for placing the tutotial instances + /* + else if(PlanetServer::getInstance().isInTutorialMode()) + { + m_preloadList->clear(); + + int n=ConfigPlanetServer::getNumTutorialServers(); + if ((ConfigPlanetServer::getMaxGameServers() != 0) && (n > ConfigPlanetServer::getMaxGameServers())) + n=ConfigPlanetServer::getMaxGameServers(); + for (int i=0 ; i(i)/static_cast(n)); + + // Make a circle of load beacons. The effect will be to divide the map into wedges + PreloadListData fakeBeacon; + fakeBeacon.m_topLeftChunkX = Node::roundToNode(static_cast(cos(angle) * 1000.0f)); + fakeBeacon.m_topLeftChunkZ = Node::roundToNode(static_cast(sin(angle) * 1000.0f)); + fakeBeacon.m_bottomRightChunkX = fakeBeacon.m_topLeftChunkX + Node::getNodeSize(); + fakeBeacon.m_bottomRightChunkZ = fakeBeacon.m_topLeftChunkZ + Node::getNodeSize(); + fakeBeacon.m_cityServerId = i+1; + fakeBeacon.m_wildernessServerId = i+1; + + DEBUG_REPORT_LOG(true,("Loadbeacon at %i, %i\n",fakeBeacon.m_topLeftChunkX, fakeBeacon.m_topLeftChunkZ)); + + m_preloadList->push_back(fakeBeacon); + PreloadServerInformation & psi = (*m_serverMap)[i+1]; + psi.m_actualServerId = 0; + psi.m_timeLoadStarted = time(0); + psi.m_loadTime = -1; + m_serversWaiting->insert(i+1); + } + }*/ + else + { + if (PlanetServer::getInstance().getEnablePreload()) + { + DataTable * data = DataTableManager::getTable(ConfigPlanetServer::getPreloadDataTableName(),true); + FATAL(data==NULL,("Could not find data table %s, needed for preload",ConfigPlanetServer::getPreloadDataTableName())); + + int numRows = data->getNumRows(); + for (int row=0; rowgetStringValue("Scene",row)==Scene::getInstance().getSceneId()) + { + PreloadListData item; + item.m_topLeftChunkX=data->getIntValue("Top Left Chunk X",row); + item.m_topLeftChunkZ=data->getIntValue("Top Left Chunk Z",row); + item.m_bottomRightChunkX=data->getIntValue("Bottom Right Chunk X",row); + item.m_bottomRightChunkZ=data->getIntValue("Bottom Right Chunk Z",row); + item.m_cityServerId=data->getIntValue("City Server Id",row); + item.m_wildernessServerId=data->getIntValue("Wilderness Server Id",row); + + if (ConfigPlanetServer::getMaxGameServers() != 0) + { + // Reduce the total number of game servers + item.m_cityServerId = ((item.m_cityServerId - 1) % ConfigPlanetServer::getMaxGameServers()) + 1; + + if (item.m_wildernessServerId != 0) + item.m_wildernessServerId = ((item.m_wildernessServerId - 1) % ConfigPlanetServer::getMaxGameServers()) + 1; + } + + m_preloadList->push_back(item); + + ServerMapType::iterator mapIter = m_serverMap->find(item.m_cityServerId); + if (mapIter == m_serverMap->end()) + { + PreloadServerInformation & psi = (*m_serverMap)[item.m_cityServerId]; + psi.m_actualServerId = 0; + psi.m_timeLoadStarted = time(0); + psi.m_loadTime = -1; + m_serversWaiting->insert(item.m_cityServerId); + } + + if (item.m_wildernessServerId != 0) + { + mapIter = m_serverMap->find(item.m_wildernessServerId); + if (mapIter == m_serverMap->end()) + { + PreloadServerInformation & psi = (*m_serverMap)[item.m_wildernessServerId]; + psi.m_actualServerId = 0; + psi.m_timeLoadStarted = time(0); + psi.m_loadTime = -1; + m_serversWaiting->insert(item.m_wildernessServerId); + } + } + } + } + } + } + + FATAL(PlanetServer::getInstance().getEnablePreload() && m_serversWaiting->size()==0,("Planet server started for scene %s, which was not listed in the preload data table", + Scene::getInstance().getSceneId().c_str())); + + std::set startGameServerList; + if (m_serversWaiting->size() < 1) + { + IGNORE_RETURN(startGameServerList.insert(1)); + } + else + { + for (ServersWaitingType::const_iterator i = m_serversWaiting->begin(); i != m_serversWaiting->end(); ++i) + IGNORE_RETURN(startGameServerList.insert(*i)); + } + + Scene::getInstance().assignPreloadsToNodes(); + PlanetServer::getInstance().startGameServer(startGameServerList, 0); +} + +// ---------------------------------------------------------------------- + +/** + * Called when a game server reports that it is ready to receive objects + */ +bool PreloadManager::onGameServerReady(uint32 serverId, PreloadServerId preloadServerId) +{ + bool result = true; + bool noPreload = true; + + // attempt to assign the game server the specified role in the preload list, if possible + bool assigned = false; + + for (ServerMapType::iterator i=m_serverMap->begin(); i!=m_serverMap->end(); ++i) + { + if (i->first == preloadServerId) + { + if (i->second.m_actualServerId == 0) + { + i->second.m_actualServerId = serverId; + sendPreloadsToGameServer(serverId, i->first); + noPreload = false; + assigned = true; + } + + break; + } + } + + if (!assigned) + { + for (ServerMapType::iterator j=m_serverMap->begin(); j!=m_serverMap->end(); ++j) + { + if (j->second.m_actualServerId == 0) + { + // assign the new game server to this role in the preload list + j->second.m_actualServerId = serverId; + sendPreloadsToGameServer(serverId, j->first); + noPreload = false; + break; + } + } + } + + if (noPreload) + { + // this server has nothing to preload, so advance its status right away + GameServerData *data = PlanetServer::getInstance().getGameServerData(serverId); + if(! PlanetServer::getInstance().getEnablePreload()) + { + if (data) + data->preloadComplete(); + + // send PreloadRequestCompleteMessage, because things on the game server wait for it + PlanetServer::getInstance().sendPreloadRequestCompleteMessage(serverId, 0); + } + else + { + if(! PlanetServer::getInstance().isInTutorialMode()) + { + result = false; // disconnect the game server, it's extraneous. + } + } + } + + // if the preload list is empty, preloading is finished as soon as at least 1 game server is up + if (m_serverMap->empty()) + { + DEBUG_REPORT_LOG(ConfigPlanetServer::getLogPreloading(),("Preloading is finished on %s because there was nothing to preload.\n",Scene::getInstance().getSceneId().c_str())); + PlanetServer::getInstance().preloadCompleteOnAllServers(); + } + return result; +} + +// ---------------------------------------------------------------------- + +void PreloadManager::sendPreloadsToGameServer(uint32 realServerId, PreloadServerId preloadServerId) const +{ + if (PlanetServer::getInstance().getEnablePreload()) + Scene::getInstance().loadAllNodesForServer(realServerId, preloadServerId); + + PlanetServer::getInstance().sendPreloadRequestCompleteMessage(realServerId, preloadServerId); +} + +// ---------------------------------------------------------------------- + +/** + * Called when a game server drops connection. Remove it from the preload assignments. + */ +void PreloadManager::removeGameServer(uint32 serverId) +{ + for (ServerMapType::iterator i=m_serverMap->begin(); i!=m_serverMap->end(); ++i) + { + if (i->second.m_actualServerId == serverId) + { + i->second.m_actualServerId = 0; + i->second.m_timeLoadStarted = time(0); + i->second.m_loadTime = -1; + m_serversWaiting->insert(i->first); + break; + } + } +} + +// ---------------------------------------------------------------------- + +/** + * Called when a game server indicates it has all the preload objects + */ +void PreloadManager::preloadCompleteOnServer(uint32 serverId) +{ + DEBUG_REPORT_LOG(ConfigPlanetServer::getLogPreloading(),("Preload is finished on server %lu (planet %s)\n",serverId, Scene::getInstance().getSceneId().c_str())); + + PreloadServerId const preloadId = getPreloadServerId(serverId); + m_serversWaiting->erase(preloadId); + + // mark how long it took the server to load + ServerMapType::iterator i = m_serverMap->find(preloadId); + if ((i != m_serverMap->end()) && (i->second.m_loadTime == -1)) + i->second.m_loadTime = std::max(static_cast(0), static_cast(time(0) - i->second.m_timeLoadStarted)); + + if (isPreloadComplete()) + { + if (ConfigPlanetServer::getEnableStartupCreateProxies()) + { + Scene::getInstance().createAllProxies(); + } + + DEBUG_REPORT_LOG(ConfigPlanetServer::getLogPreloading(),("Preloading is finished on all servers for %s.\n", Scene::getInstance().getSceneId().c_str())); + PlanetServer::getInstance().preloadCompleteOnAllServers(); + } +} + +// ---------------------------------------------------------------------- + +/** + * Finds the preload game server for the specified spot. + */ +PreloadServerId PreloadManager::getPreloadGameServer(int x, int z) +{ + PreloadServerId bestSoFar = 0; + int bestDistance = 0; + for (std::vector::const_iterator item=m_preloadList->begin(); item!=m_preloadList->end(); ++item) + { + if (item->m_cityServerId != 0) + { + int minX=Node::roundToNode(item->m_topLeftChunkX); + int minZ=Node::roundToNode(item->m_topLeftChunkZ); + int maxX=Node::roundToNode(item->m_bottomRightChunkX); + int maxZ=Node::roundToNode(item->m_bottomRightChunkZ); + + if ((x >= minX) && (x < maxX) && (z >= minZ) && (z < maxZ)) + return item->m_cityServerId; + } + if (item->m_wildernessServerId != 0) + { + // Pretend there is a "beacon" in the middle of the preload area + int beaconX=(item->m_topLeftChunkX + item->m_bottomRightChunkX) / 2; + int beaconZ=(item->m_topLeftChunkZ + item->m_bottomRightChunkZ) / 2; + int distanceToBeacon = ((x-beaconX)*(x-beaconX) + (z-beaconZ)*(z-beaconZ)); + + if (bestSoFar == 0 || distanceToBeacon < bestDistance) + { + bestDistance = distanceToBeacon; + bestSoFar = item->m_wildernessServerId; + } + } + } + + return bestSoFar; +} + +// ---------------------------------------------------------------------- + +/** + * Finds the closest operational game server to the specified spot. Used + * when the server specified in the preload list isn't available, because + * either it crashed or it hasn't started yet. + * Considers wilderness first, because city servers are probably more + * heavily loaded. + */ +uint32 PreloadManager::getClosestGameServer (int x, int z) +{ + uint32 bestSoFar = 0; + int bestDistance = 0; + std::vector::const_iterator item; + for (item=m_preloadList->begin(); item!=m_preloadList->end(); ++item) + { + if (item->m_wildernessServerId != 0) + { + int beaconX=(item->m_topLeftChunkX + item->m_bottomRightChunkX) / 2; + int beaconZ=(item->m_topLeftChunkZ + item->m_bottomRightChunkZ) / 2; + int distanceToBeacon = ((x-beaconX)*(x-beaconX) + (z-beaconZ)*(z-beaconZ)); + uint32 realServerId = getRealServerId(item->m_wildernessServerId); + if ((realServerId != 0) && (bestSoFar == 0 || distanceToBeacon < bestDistance)) + { + bestDistance = distanceToBeacon; + bestSoFar = realServerId; + } + } + } + + if (bestSoFar==0) + { + // No wilderness servers. Try city servers + for (item=m_preloadList->begin(); item!=m_preloadList->end(); ++item) + { + if (item->m_cityServerId != 0) + { + int beaconX=(item->m_topLeftChunkX + item->m_bottomRightChunkX) / 2; + int beaconZ=(item->m_topLeftChunkZ + item->m_bottomRightChunkZ) / 2; + int distanceToBeacon = ((x-beaconX)*(x-beaconX) + (z-beaconZ)*(z-beaconZ)); + uint32 realServerId = getRealServerId(item->m_cityServerId); + if ((realServerId != 0) && (bestSoFar == 0 || distanceToBeacon < bestDistance)) + { + bestDistance = distanceToBeacon; + bestSoFar = realServerId; + } + } + } + } + + return bestSoFar; // returning 0 is possible +} + +// ---------------------------------------------------------------------- + +/** + * Adds the loadbeacon locations to a PopulationList structure. + */ +void PreloadManager::updatePopulationList(PopulationList &theList) const +{ + for (std::vector::const_iterator item=m_preloadList->begin(); item!=m_preloadList->end(); ++item) + { + int chunkX=(item->m_topLeftChunkX + item->m_bottomRightChunkX) / 2; + int chunkZ=(item->m_topLeftChunkZ + item->m_bottomRightChunkZ) / 2; + theList.setPopulation(Scene::getInstance().getSceneId(),chunkX, chunkZ, Scene::getInstance().getServerPopulationByLocation(chunkX, chunkZ)); + } +} + +// ---------------------------------------------------------------------- + +PreloadServerId PreloadManager::getPreloadServerId(uint32 realServerId) const +{ + for (ServerMapType::iterator i=m_serverMap->begin(); i!=m_serverMap->end(); ++i) + if (i->second.m_actualServerId == realServerId) + return i->first; + + return 0; +} + +// ---------------------------------------------------------------------- + +bool PreloadManager::isPreloadComplete() const +{ + return (m_listReceived && m_serversWaiting->empty()); +} + +// ---------------------------------------------------------------------- + +void PreloadManager::getDebugString(std::string &output) const +{ + if (isPreloadComplete()) + output="finished"; + else + { + output="loading"; + for (ServersWaitingType::const_iterator i=m_serversWaiting->begin(); i!=m_serversWaiting->end(); ++i) + { + uint32 realServerId=0; + int loadTime=0; + ServerMapType::const_iterator j=m_serverMap->find(*i); + if (j!=m_serverMap->end()) + { + realServerId=j->second.m_actualServerId; + + if (j->second.m_loadTime != -1) + loadTime = j->second.m_loadTime; + else + loadTime = time(0) - j->second.m_timeLoadStarted; + } + + char temp[100]; + sprintf(temp," %lu-%ds (%lu)",*i,loadTime,realServerId); + output+=temp; + } + } +} + +// ---------------------------------------------------------------------- + +uint32 PreloadManager::getRealServerId(PreloadServerId preloadServerId) const +{ + ServerMapType::iterator i=m_serverMap->find(preloadServerId); + if (i!=m_serverMap->end()) + return i->second.m_actualServerId; + + return 0; +} + +// ====================================================================== diff --git a/engine/server/application/PlanetServer/src/shared/PreloadManager.h b/engine/server/application/PlanetServer/src/shared/PreloadManager.h new file mode 100644 index 00000000..b13d53cb --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/PreloadManager.h @@ -0,0 +1,88 @@ +// ====================================================================== +// +// PreloadManager.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_PreloadManager_H +#define INCLUDED_PreloadManager_H + +// ====================================================================== + +#include "Singleton/Singleton2.h" + +// ====================================================================== + +class PopulationList; + +typedef uint32 PreloadServerId; // each server has a id from the preload list, which isn't the same as its processid. Typedef to make the code easier to read. + +class PreloadListData +{ + public: + int m_topLeftChunkX; + int m_topLeftChunkZ; + int m_bottomRightChunkX; + int m_bottomRightChunkZ; + PreloadServerId m_cityServerId; + PreloadServerId m_wildernessServerId; +}; + +class PreloadServerInformation +{ + public: + uint32 m_actualServerId; + time_t m_timeLoadStarted; + int m_loadTime; +}; + +// ====================================================================== + +class PreloadManager : public Singleton2 +{ + public: + PreloadManager(); + virtual ~PreloadManager(); + + public: + bool onGameServerReady (uint32 serverId, PreloadServerId preloadServerId); + void handlePreloadList (); + void removeGameServer (uint32 serverId); + void preloadCompleteOnServer (uint32 serverId); + uint32 getClosestGameServer (int x, int z); + PreloadServerId getPreloadGameServer (int x, int z); + void updatePopulationList (PopulationList &theList) const; + bool isPreloadComplete () const; + PreloadServerId getPreloadServerId (uint32 realServerId) const; + uint32 getRealServerId (PreloadServerId preloadServerId) const; + void getDebugString (std::string &output) const; + + typedef stdmap::fwd ServerMapType; + ServerMapType const * getServerMap () const; + + private: + void sendPreloadsToGameServer (uint32 realServerId, PreloadServerId preloadServerId) const; + + private: + ServerMapType *m_serverMap; // map of server id in preload list ---> actual server id + + typedef stdvector::fwd PreloadListType; + PreloadListType *m_preloadList; + + typedef stdset::fwd ServersWaitingType; + ServersWaitingType *m_serversWaiting; // set of servers (by list id, not actual id) that still need preloads. + + bool m_listReceived; +}; + +//----------------------------------------------------------------------- + +inline PreloadManager::ServerMapType const * PreloadManager::getServerMap () const +{ + return m_serverMap; +} + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/shared/QuadtreeNode.cpp b/engine/server/application/PlanetServer/src/shared/QuadtreeNode.cpp new file mode 100644 index 00000000..240e4196 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/QuadtreeNode.cpp @@ -0,0 +1,582 @@ +// ====================================================================== +// +// Node.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "QuadtreeNode.h" + +#include + +#include "ConfigPlanetServer.h" +#include "PlanetProxyObject.h" +#include "PlanetServer.h" +#include "PreloadManager.h" +#include "Scene.h" +#include "WatcherConnection.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedFoundation/MemoryBlockManager.h" +#include "sharedNetworkMessages/PlanetNodeStatusMessage.h" +#include "sharedLog/Log.h" + +//=================================================================== + +MemoryBlockManager* Node::memoryBlockManager; + +// ====================================================================== + +/** + * Create a new spatial subdivision node. + */ + +Node::Node(int x, int z) : + m_objectList(), + m_subscriptionList(), + m_preferredServer(0), + m_preloadServerId(0), + m_loaded(false), + m_isBorder(false), + m_x(x), + m_z(z) +{ + WARNING_DEBUG_FATAL((x!=roundToNode(x)) || (z!=roundToNode(z)),("Creating node %s, coordinates not on a node boundary.",getDebugNodeString().c_str())); +} + +// ---------------------------------------------------------------------- + +/** + * Places the object in this node and notifies servers as appropriate. + * + * @param newObject The object to add + */ + +void Node::addObject(PlanetProxyObject *newObject) +{ + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Adding object %s to node %s",newObject->getObjectId().getValueString().c_str(),getDebugNodeString().c_str())); + + WARNING_DEBUG_FATAL(!m_loaded,("Attempt to place an object in an unloaded node.")); + WARNING_DEBUG_FATAL(newObject->getObjectId() == NetworkId::cms_invalid,("Attempt to place object 0 in a node.")); + + m_objectList.push_back(newObject); + // We don't send add proxy messages here, because PlanetProxyObject::move() does that. +} + +// ---------------------------------------------------------------------- + +/** + * Remove an object from the node. + */ + +void Node::removeObject(const PlanetProxyObject *oldObject) +{ + NOT_NULL(oldObject); + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Removing object %s from node %s",oldObject->getObjectId().getValueString().c_str(),getDebugNodeString().c_str())); + ObjectListType::iterator i; + for (i=m_objectList.begin();i!=m_objectList.end();++i) + if (*i==oldObject) + break; + if (i==m_objectList.end()) + { + WARNING_DEBUG_FATAL(true,("Attempted to remove object %s that was not in the node.", oldObject->getObjectId().getValueString().c_str())); + return; //lint !e527 Unreachable (in debug mode only) + } + IGNORE_RETURN(m_objectList.erase(i)); +} + +// ---------------------------------------------------------------------- + +/** + * Subscribe a server to this node. + * Makes sure that the specifed server has proxies of the objects in the + * node and gets updates when new objects enter the node. + * @param serverId the server + * @param count How many objects are interested (Note: 0 is a legal value. + * It means that we want the server to know about this node, but there + * aren't any interested objects nearby yet.) + */ + +void Node::subscribeServer(uint32 serverId, int count) +{ + WARNING_DEBUG_FATAL(serverId==0,("Programmer bug: Server 0 passed to subscribeServer")); + WARNING_DEBUG_FATAL(count < 0,("Programmer bug: Passed negative count to subscribeServer")); + + bool newSub=false; + + if (!m_loaded) + { + load(); + } + + SubscriptionListType::iterator i=m_subscriptionList.find(serverId); + if (i!=m_subscriptionList.end()) + { + ((*i).second)+=count; + } + else + { + newSub = true; + m_subscriptionList[serverId]=count; + + for (ObjectListType::iterator j=m_objectList.begin(); j!=m_objectList.end(); ++j) + { + if (serverId!=(*j)->getAuthoritativeServer() && (*j)->getContainedBy() == NetworkId::cms_invalid) + { + (*j)->sendAddProxy(serverId); + } + } + } + + if (PlanetServer::getInstance().isWatcherPresent()) + { + outputStatusToAll(); + } + + if (ConfigPlanetServer::getLogChunkLoading()) + { + if (newSub) + LOG("ChunkLoading",("Node %s Server %lu new subscription, count %i",getDebugNodeString().c_str(), serverId, m_subscriptionList[serverId])); + else + LOG("ChunkLoading",("Node %s Server %lu subscription count changed by %i to %i",getDebugNodeString().c_str(), serverId, count, m_subscriptionList[serverId])); + } +} + +// ---------------------------------------------------------------------- + + +/** + * Decrease the count of objects on a server that are interested in this + * node. If the count reaches 0, the subscription is kept, but + * the load balancing algorithms may remove it later. + */ + +void Node::unsubscribeServer(uint32 serverId, int count, bool clearIfZero) +{ + WARNING_DEBUG_FATAL(serverId==0,("Programmer bug: Server 0 passed to unsubscribeServer")); + WARNING_DEBUG_FATAL(count < 0,("Programmer bug: Passed negative count to unsubscribeServer")); + + bool cleared=false; + + SubscriptionListType::iterator i=m_subscriptionList.find(serverId); + WARNING(i==m_subscriptionList.end(),("Attempted to unsubscribe server %i that was not subscribed to node %s.",serverId,getDebugNodeString().c_str())); + + if (i!=m_subscriptionList.end()) + { + i->second-=count; + + if (i->second < 0) + { + WARNING(true,("Programmer bug: Subscription count for server %i on node %s went negative.",serverId,getDebugNodeString().c_str())); + i->second = 0; + } + + if (clearIfZero && i->second == 0 && m_preferredServer != serverId) + { + cleared=true; + unproxyAllObjects(serverId); + m_subscriptionList.erase(i); + + if (PlanetServer::getInstance().isWatcherPresent()) + { + outputStatusToAll(); + } + } + } + + if (ConfigPlanetServer::getLogChunkLoading()) + { + if (cleared) + LOG("ChunkLoading",("Node %s Server %lu unsubscribed",getDebugNodeString().c_str(), serverId)); + else + LOG("ChunkLoading",("Node %s Server %lu subscription count changed by -%i to %i",getDebugNodeString().c_str(), serverId, count, m_subscriptionList[serverId])); + } +} + +// ---------------------------------------------------------------------- + +/** + * Helper function. Removes the proxies on the specified server. + * Does not update the subscription list. + */ +void Node::unproxyAllObjects(uint32 serverId) +{ + for (ObjectListType::iterator j=m_objectList.begin(); j!=m_objectList.end(); ++j) + (*j)->sendRemoveProxy(serverId); +} + +// ---------------------------------------------------------------------- + +/** + * Load a node from the database. + * Marks the node as loaded, and instructs the database to put the objects + * in the node on a particular game server. The game server will report + * the objects back to us after they get loaded. + */ + +void Node::load() +{ + if (m_loaded) + return; + + if (m_preferredServer == 0) + pickPreferredServer(); + + if (m_preferredServer == 0) + { + WARNING_DEBUG_FATAL(true, ("load failed for node %s because m_preferredServer is 0",getDebugNodeString().c_str())); + return; + } + + m_loaded=true; + + bool sendRequest=true; // in certain modes, we don't need to check the DB for objects + if (PlanetServer::getInstance().isInTutorialMode()) + sendRequest=false; + if (PlanetServer::getInstance().isInSpaceMode() && !(m_x == 0 && m_z == 0)) // only load (0,0) in space + sendRequest=false; + + if (sendRequest) + { + PlanetServer::getInstance().sendChunkRequest(m_preferredServer, m_x,m_z); + if (ConfigPlanetServer::getLogChunkLoading()) + LOG("ChunkLoading", ("RequestChunk: Node %s Server %i",getDebugNodeString().c_str(),m_preferredServer)); + } + + subscribeServer(m_preferredServer,0); +} + +// ---------------------------------------------------------------------- + +/** + * Helper function: picks the best server for this node and puts it + * into m_preferredServer. + */ +void Node::pickPreferredServer() +{ + m_preferredServer = PreloadManager::getInstance().getRealServerId(m_preloadServerId); + if (m_preferredServer == 0) + m_preferredServer = PreloadManager::getInstance().getClosestGameServer(m_x, m_z); + WARNING_DEBUG_FATAL(m_preferredServer==0,("Programmer bug: Node::pickPreferredServer could not pick a server")); +} + +// ---------------------------------------------------------------------- + +/** + * Get a string listing the coordinates of this node. + * For debugging, FATALs, etc. + */ +std::string Node::getDebugNodeString() const +{ + char buffer[100]; + snprintf(buffer,sizeof(buffer)-1,"%s (%i,%i)",Scene::getInstance().getSceneId().c_str(),m_x,m_z); + buffer[sizeof(buffer)-1]='\0'; + return std::string(buffer); +} + +// ---------------------------------------------------------------------- + +/** + * Output the status of the node to the PlanetWatch tool. + */ +void Node::outputStatus(WatcherConnection &conn) const +{ + std::vector servers; + std::vector subscriptionCount; + for (SubscriptionListType::const_iterator i=m_subscriptionList.begin(); i!=m_subscriptionList.end(); ++i) + { + servers.push_back((*i).first); + subscriptionCount.push_back((*i).second); + + WARNING_DEBUG_FATAL((*i).first==0,("Found a 0 in subscrption list (where did it come from?)")); + } + + conn.addNodeUpdate(m_x,m_z,m_loaded, servers, subscriptionCount); +} + +// ---------------------------------------------------------------------- + +void Node::outputStatusToAll() const +{ + const PlanetServer::WatcherList &connections = PlanetServer::getInstance().getWatchers(); + for (PlanetServer::WatcherList::const_iterator i= connections.begin(); i!=connections.end(); ++i) + { + outputStatus(**i); + } +} + +// ---------------------------------------------------------------------- + +/** + * Set up a memory pool for QuadTreeNodes. + */ + +void Node::install() +{ + memoryBlockManager = new MemoryBlockManager("Node::memoryBlockManager", true, sizeof(Node), 0, 0, 0); + + ExitChain::add(&remove, "Node::remove"); +} + +// ---------------------------------------------------------------------- + +void Node::remove() +{ + WARNING_DEBUG_FATAL(!memoryBlockManager,("Node is not installed")); + + delete memoryBlockManager; + memoryBlockManager = 0; +} + +// ---------------------------------------------------------------------- + +void * Node::operator new(size_t size) +{ + UNREF(size); + + WARNING_DEBUG_FATAL(!memoryBlockManager,("Node is not installed")); + + // do not try to alloc a descendant class with this allocator + WARNING_DEBUG_FATAL(size != sizeof(Node),("bad size")); + + return memoryBlockManager->allocate(); +} + +// ---------------------------------------------------------------------- + +void Node::operator delete(void* pointer) +{ + WARNING_DEBUG_FATAL(!memoryBlockManager,("Node is not installed")); + memoryBlockManager->free(pointer); +} + +// ---------------------------------------------------------------------- + +/** + * Load the node onto the specified server. + */ +void Node::load(uint32 serverId) +{ + if (m_loaded) + { + WARNING_DEBUG_FATAL(m_loaded,("Programmer bug: called load on Node %s,.which was already loaded",getDebugNodeString().c_str())); + return; + } + m_preferredServer = serverId; + load(); +} + +// ---------------------------------------------------------------------- + +void Node::getServers(std::vector &serverList) const +{ + for (SubscriptionListType::const_iterator i=m_subscriptionList.begin(); i!=m_subscriptionList.end(); ++i) + { + serverList.push_back(i->first); + } +} + +// ---------------------------------------------------------------------- + +/** + * Migrate the objects to the specfied server, and get rid of any subscribed + * servers that aren't interested in the node anymore + */ +void Node::migrateToServer(uint32 newServer) +{ + WARNING_DEBUG_FATAL (!m_loaded,("Programmer bug: called migrateToServer(%i) on unloaded node %s",newServer,getDebugNodeString().c_str())); + + if (m_preferredServer == newServer) + return; + + subscribeServer(newServer,0); + m_preferredServer = newServer; + + for (ObjectListType::iterator i=m_objectList.begin(); i!=m_objectList.end(); ++i) + { + (*i)->unsubscribeSurroundingNodes(true); + (*i)->changeAuthority(newServer, false, false); + (*i)->subscribeSurroundingNodes(); + } + + // remove any zero subscriptions: + + std::vector unsubscribeList; + + for (SubscriptionListType::iterator subscription=m_subscriptionList.begin(); subscription!=m_subscriptionList.end(); ++subscription) + if (subscription->second == 0 && subscription->first != newServer) + unsubscribeList.push_back(subscription->first); + + for (std::vector::iterator s=unsubscribeList.begin(); s!=unsubscribeList.end(); ++s) + { + unproxyAllObjects(*s); + IGNORE_RETURN(m_subscriptionList.erase(*s)); + } + + if (PlanetServer::getInstance().isWatcherPresent()) + { + outputStatusToAll(); + } + + if (ConfigPlanetServer::getLogChunkLoading()) + LOG("ChunkLoading",("Node %s Migrate to server %lu",getDebugNodeString().c_str(), newServer)); +} + +// ---------------------------------------------------------------------- + +/** + * Check that this node is assigned to the right server, and fix it if + * it isn't. + */ +void Node::checkServerAssignment() +{ + if (!m_loaded) + return; + uint32 bestServer = PreloadManager::getInstance().getRealServerId(m_preloadServerId); + if (bestServer==0) + return; + + if (m_preferredServer != bestServer) + migrateToServer(bestServer); +} + +// ---------------------------------------------------------------------- + +void Node::handleCrash(uint32 crashedServer, std::vector &subscriptionFixups) +{ + bool wasSubscribed = false; + + { + for (SubscriptionListType::iterator i=m_subscriptionList.begin(); i!=m_subscriptionList.end();) + { + if (i->first == crashedServer) + { + m_subscriptionList.erase(i++); + wasSubscribed = true; + } + else + { + ++i; + } + } + } + + if (m_preferredServer == crashedServer) + { + if (!m_subscriptionList.empty()) + { + // pick a new server for this node and move the objects + + uint32 newServer = m_subscriptionList.begin()->first; + m_preferredServer = newServer; + WARNING_DEBUG_FATAL(newServer == crashedServer,("Programmer bug: crashed server was still in the subscription list after it was supposedly removed.")); + } + else + { + // no other server has proxies. Unload the node. + m_loaded = false; + m_preferredServer = 0; + + for (ObjectListType::iterator i=m_objectList.begin(); i!=m_objectList.end(); ++i) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading", + ("Handling crash: Dropping object %s because node %s was dropped", + (*i)->getObjectId().getValueString().c_str(),getDebugNodeString().c_str())); + + Scene::getInstance().removeObjectFromMap((*i)->getObjectId(),false); + delete *i; + } + m_objectList.clear(); + } + } + + { + for (ObjectListType::iterator i=m_objectList.begin(); i!=m_objectList.end(); ++i) + { + if ((*i)->getContainedBy() == NetworkId::cms_invalid) + { + if (crashedServer==(*i)->getAuthoritativeServer()) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading", + ("Handling crash: moving object %s from server %lu to server %lu", + (*i)->getObjectId().getValueString().c_str(),crashedServer,m_preferredServer)); + + (*i)->changeAuthority(m_preferredServer, true, false); + (*i)->sendRemoveProxy(crashedServer); + if ((*i)->getInterestRadius() > 0) + subscriptionFixups.push_back(*i); + } + else + { + // tell objects they don't have a proxy on this server anymore + if (wasSubscribed) + (*i)->sendRemoveProxy(crashedServer); + } + } + } + } + + if (PlanetServer::getInstance().isWatcherPresent()) + { + outputStatusToAll(); + } +} + +// ---------------------------------------------------------------------- + +bool Node::isBorderNode() const +{ + WARNING_DEBUG_FATAL(getPreferredServer()==0,("Programmer bug: called isBorderNode() on node %s, which has 0 for its preferred server.",getDebugNodeString().c_str())); + + if (m_subscriptionList.size() < 2) + { + m_isBorder = false; + return false; + } + + if (m_isBorder) + return true; + + int maxX=m_x+getNodeSize(); + int maxZ=m_z+getNodeSize(); + for (int x=m_x-getNodeSize();x<=maxX;x+=getNodeSize()) + for (int z=m_z-getNodeSize();z<=maxZ;z+=getNodeSize()) + if (!(x==m_x && z==m_z)) + { + const Node *node = Scene::getInstance().findNodeByRoundedPositionConst(x,z); + if (node && node->getPreferredServer()!=0 && node->getPreferredServer() != getPreferredServer()) + { + m_isBorder = true; + return true; + } + } + + return false; +} + +// ---------------------------------------------------------------------- + +bool Node::isServerSubscribed(uint32 serverId) const +{ + return (m_subscriptionList.find(serverId)!=m_subscriptionList.end()); +} + +// ---------------------------------------------------------------------- + +void Node::setPreloadServerId(PreloadServerId preloadServerId) +{ + m_preloadServerId = preloadServerId; +} + +// ---------------------------------------------------------------------- + +PreloadServerId Node::getPreloadServerId() const +{ + return m_preloadServerId; +} + +// ====================================================================== diff --git a/engine/server/application/PlanetServer/src/shared/QuadtreeNode.h b/engine/server/application/PlanetServer/src/shared/QuadtreeNode.h new file mode 100644 index 00000000..04b6d719 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/QuadtreeNode.h @@ -0,0 +1,119 @@ +// ====================================================================== +// +// Node.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Node_H +#define INCLUDED_Node_H + +// ====================================================================== + +#include +#include +#include +#include + +class PlanetProxyObject; +class MemoryBlockManager; +class WatcherConnection; + +// ====================================================================== + +class Node +{ + public: + typedef std::map SubscriptionListType; + + public: + Node(int x, int z); + + public: + void addObject (PlanetProxyObject *newObject); + void checkServerAssignment(); + void handleCrash (uint32 crashedServer, stdvector::fwd &subscriptionFixups); + void load (); + void migrateToServer (uint32 newServerId); + void load (uint32 serverId); + void removeObject (const PlanetProxyObject *object); + void subscribeServer (uint32 serverId, int count); + void unsubscribeServer (uint32 serverId, int count, bool clearIfZero); + void setPreloadServerId(PreloadServerId preloadServerId); + + public: + static int getNodeSize (); + static int roundToNode (int coordinate); + bool isBorderNode () const; + bool isLoaded () const; + bool isServerSubscribed(uint32 serverId) const; + std::string getDebugNodeString() const; + uint32 getPreferredServer() const; + PreloadServerId getPreloadServerId() const; + void getServers (std::vector &serverList) const; + void outputStatus (WatcherConnection &conn) const; + void outputStatusToAll () const; + + public: + static void install (); + static void remove (); + static void* operator new (size_t size); + static void operator delete (void* pointer); + + private: + void pickPreferredServer(); + void unproxyAllObjects (uint32 serverId); + + private: + typedef std::vector ObjectListType; + + private: + ObjectListType m_objectList; + SubscriptionListType m_subscriptionList; + uint32 m_preferredServer; + PreloadServerId m_preloadServerId; + bool m_loaded; + mutable bool m_isBorder; + + /** + * Coordinates of the node are represented by the minimum X and minimum Z coordinates. + */ + int m_x, m_z; + + static MemoryBlockManager* memoryBlockManager; + + private: + Node(); //disable +}; + +// ====================================================================== + +inline bool Node::isLoaded() const +{ + return m_loaded; +} + +// ---------------------------------------------------------------------- + +inline int Node::roundToNode(int coordinate) +{ + return (coordinate >= 0) ? (coordinate/getNodeSize())*getNodeSize() : ((coordinate-getNodeSize()+1)/getNodeSize())*getNodeSize(); //lint !e834 // Lint doesn't like a-b+c +} + +// ---------------------------------------------------------------------- + +inline int Node::getNodeSize() +{ + return 100; +} + +// ---------------------------------------------------------------------- + +inline uint32 Node::getPreferredServer() const +{ + return m_preferredServer; +} + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/shared/Scene.cpp b/engine/server/application/PlanetServer/src/shared/Scene.cpp new file mode 100644 index 00000000..3809086a --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/Scene.cpp @@ -0,0 +1,674 @@ +// ====================================================================== +// +// Scene.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "Scene.h" + +#include "ConfigPlanetServer.h" +#include "GameServerConnection.h" +#include "GameServerData.h" +#include "PlanetProxyObject.h" +#include "PreloadManager.h" +#include "QuadtreeNode.h" +#include "serverNetworkMessages/PlanetRemoveObject.h" +#include "serverNetworkMessages/PopulationListMessage.h" +#include "serverNetworkMessages/UpdateObjectOnPlanetMessage.h" +#include "serverUtility/PopulationList.h" +#include "sharedFoundation/ExitChain.h" +#include "sharedLog/Log.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" +#include "sharedUtility/LocationManager.h" + +// ====================================================================== + +Scene::Scene() : + Singleton2(), + MessageDispatch::Receiver(), + m_sceneId(), + m_objects(), + m_deletedObjects(), + m_nodeMap(), + m_populationList(new PopulationList), + m_populationCountTimer(static_cast(ConfigPlanetServer::getPopulationCountTime())), + m_maxCoordinate(0) +{ + ExitChain::add(&remove, "Scene::remove"); + + connectToMessage("GameConnectionClosed"); + connectToMessage("UpdateObjectOnPlanetMessage"); + connectToMessage("PlanetRemoveObject"); + connectToMessage("ForceLoadArea"); +} + +// ---------------------------------------------------------------------- + +/** + * Set the scene ID. Make this part of the constructor if we ever make + * this a non-singleton class. + */ +void Scene::setSceneId(const std::string &sceneId) +{ + m_sceneId=sceneId; + + //-- tell the LocationManager what planet we're on + LocationManager::setPlanetName (sceneId.c_str ()); +} + +// ---------------------------------------------------------------------- + +/** + * Called to add an object to the scene. Scene takes ownership + * of the object and will be resposible for deleting it. + */ + +void Scene::addObject(PlanetProxyObject *newObject) +{ + m_objects[newObject->getObjectId()]=newObject; + newObject->addServerStatistics(); +} + +// ---------------------------------------------------------------------- + +PlanetProxyObject *Scene::findObjectByID(const NetworkId &objectID) const +{ + ObjectMapType::const_iterator i=m_objects.find(objectID); + if (i==m_objects.end()) + return NULL; + else + return ((*i).second); +} + +// ---------------------------------------------------------------------- + +bool Scene::isObjectLoaded(const NetworkId &networkId) const +{ + return m_objects.find(networkId) != m_objects.end(); +} + +// ---------------------------------------------------------------------- + +void Scene::receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message) +{ + const GameServerConnection *gameServer=dynamic_cast(&source); + WARNING_DEBUG_FATAL(gameServer==0,("Source was NULL or source was not a GameServerConnection.")); + + if (message.isType("GameConnectionClosed")) + { + DEBUG_REPORT_LOG(true, ("Handling a game server crash for server %lu\n", gameServer->getProcessId())); + handleCrash(gameServer->getProcessId()); + } + + else if (message.isType("UpdateObjectOnPlanetMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + UpdateObjectOnPlanetMessage msg(ri); + + int interestRadius = msg.getInterestRadius(); + + if(msg.getWatched() && interestRadius == 0) + interestRadius = 1; + + if (m_deletedObjects.find(msg.getObjectId())!=m_deletedObjects.end()) + { + DEBUG_REPORT_LOG(true,("Ignoring update for object %s which has been removed.\n",msg.getObjectId().getValueString().c_str())); + } + else + { + NetworkId container(msg.getTopmostContainer()); + if (container==msg.getObjectId()) // game server sends container == objectId if the object is not contained + container=NetworkId::cms_invalid; + + bool processUpdate = true; + if (container != NetworkId::cms_invalid) + { + PlanetProxyObject const *const containerObject = findObjectByID(container); + if (containerObject) + { + if (containerObject->getAuthoritativeServer() != gameServer->getProcessId()) + { + // This update message came from a server that is not + // authoritative for the container of this object. This + // can happen when the planet server is in control of + // switching the container's authority but is not in + // charge of changing the container content's authority. + // This happens to the rider of a mount when the mount + // switches authority. + processUpdate = false; + } + } + } + + if (processUpdate) + { + PlanetProxyObject *theObject=findObjectByID(msg.getObjectId()); + if (!theObject) + { + // this is a new object + theObject=new PlanetProxyObject(msg.getObjectId()); + addObject(theObject); + } + + theObject->onReceivedMessageFromServer(gameServer->getProcessId()); + theObject->update(msg.getX(), msg.getY(), msg.getZ(), container, gameServer->getProcessId(), interestRadius, msg.getObjectTypeTag(), msg.getLevel(), msg.getHibernating(), msg.getTemplateCrc(), msg.getAiActivity(), msg.getCreationType()); + + //-- handle updating LocationManager + if (msg.getLocationReservationRadius () > 0.f) + LocationManager::updateObject (msg.getObjectId (), msg.getX (), msg.getZ (), msg.getLocationReservationRadius ()); + } + } + } + else if (message.isType("PlanetRemoveObject")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + PlanetRemoveObject msg(ri); + handleRemoveObject(msg.getObjectId()); + + //-- handle updating LocationManager + LocationManager::removeObject (msg.getObjectId ()); + } + else if (message.isType("ForceLoadArea")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage, std::pair > > > const msg(ri); + + handleForceLoadArea( + msg.getValue().first, + static_cast(msg.getValue().second.first.first), + static_cast(msg.getValue().second.first.second), + static_cast(msg.getValue().second.second.first), + static_cast(msg.getValue().second.second.second)); + } + else + { + DEBUG_REPORT_LOG(true,("Scene.cpp received an unidentified message.\n")); + } +} + +// ---------------------------------------------------------------------- + +void Scene::subscribeServer(uint32 server, int x, int z, int interestRadius, int count) +{ + NodeListType nodelist; + findIntersection(nodelist,x,z,interestRadius); + + for (NodeListType::iterator i=nodelist.begin(); i!=nodelist.end(); ++i) + { + (*i)->subscribeServer(server, count); + } +} + +// ---------------------------------------------------------------------- + +/** + * Given coordinates, return the node that encloses those coordinates. + * Creates the node if it doesn't exist already. + */ +Node *Scene::findNodeByPosition(int x, int z) +{ + return findNodeByRoundedPosition(Node::roundToNode(x),Node::roundToNode(z)); +} + +// ---------------------------------------------------------------------- + +/** + * Given coordinates, return a const pointer to the node that encloses + * those coordinates. Will not create new nodes, so it may return NULL. + */ +const Node *Scene::findNodeByPositionConst(int x, int z) const +{ + return findNodeByRoundedPositionConst(Node::roundToNode(x),Node::roundToNode(z)); +} + +// ---------------------------------------------------------------------- + +/** + * Given coordinates that are known to be a node boundary, return the node. + * Creates the node if it doesn't exist already. + */ +Node *Scene::findNodeByRoundedPosition(int x, int z) +{ + DEBUG_FATAL((x!=Node::roundToNode(x)) || (z!=Node::roundToNode(z)),("Parameters to findNodeByRoundedPosistion weren't rounded.")); + Node *result=m_nodeMap[Coordinates(x,z)]; + if (!result) + { + result=new Node(x,z); + m_nodeMap[Coordinates(x,z)]=result; + } + return result; +} + +// ---------------------------------------------------------------------- + +/** + * Given coordinates that are known to be a node boundary, return a const pointer + * to the node. Does not create new nodes, so may return NULL. + */ +const Node *Scene::findNodeByRoundedPositionConst(int x, int z) const +{ + WARNING_DEBUG_FATAL((x!=Node::roundToNode(x)) || (z!=Node::roundToNode(z)),("Parameters to findNodeByRoundedPosistionConst weren't rounded.")); + NodeMapType::const_iterator i=m_nodeMap.find(Coordinates(x,z)); + if (i!=m_nodeMap.end()) + return (*i).second; + else + return NULL; +} + +// ---------------------------------------------------------------------- + +/** + * Given coordinates and a radius, find a set of nodes that contains the circle. + * + * May return some nodes that don't contain any part of the circle, but + * is guaranteed to return at least every node that the circle intersects. + * Will add nodes if they don't exist. + */ + +void Scene::findIntersection(NodeListType &results,int x, int z, int radius) +{ + int minX=Node::roundToNode(x-radius); + int minZ=Node::roundToNode(z-radius); + int maxX=Node::roundToNode(x+radius); + int maxZ=Node::roundToNode(z+radius); + int nodeSize=Node::getNodeSize(); + + for (int loopX=minX; loopX <= maxX; loopX+=nodeSize) + for (int loopZ=minZ; loopZ <= maxZ; loopZ+=nodeSize) + results.push_back(findNodeByRoundedPosition(loopX,loopZ)); +} + +// ---------------------------------------------------------------------- + +/** + * Removes an object from the list of objects. + * + * Places the object in a list of pending deletes. Further updates to + * this object will be ignored. + * Doesn't remove the object from the quadtree or notify any servers. + * This function should only be called by PlanetProxyObject::unload(). + * + * @param objectId Object to remove. + * @param addToDeletedList true if we should remember that we deleted the object, + * false if not. Generally, we don't need to put the object in the deleted map + * if the game server initated the removal. If we initated it, we remember + * the object in the deleted map so that we ignore all updates to it until + * the game server acknowledges the removal. + */ +void Scene::removeObjectFromMap(const NetworkId &objectId, bool addToDeletedList) +{ + ObjectMapType::iterator i=m_objects.find(objectId); + if (i==m_objects.end()) + { + DEBUG_REPORT_LOG(true,("Requested removing object %s, but it was not in the list.\n",objectId.getValueString().c_str())); + } + else + { + m_objects.erase(i); + } + + if (addToDeletedList) + IGNORE_RETURN(m_deletedObjects.insert(objectId)); +} + +// ---------------------------------------------------------------------- + +/** + * Handler for the PlanetRemoveObject message. + */ + +void Scene::handleRemoveObject(const NetworkId &objectId) +{ + DeletedSetType::iterator i=m_deletedObjects.find(objectId); + if (i!=m_deletedObjects.end()) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Removal of object %s confirmed.",objectId.getValueString().c_str())); + m_deletedObjects.erase(i); + } + else + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Removal of object %s initiated by GameServer.",objectId.getValueString().c_str())); + PlanetProxyObject *theObject=findObjectByID(objectId); + if (theObject) + { + theObject->unload(false); + + if (theObject->getInterestRadius()>0) + theObject->unsubscribeSurroundingNodes(false); + delete theObject; + } + else + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Object %s was unknown. Removal request ignored.",objectId.getValueString().c_str())); + } + } +} + +// ---------------------------------------------------------------------- + +/** + * Given an object, find which server it is on. + * Used by the login process, for example, to find where the building + * a player is in is loaded. + */ +uint32 Scene::getGameServerForObject(const NetworkId &object) const +{ + ObjectMapType::const_iterator i=m_objects.find(object); + if (i!=m_objects.end()) + { + const PlanetProxyObject *obj=(*i).second; + NOT_NULL(obj); + return obj->getAuthoritativeServer(); + } + else + return 0; +} + +// ---------------------------------------------------------------------- + +/** + * Output the status of the scene to a Watcher. (Call when a new watcher + * connects, for example.) + */ +void Scene::outputStatus(WatcherConnection &conn) const +{ + for (NodeMapType::const_iterator i=m_nodeMap.begin(); i!=m_nodeMap.end(); ++i) + { + (*i).second->outputStatus(conn); + } + + for (ObjectMapType::const_iterator j=m_objects.begin(); j!=m_objects.end(); ++j) + { + (*j).second->outputStatus(conn, false); + } +} + +// ---------------------------------------------------------------------- + +Scene::~Scene() +{ + for (NodeMapType::iterator i=m_nodeMap.begin(); i!=m_nodeMap.end(); ++i) + { + delete (*i).second; + (*i).second=0; + } + + for (ObjectMapType::iterator j=m_objects.begin(); j!=m_objects.end(); ++j) + { + delete (*j).second; + (*j).second = 0; + } +} + +// ---------------------------------------------------------------------- + +/** + * Every so often, check for nodes that can be unloaded. If any are found, + * unload one node each frame to avoid sending too many unloads to the + * game servers at the same time. + */ +void Scene::update(float time) +{ + //-- Update population count + if (m_populationCountTimer.updateSubtract(time)) + { + PreloadManager::getInstance().updatePopulationList(*m_populationList); + + PopulationListMessage msg(*m_populationList); + PlanetServer::getInstance().sendToCentral(msg, true); + } +} + +// ---------------------------------------------------------------------- + +/** + * For each node, record the preload server id. + */ +void Scene::assignPreloadsToNodes() +{ + for (int x=-getMaxCoordinate(); x<= getMaxCoordinate(); x+=Node::getNodeSize()) + { + for (int z=-getMaxCoordinate(); z<= getMaxCoordinate(); z+=Node::getNodeSize()) + { + Node *node = findNodeByRoundedPosition(x,z); + node->setPreloadServerId(PreloadManager::getInstance().getPreloadGameServer(x,z)); + } + } +} + +// ---------------------------------------------------------------------- + +/** + * Check the map for any nodes that belong to the specified server, and + * load them. If they are already loaded, move them to the right server. + */ +void Scene::loadAllNodesForServer(uint32 server, PreloadServerId preloadServerId) +{ + for (int x=-getMaxCoordinate(); x<= getMaxCoordinate(); x+=Node::getNodeSize()) + { + for (int z=-getMaxCoordinate(); z<= getMaxCoordinate(); z+=Node::getNodeSize()) + { + Node *node = findNodeByRoundedPosition(x,z); + if (node->getPreloadServerId()==preloadServerId) + { + if (ConfigPlanetServer::getLogChunkLoading()) + LOG("ChunkLoading",("Node %s preload to server %lu",node->getDebugNodeString().c_str(), server)); + + if (!node->isLoaded()) + node->load(server); + else + node->migrateToServer(server); + } + } + } +} + +// ---------------------------------------------------------------------- + +/** + * Make sure that all nodes are on the server that they should be. + * Move any nodes that aren't. They could be on the wrong server due + * to server crashes, etc. + */ +void Scene::checkServerAssignments() +{ + for (NodeMapType::iterator i=m_nodeMap.begin(); i!=m_nodeMap.end(); ++i) + i->second->checkServerAssignment(); +} + +// ---------------------------------------------------------------------- + +void Scene::handleCrash(uint32 gameServerId) +{ + // wait for the next save before allowing new loads + if (ConfigPlanetServer::getRequestDbSaveOnGameServerCrash()) + PlanetServer::getInstance().requestAndWaitForSave(); + + PreloadManager::getInstance().removeGameServer(gameServerId); + + std::vector subscriptionFixups; + + for (NodeMapType::iterator nodeMapIter = m_nodeMap.begin(); nodeMapIter != m_nodeMap.end(); ++nodeMapIter) + { + nodeMapIter->second->handleCrash(gameServerId, subscriptionFixups); + } + + // These have to be done after all the nodes are examined, because otherwise it might think + // a server has proxies of the objects on a crashed node when it really doesn't + for (std::vector::iterator j=subscriptionFixups.begin(); j!=subscriptionFixups.end(); ++j) + (*j)->subscribeSurroundingNodes(); + +} + +// ---------------------------------------------------------------------- + +int Scene::getServerPopulationByLocation(int x, int z) const +{ + const Node *node=findNodeByPositionConst(x,z); + if (node && node->isLoaded()) + { + uint32 server = node->getPreferredServer(); + return PlanetServer::getInstance().getInterestCreatureObjectCountForServer(server); + } + else + return 0; +} + +// ---------------------------------------------------------------------- + +bool Scene::requestSameServer(const NetworkId &id1, const NetworkId &id2) +{ + PlanetProxyObject *obj1 = findObjectByID(id1); + PlanetProxyObject *obj2 = findObjectByID(id2); + if (!obj1 || !obj2) + return false; + + if (!obj1->isAuthorityClean() || !obj2->isAuthorityClean()) + return false; + + uint32 server1 = obj1->getAuthoritativeServer(); + uint32 server2 = obj2->getAuthoritativeServer(); + if (server1 == server2) + return true; + + if (!obj1->getNode() || !obj2->getNode()) + return false; + + uint32 preferredServer = obj1->getNode()->getPreferredServer(); + if (obj2->getNode()->getPreferredServer() == preferredServer) + { + if (server1 == preferredServer) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Moving object %s to server %lu, so that it is on the same server as %s",id2.getValueString().c_str(),server1,id1.getValueString().c_str())); + obj2->changeAuthorityAndSubscriptions(server1); + return true; + } + else if (server2 == preferredServer) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Moving object %s to server %lu, so that it is on the same server as %s",id1.getValueString().c_str(),server2,id2.getValueString().c_str())); + obj1->changeAuthorityAndSubscriptions(server2); + return true; + } + } + + if (obj2->wouldAuthorityBeOk(server1)) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Moving object %s to server %lu, so that it is on the same server as %s",id2.getValueString().c_str(),server1,id1.getValueString().c_str())); + obj2->changeAuthorityAndSubscriptions(server1); + return true; + } + + if (obj1->wouldAuthorityBeOk(server2)) + { + if (ConfigPlanetServer::getLogObjectLoading()) + LOG("ObjectLoading",("Moving object %s to server %lu, so that it is on the same server as %s",id1.getValueString().c_str(),server2,id2.getValueString().c_str())); + obj1->changeAuthorityAndSubscriptions(server2); + return true; + } + + // Neither server will work. + return false; +} + +//------------------------------------------------------------------------------------------ + +bool Scene::requestAuthTransfer(const NetworkId &id1, uint32 newServer) +{ + PlanetProxyObject *obj1 = findObjectByID(id1); + if (!obj1) + return false; + + uint32 server1 = obj1->getAuthoritativeServer(); + + if (server1 == newServer) + return true; + + if (obj1->wouldAuthorityBeOk(newServer)) + { + obj1->changeAuthorityAndSubscriptions(newServer); + return true; + } + + // Requested server cannot be used. + return false; +} + +// ---------------------------------------------------------------------- + +void Scene::handleForceLoadArea(uint32 gameServerId, int x1, int z1, int x2, int z2) +{ + typedef Node QuadTreeNode; + + int const zStart = QuadTreeNode::roundToNode(static_cast(z1)); + int const xStart = QuadTreeNode::roundToNode(static_cast(x1)); + + for (int nz = zStart; nz <= z2; nz += 100) + { + for (int nx = xStart; nx <= x2; nx += 100) + { + Node * const node = findNodeByPosition(nx, nz); + node->subscribeServer(gameServerId, 1); + } + } +} + +// ---------------------------------------------------------------------- + +int Scene::getMaxCoordinate() const +{ + if (m_maxCoordinate==0) + return 8000; // Assume largest possible map until m_maxCoordiate is set + return m_maxCoordinate; +} + +// ---------------------------------------------------------------------- + +void Scene::setMapSize(int mapSize) +{ + int maxCoordinate = Node::roundToNode(mapSize/2) + Node::getNodeSize(); + if (m_maxCoordinate == 0) + m_maxCoordinate = maxCoordinate; + else + FATAL(m_maxCoordinate!=maxCoordinate,("Game servers for planet %s sent different sizes for the maps. Possible sizes are %i and %i", m_sceneId.c_str(), m_maxCoordinate, maxCoordinate)); +} + +// ---------------------------------------------------------------------- + +/** + * Create all the proxies that would ever be needed, in a single pass + */ +void Scene::createAllProxies() +{ + static int const subscriptionRange = ConfigPlanetServer::getMaxInterestRadius() + Node::getNodeSize(); // The logic in PlanetProxyObject effectively creates an overlap area of interestRadius + nodeSize, so don't change this without changing the logic in PlanetProxyObject + + for (int x=-getMaxCoordinate(); x<= getMaxCoordinate(); x+=Node::getNodeSize()) + { + for (int z=-getMaxCoordinate(); z<= getMaxCoordinate(); z+=Node::getNodeSize()) + { + Node * const node = findNodeByRoundedPosition(x,z); + uint32 serverId = NON_NULL(node)->getPreferredServer(); + + if (serverId!=0) + { + for (int subX=x-subscriptionRange; subX<= x+subscriptionRange; subX+=Node::getNodeSize()) + { + for (int subZ=z-subscriptionRange; subZ<= z+subscriptionRange; subZ+=Node::getNodeSize()) + { + NON_NULL(findNodeByRoundedPosition(subX, subZ))->subscribeServer(serverId, 0); + } + } + } + } + } +} + +// ====================================================================== + diff --git a/engine/server/application/PlanetServer/src/shared/Scene.h b/engine/server/application/PlanetServer/src/shared/Scene.h new file mode 100644 index 00000000..95c965a4 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/Scene.h @@ -0,0 +1,142 @@ +// ====================================================================== +// +// Scene.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_Scene_H +#define INCLUDED_Scene_H + +// ====================================================================== + +#include +#include +#include +#include + +#include "sharedFoundation/NetworkId.h" +#include "sharedFoundation/Timer.h" +#include "sharedMessageDispatch/Receiver.h" +#include "Singleton/Singleton2.h" +#include "sharedMath/Vector.h" + +class PlanetProxyObject; +class PopulationList; +class Node; + +// ====================================================================== + +/** + * Organizes all data on the Planet server relating to a particular scene. + */ +class Scene : public Singleton2, public MessageDispatch::Receiver +{ + public: + // Some functions return lists of nodes + typedef std::vector NodeListType; + + public: + Scene(); + ~Scene(); + + public: + virtual void receiveMessage (const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message); + Node * findNodeByPosition (int x, int z); + Node * findNodeByRoundedPosition (int x, int z); + void addObject (PlanetProxyObject *newObject); + void findIntersection (NodeListType &results,int x, int z, int radius); + void assignPreloadsToNodes (); + void loadAllNodesForServer (uint32 server, PreloadServerId preloadServerId); + void checkServerAssignments (); + void removeObjectFromMap (const NetworkId &objectId, bool addToDeletedList); + void setSceneId (const std::string &sceneId); + void subscribeServer (uint32 server, int x, int z, int interestRadius, int count=1); + void update (float time); + bool requestSameServer (const NetworkId &id1, const NetworkId &id2); + bool requestAuthTransfer (const NetworkId &id1, uint32 newServer); + void setMapSize (int mapSize); + void createAllProxies (); + + public: + const Node * findNodeByPositionConst (int x, int z) const; + const Node * findNodeByRoundedPositionConst (int x, int z) const; + bool isObjectLoaded (const NetworkId &networkId) const; + PlanetProxyObject * findObjectByID (const NetworkId &objectID) const; + const std::string & getSceneId () const; + uint32 getGameServerForObject (const NetworkId &object) const; + void outputStatus (WatcherConnection &conn) const; + int getServerPopulationByLocation (int x, int z) const; + int getMaxCoordinate () const; + + private: + void handleRemoveObject (const NetworkId &objectId); + void handleCrash (uint32 gameServerId); + void handleForceLoadArea (uint32 gameServerId, int x1, int z1, int x2, int z2); + + private: + struct Coordinates + { + public: + int m_x; + int m_z; + Coordinates(int x, int z); + bool operator==(const Coordinates &rhs) const; + + class Hasher + { + public: + size_t operator()(const Coordinates &c) const; + }; + + private: + Coordinates(); + }; + + typedef std::hash_map ObjectMapType; + typedef std::hash_set DeletedSetType; + typedef std::hash_map NodeMapType; + + private: + std::string m_sceneId; + ObjectMapType m_objects; + DeletedSetType m_deletedObjects; + NodeMapType m_nodeMap; + PopulationList * m_populationList; + Timer m_populationCountTimer; + int m_maxCoordinate; +}; + +// ====================================================================== + +inline Scene::Coordinates::Coordinates(int x, int z) : + m_x(x), + m_z(z) +{ +} + +// ---------------------------------------------------------------------- + +inline bool Scene::Coordinates::operator==(const Coordinates &rhs) const +{ + return ((m_x==rhs.m_x) && (m_z==rhs.m_z)); +} + +// ---------------------------------------------------------------------- + +inline size_t Scene::Coordinates::Hasher::operator()(const Coordinates &c) const +{ + // will need to change this if we make planets bigger than 32k * 32k + return (c.m_x<<16 | c.m_z); //lint !e701 shift left of signed +} + +// ---------------------------------------------------------------------- + +inline const std::string &Scene::getSceneId() const +{ + return m_sceneId; +} + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/shared/TaskConnection.cpp b/engine/server/application/PlanetServer/src/shared/TaskConnection.cpp new file mode 100644 index 00000000..15636b14 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/TaskConnection.cpp @@ -0,0 +1,93 @@ +// TaskConnection.cpp +// copyright 2000 Verant Interactive +// Author: Justin Randall + + +//----------------------------------------------------------------------- + +#include "FirstPlanetServer.h" +#include "serverNetworkMessages/TaskConnectionIdMessage.h" +#include "serverNetworkMessages/TaskUtilization.h" +#include "sharedFoundation/ConfigFile.h" +#include "sharedNetwork/NetworkSetupData.h" +#include "TaskConnection.h" + +//----------------------------------------------------------------------- + +TaskConnection::TaskConnection(const std::string & a, const unsigned short p) : +ServerConnection(a, p, NetworkSetupData()) +{ +} + +//----------------------------------------------------------------------- + +TaskConnection::TaskConnection(UdpConnectionMT * u, TcpClient * t) : +ServerConnection(u, t) +{ +} + +//----------------------------------------------------------------------- + +TaskConnection::~TaskConnection() +{ +} + +//----------------------------------------------------------------------- + +void TaskConnection::onConnectionClosed() +{ + static MessageConnectionCallback m("TaskConnectionClosed"); + emitMessage(m); + PlanetServer::getInstance().setTaskManager(0); +} + +//----------------------------------------------------------------------- + +void TaskConnection::onConnectionOpened() +{ + PlanetServer::getInstance().setTaskManager(this); + + // get cluster name + std::string clusterName; + ConfigFile::Section const * const sec = ConfigFile::getSection("TaskManager"); + if (sec) + { + ConfigFile::Key const * const ky = sec->findKey("clusterName"); + if (ky) + { + clusterName = ky->getAsString(ky->getCount()-1, ""); + } + } + + TaskConnectionIdMessage id(TaskConnectionIdMessage::Planet, "", clusterName); + send(id, true); + static MessageConnectionCallback m("TaskConnectionOpened"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void TaskConnection::onReceive(const Archive::ByteStream & message) +{ + Archive::ReadIterator r(message); + GameNetworkMessage m(r); + if(m.isType("TaskUtilization")) + { + // generate utilization messages for the task manager + TaskUtilization sysCpu(static_cast(TaskUtilization::SYSTEM_CPU), 0.5f); + TaskUtilization sysMem(static_cast(TaskUtilization::SYSTEM_MEMORY), 0.5f); + TaskUtilization sysNet(static_cast(TaskUtilization::SYSTEM_NETWORK_IO), 50000.0f); + TaskUtilization procCpu(static_cast(TaskUtilization::PROCESS_CPU), 0.5f); + TaskUtilization procMem(static_cast(TaskUtilization::PROCESS_MEMORY), 0.5f); + TaskUtilization procNet(static_cast(TaskUtilization::PROCESS_NETWORK_IO), 50000.0f); + + send(sysCpu, true); + send(sysMem, true); + send(sysNet, true); + send(procCpu, true); + send(procMem, true); + send(procNet, true); + } +} + +//----------------------------------------------------------------------- diff --git a/engine/server/application/PlanetServer/src/shared/TaskConnection.h b/engine/server/application/PlanetServer/src/shared/TaskConnection.h new file mode 100644 index 00000000..1c20e1df --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/TaskConnection.h @@ -0,0 +1,39 @@ +// TaskConnection.h +// copyright 2001 Verant Interactive +// Author: Justin Randall + +#ifndef _TaskConnection_H +#define _TaskConnection_H + +//----------------------------------------------------------------------- + +class TaskCommandChannel; + +//----------------------------------------------------------------------- + +#include "serverUtility/ServerConnection.h" + +//----------------------------------------------------------------------- + +class TaskConnection : public ServerConnection +{ +public: + TaskConnection(const std::string & remoteAddress, const unsigned short remotePort); + TaskConnection(UdpConnectionMT * u, TcpClient *); + ~TaskConnection(); + + void onConnectionClosed(); + void onConnectionOpened(); + void onReceive(const Archive::ByteStream & message); + +private: + TaskConnection(const TaskConnection&); + TaskConnection& operator= (const TaskConnection&); + TaskConnection(); +}; + +//----------------------------------------------------------------------- + +#endif // _TaskConnection_H + + diff --git a/engine/server/application/PlanetServer/src/shared/WatcherConnection.cpp b/engine/server/application/PlanetServer/src/shared/WatcherConnection.cpp new file mode 100644 index 00000000..363b4741 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/WatcherConnection.cpp @@ -0,0 +1,110 @@ +// ====================================================================== +// +// WatcherConnection.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" +#include "WatcherConnection.h" + +#include "ConfigPlanetServer.h" +#include "sharedNetworkMessages/PlanetNodeStatusMessage.h" +#include "sharedNetworkMessages/PlanetObjectStatusMessage.h" + +//----------------------------------------------------------------------- + +WatcherConnection::WatcherConnection(UdpConnectionMT * u, TcpClient * t) : + ServerConnection(u, t), + m_objectData(new ObjectDataList), + m_nodeData(new NodeDataList) +{ +} + +//----------------------------------------------------------------------- + +WatcherConnection::~WatcherConnection() +{ + delete m_objectData; + delete m_nodeData; + m_objectData = 0; + m_nodeData = 0; + +} //lint !e1740 // thinks we didn't delete m_objectData and m_nodeData, but clearly we did + +//----------------------------------------------------------------------- + +void WatcherConnection::onConnectionClosed() +{ + DEBUG_REPORT_LOG(true,("WatcherConnection closed.\n")); + ServerConnection::onConnectionClosed(); + + static MessageConnectionCallback m("WatcherConnectionClosed"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void WatcherConnection::onConnectionOpened() +{ + DEBUG_REPORT_LOG(true,("WatcherConnection opened.\n")); + ServerConnection::onConnectionOpened(); + + static MessageConnectionCallback m("WatcherConnectionOpened"); + emitMessage(m); + setOverflowLimit(ConfigPlanetServer::getWatcherOverflowLimit()); +} + +// ---------------------------------------------------------------------- + +void WatcherConnection::addObjectUpdate(const NetworkId &objectId, int x, int z, uint32 authoritativeServer, int interestRadius, bool deleteObject, int objectTypeTag, int const level, bool const hibernating, uint32 const templateCrc, int const aiActivity, int const creationType) +{ + NOT_NULL(m_objectData); + m_objectData->push_back(PlanetObjectStatusMessageData(objectId, x,z, authoritativeServer, interestRadius, static_cast(deleteObject), objectTypeTag, level, hibernating, templateCrc, aiActivity, creationType)); + + if (static_cast(m_objectData->size()) > ConfigPlanetServer::getMaxWatcherUpdatesPerMessage()) + { + flushQueuedObjectData(); + } +} + +//----------------------------------------------------------------------- + +void WatcherConnection::flushQueuedObjectData() +{ + NOT_NULL(m_objectData); + if (m_objectData->size() != 0) + { + PlanetObjectStatusMessage msg(*m_objectData); + send(msg,true); + m_objectData->clear(); + } +} + +// ---------------------------------------------------------------------- + +void WatcherConnection::addNodeUpdate(int x, int z, bool loaded, const stdvector::fwd &servers, const stdvector::fwd &subscriptionCounts) +{ + NOT_NULL(m_nodeData); + m_nodeData->push_back(PlanetNodeStatusMessageData(x,z,loaded,servers,subscriptionCounts)); + + if (static_cast(m_objectData->size()) > ConfigPlanetServer::getMaxWatcherUpdatesPerMessage()) + { + flushQueuedNodeData(); + } +} + +// ---------------------------------------------------------------------- + +void WatcherConnection::flushQueuedNodeData() +{ + NOT_NULL(m_nodeData); + if (m_nodeData->size() != 0) + { + PlanetNodeStatusMessage msg(*m_nodeData); + send(msg,true); + m_nodeData->clear(); + } +} + +// ---------------------------------------------------------------------- diff --git a/engine/server/application/PlanetServer/src/shared/WatcherConnection.h b/engine/server/application/PlanetServer/src/shared/WatcherConnection.h new file mode 100644 index 00000000..6fbf6e01 --- /dev/null +++ b/engine/server/application/PlanetServer/src/shared/WatcherConnection.h @@ -0,0 +1,59 @@ +// ====================================================================== +// +// WatcherConnection.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_WatcherConnection_H +#define INCLUDED_WatcherConnection_H + +// ====================================================================== + +#include "serverUtility/ServerConnection.h" + +class PlanetObjectStatusMessageData; +class PlanetNodeStatusMessageData; + +// ====================================================================== + +class WatcherConnection : public ServerConnection +{ + public: + WatcherConnection(UdpConnectionMT *, TcpClient *); + ~WatcherConnection(); + void onConnectionClosed (); + void onConnectionOpened (); + + void addObjectUpdate (const NetworkId &objectId, int x, int z, uint32 authoritativeServer, int interestRadius, bool deleteObject, int objectTypeTag, int level, bool hibernating, uint32 templateCrc, int aiActivity, int creationType); + void addNodeUpdate (int x, int z, bool loaded, const stdvector::fwd &servers, const stdvector::fwd &subscriptionCounts); + void flushQueuedData (); + + private: + void flushQueuedObjectData(); + void flushQueuedNodeData(); + + private: + typedef stdvector::fwd ObjectDataList; + ObjectDataList *m_objectData; + + typedef stdvector::fwd NodeDataList; + NodeDataList *m_nodeData; + + private: + WatcherConnection (const WatcherConnection&); + WatcherConnection& operator= (const WatcherConnection&); + WatcherConnection(); +}; + +// ---------------------------------------------------------------------- + +inline void WatcherConnection::flushQueuedData() +{ + flushQueuedObjectData(); + flushQueuedNodeData(); +} + +// ====================================================================== + +#endif diff --git a/engine/server/application/PlanetServer/src/win32/FirstPlanetServer.cpp b/engine/server/application/PlanetServer/src/win32/FirstPlanetServer.cpp new file mode 100644 index 00000000..97e05471 --- /dev/null +++ b/engine/server/application/PlanetServer/src/win32/FirstPlanetServer.cpp @@ -0,0 +1,8 @@ +// ====================================================================== +// +// FirstPlanetServer.cpp +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#include "FirstPlanetServer.h" diff --git a/engine/server/application/PlanetServer/src/win32/WinMain.cpp b/engine/server/application/PlanetServer/src/win32/WinMain.cpp new file mode 100644 index 00000000..f153fee1 --- /dev/null +++ b/engine/server/application/PlanetServer/src/win32/WinMain.cpp @@ -0,0 +1,67 @@ +#include "FirstPlanetServer.h" +#include "ConfigPlanetServer.h" +#include "PlanetServer.h" + +#include "sharedCompression/SetupSharedCompression.h" +#include "sharedDebug/SetupSharedDebug.h" +#include "sharedFile/SetupSharedFile.h" +#include "sharedFoundation/Os.h" +#include "sharedFoundation/SetupSharedFoundation.h" +#include "sharedLog/SetupSharedLog.h" +#include "sharedNetwork/SetupSharedNetwork.h" +#include "sharedNetworkMessages/SetupSharedNetworkMessages.h" +#include "sharedRandom/SetupSharedRandom.h" +#include "sharedThread/SetupSharedThread.h" +#include "sharedUtility/SetupSharedUtility.h" + +#include + +//_____________________________________________________________________ +int main(int argc, char ** argv) +{ + SetupSharedThread::install(); + SetupSharedDebug::install(1024); + + //-- setup foundation + SetupSharedFoundation::Data setupFoundationData(SetupSharedFoundation::Data::D_game); + + setupFoundationData.argc = argc; + setupFoundationData.argv = argv; + setupFoundationData.createWindow = false; + setupFoundationData.clockUsesSleep = true; + + SetupSharedFoundation::install (setupFoundationData); + + SetupSharedCompression::install(); + SetupSharedFile::install(false); + + SetupSharedNetwork::SetupData networkSetupData; + SetupSharedNetwork::getDefaultServerSetupData(networkSetupData); + SetupSharedNetwork::install(networkSetupData); + + SetupSharedNetworkMessages::install(); + + SetupSharedRandom::install(int(time(NULL))); + + SetupSharedUtility::Data sharedUtilityData; + SetupSharedUtility::setupGameData (sharedUtilityData); + SetupSharedUtility::install (sharedUtilityData); + + //-- setup server + ConfigPlanetServer::install (); + + char tmp[92]; + sprintf(tmp, "PlanetServer:%d", Os::getProcessId()); + SetupSharedLog::install(tmp); + + //-- run server + SetupSharedFoundation::callbackWithExceptionHandling(PlanetServer::run); + + SetupSharedLog::remove(); + SetupSharedFoundation::remove(); + SetupSharedThread::remove(); + + return 0; +} + +//_____________________________________________________________________