1524 lines
57 KiB
C++
1524 lines
57 KiB
C++
// ======================================================================
|
|
//
|
|
// 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 <cstdio>
|
|
#include <list>
|
|
#include <set>
|
|
|
|
// ======================================================================
|
|
|
|
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<RequestChunkMessage::Chunk> m_pendingChunkRequests;
|
|
std::vector<std::pair<uint32, uint32> > m_pendingPreloadRequestCompletes;
|
|
}
|
|
using namespace PlanetServerNamespace;
|
|
|
|
// ======================================================================
|
|
|
|
PlanetServer::PlanetServer() :
|
|
Singleton<PlanetServer>(),
|
|
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<PreloadServerId, GameServerSpawnDelaySeconds>()),
|
|
m_startingGameServers(new std::map<int, std::pair<std::string, time_t> >()),
|
|
m_firstGameServer(0),
|
|
m_tutorialMode(false),
|
|
m_spaceMode(false),
|
|
m_messagesWaitingForGameServer(),
|
|
m_metricsData(0),
|
|
m_taskManagerConnection(NULL),
|
|
m_sceneTransferChunkLoads(new std::list<const RequestSceneTransfer*>),
|
|
m_pendingCharacterSaves(new std::map<NetworkId, uint32>),
|
|
m_watchers(),
|
|
m_watcherIsPresent(false),
|
|
m_queuedMessages(),
|
|
m_characterFindMap(new std::map<NetworkId, CharacterFindInfo>),
|
|
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<GameServerConnection>(), 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<WatcherConnection>(), 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<PreloadManager*>(0));
|
|
Node::install();
|
|
Scene::install(static_cast<Scene*>(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<const GameNetworkMessage *>::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<float>(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<std::pair<uint32, uint32> >::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<ServerConnection const *>(&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<GameServerConnection *>(dynamic_cast<const GameServerConnection*>(&source));
|
|
std::set<GameServerConnection *>::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<PreloadServerId> 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<WatcherConnection *>::iterator w;
|
|
std::vector<WatcherConnection *>::iterator begin = m_watchers.begin();
|
|
std::vector<WatcherConnection *>::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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GameGameServerConnect msg(ri);
|
|
GameServerConnection * conn = const_cast<GameServerConnection*>(safe_cast<const GameServerConnection *>(&source));
|
|
|
|
std::map<int, std::pair<std::string, time_t> >::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<WatcherConnection *>::iterator i;
|
|
std::vector<WatcherConnection *>::iterator begin = m_watchers.begin();
|
|
std::vector<WatcherConnection *>::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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
const GameServerReadyMessage msg(ri);
|
|
|
|
GameServerConnection * conn = const_cast<GameServerConnection*>(dynamic_cast<const GameServerConnection *>(&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<GameServerConnection*>(dynamic_cast<const GameServerConnection *>(&source));
|
|
if (conn)
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(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<PreloadServerId>(conn->getPreloadNumber())))
|
|
{
|
|
m_pendingGameServerDisconnects.insert(conn);
|
|
conn->disconnect();
|
|
}
|
|
}
|
|
flushWaitingMessages();
|
|
}
|
|
else if (message.isType("PreloadRequestCompleteMessage"))
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(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<const GameNetworkMessage &>(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<const GameNetworkMessage &>(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<GameServerConnection*>(dynamic_cast<const GameServerConnection *>(&source));
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ChunkCompleteMessage msg(ri);
|
|
handleChunkComplete(msg, conn);
|
|
}
|
|
else if (message.isType("WatcherConnectionOpened"))
|
|
{
|
|
WatcherConnection *newConn = const_cast<WatcherConnection*>(dynamic_cast<const WatcherConnection*>(&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<int>(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<PreloadServerId> startGameServerList;
|
|
startGameServer(startGameServerList, 0); // flush any pending game server start requests
|
|
}
|
|
else if(message.isType("ProfilerOperationMessage"))
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(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<const GameNetworkMessage &>(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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
UnloadedPlayerMessage msg(ri);
|
|
|
|
GameServerConnection *gameServer=const_cast<GameServerConnection *>(dynamic_cast<const GameServerConnection*>(&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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
PersistedPlayerMessage msg(ri);
|
|
|
|
m_pendingCharacterSaves->erase(msg.getPlayerId());
|
|
}
|
|
else if (message.isType("ExcommunicateGameServerMessage"))
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ri = static_cast<const GameNetworkMessage &>(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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ri = static_cast<const GameNetworkMessage &>(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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<std::pair<std::pair<std::string, std::vector<int8> >, std::pair<float, bool> >, std::pair<Vector, float> > > 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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<NetworkId, NetworkId> > msg(ri);
|
|
|
|
bool result=Scene::getInstance().requestSameServer(msg.getValue().first, msg.getValue().second);
|
|
|
|
GameServerConnection * const gameServer = const_cast<GameServerConnection *>(dynamic_cast<const GameServerConnection*>(&source));
|
|
|
|
if (gameServer)
|
|
{
|
|
GenericValueTypeMessage<bool> const rssReply("RequestSameServerReply", result);
|
|
gameServer->send(rssReply, true);
|
|
}
|
|
}
|
|
else if (message.isType("RequestAuthTransfer"))
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<NetworkId, uint32> > msg(ri);
|
|
|
|
bool result=Scene::getInstance().requestAuthTransfer(msg.getValue().first, msg.getValue().second);
|
|
|
|
GameServerConnection * const gameServer = const_cast<GameServerConnection *>(dynamic_cast<const GameServerConnection*>(&source));
|
|
|
|
if (gameServer)
|
|
{
|
|
GenericValueTypeMessage<bool> const ratReply("RequestAuthTransferReply", result);
|
|
gameServer->send(ratReply, true);
|
|
}
|
|
}
|
|
else if (message.isType("GameServerForceChangeAuthorityMessage"))
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ri = static_cast<const GameNetworkMessage &>(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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<std::pair<NetworkId, unsigned int>, bool> > msg(ri);
|
|
GameServerConnection const *gameServer = dynamic_cast<const GameServerConnection*>(&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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ri = static_cast<const GameNetworkMessage &>(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<int, std::pair<std::string, time_t> >::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<PreloadServerId>(atoi(preloadServerIdStr.c_str()));
|
|
|
|
std::set<PreloadServerId> 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<GameNetworkMessage const &> (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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<int> msg(ri);
|
|
handleDatabaseSaveComplete(msg.getValue());
|
|
}
|
|
else if (message.isType("LocateStructureMessage"))
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<NetworkId, uint32> > 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<const GameNetworkMessage &>(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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<uint8> 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<int, std::pair<std::string, time_t> >::iterator iter = m_startingGameServers->begin(); iter != m_startingGameServers->end(); ++iter)
|
|
{
|
|
if ((iter->second.second + static_cast<time_t>(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<unsigned int>(timeNow - iter->second.second)).c_str(), CalendarTime::convertSecondsToMS(static_cast<unsigned int>(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<unsigned int>(timeNow - iter->second.second)).c_str(), iter->second.first.c_str(), CalendarTime::convertSecondsToMS(static_cast<unsigned int>(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<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<std::string, uint32> > 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<NetworkId, uint32>::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<int>(m_gameServers.size());
|
|
cfi.forCtsSourceCharacter = forCtsSourceCharacter;
|
|
|
|
// message all game servers for this planet asking if they have this auth character
|
|
GenericValueTypeMessage<std::pair<NetworkId, unsigned int> > 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<NetworkId, CharacterFindInfo>::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<int>(coords.x), static_cast<int>(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<int>(coords.x), static_cast<int>(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<int>(coords.x), static_cast<int>(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<std::pair<int, int> > const & chunks = msg.getChunks();
|
|
for (std::vector<std::pair<int, int> >::const_iterator chunk=chunks.begin(); chunk!=chunks.end(); ++chunk)
|
|
{
|
|
int nodeX = chunk->first;
|
|
int nodeZ = chunk->second;
|
|
|
|
std::list<const RequestSceneTransfer*>::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<int>(coords.x)) == nodeX
|
|
&& Node::roundToNode(static_cast<int>(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> & preloadServerId, const GameServerSpawnDelaySeconds spawnDelay)
|
|
{
|
|
if(m_taskManagerConnection && m_centralServerConnection)
|
|
{
|
|
for (std::set<PreloadServerId>::const_iterator it = preloadServerId.begin(); it != preloadServerId.end(); ++it)
|
|
(*m_pendingServerStarts)[*it] = spawnDelay;
|
|
|
|
for (std::map<PreloadServerId, GameServerSpawnDelaySeconds>::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<PreloadServerId>::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<const GameNetworkMessage *>::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<const RequestSceneTransfer *>(msg);
|
|
if (sceneTransfer)
|
|
handleRequestSceneTransfer(sceneTransfer);
|
|
else
|
|
{
|
|
const RequestGameServerForLoginMessage * loginMessage = dynamic_cast<const RequestGameServerForLoginMessage *>(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<uint32, GameServerData> &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<GameNetworkMessage *> & 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<GameNetworkMessage *> & v = i->second;
|
|
for (std::vector<GameNetworkMessage *>::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<int>(Os::getProcessId()),
|
|
reason));
|
|
|
|
REPORT_LOG(
|
|
true,
|
|
(
|
|
"PlanetServer (pid %d) shutdown, reason: %s\n",
|
|
static_cast<int>(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());
|
|
}
|
|
|
|
// ======================================================================
|