Files
src/engine/server/application/CentralServer/src/shared/CentralServer.cpp
T

4166 lines
159 KiB
C++
Executable File

// CentralServer.cpp
// copyright 2000 Verant Interactive
// Author: Justin Randall
//-----------------------------------------------------------------------
#include "FirstCentralServer.h"
#include "CentralServer.h"
#include "CentralCSHandler.h"
#include "CentralServerMetricsData.h"
#include "CharacterCreationTracker.h"
#include "ChatServerConnection.h"
#include "ConfigCentralServer.h"
#include "ConnectionServerConnection.h"
#include "ConsoleConnection.h"
#include "ConsoleManager.h"
#include "CustomerServiceConnection.h"
#include "fileInterface/StdioFile.h"
#include "GameServerConnection.h"
#include "LoginServerConnection.h"
#include "PlanetManager.h"
#include "PlanetServerConnection.h"
#include "TaskConnection.h"
#include "UnicodeUtils.h"
#include "UniverseManager.h"
#include "serverKeyShare/KeyShare.h"
#include "serverMetrics/MetricsManager.h"
#include "serverNetworkMessages/AccountFeatureIdResponse.h"
#include "serverNetworkMessages/AddCharacterMessage.h"
#include "serverNetworkMessages/AddObjectIdBlockMessage.h"
#include "serverNetworkMessages/AdjustAccountFeatureIdResponse.h"
#include "serverNetworkMessages/CentralConnectionServerMessages.h"
#include "serverNetworkMessages/CentralGameServerMessages.h"
#include "serverNetworkMessages/CentralPingMessage.h"
#include "serverNetworkMessages/CentralPlanetServerConnect.h"
#include "serverNetworkMessages/CentralTaskMessages.h"
#include "serverNetworkMessages/CharacterListMessage.h"
#include "serverNetworkMessages/ChatServerOnline.h"
#include "serverNetworkMessages/ChunkObjectListMessage.h"
#include "serverNetworkMessages/ClaimRewardsMessage.h"
#include "serverNetworkMessages/ClaimRewardsReplyMessage.h"
#include "serverNetworkMessages/ConnectionServerDown.h"
#include "serverNetworkMessages/CreateNewObjectMessage.h"
#include "serverNetworkMessages/EnumerateServers.h"
#include "serverNetworkMessages/ExchangeListCreditsMessage.h"
#include "serverNetworkMessages/ExcommunicateGameServerMessage.h"
#include "serverNetworkMessages/FailedToLoadObjectMessage.h"
#include "serverNetworkMessages/FeatureIdTransactionResponse.h"
#include "serverNetworkMessages/ForceUnloadObjectMessage.h"
#include "serverNetworkMessages/GameServerForLoginMessage.h"
#include "serverNetworkMessages/GameServerReadyMessage.h"
#include "serverNetworkMessages/GameServerUniverseLoadedMessage.h"
#include "serverNetworkMessages/GameTaskManagerMessages.h"
#include "serverNetworkMessages/LoadObjectMessage.h"
#include "serverNetworkMessages/LocateStructureMessage.h"
#include "serverNetworkMessages/LoginConnectionServerAddress.h"
#include "serverNetworkMessages/LoginCreateCharacterAckMessage.h"
#include "serverNetworkMessages/LoginCreateCharacterMessage.h"
#include "serverNetworkMessages/LoginKeyPush.h"
#include "serverNetworkMessages/LoginRestoreCharacterMessage.h"
#include "serverNetworkMessages/LoginUpgradeAccountMessage.h"
#include "serverNetworkMessages/MessageToAckMessage.h"
#include "serverNetworkMessages/MessageToMessage.h"
#include "serverNetworkMessages/PlanetLoadCharacterMessage.h"
#include "serverNetworkMessages/PopulationListMessage.h"
#include "serverNetworkMessages/PreloadFinishedMessage.h"
#include "serverNetworkMessages/PreloadRequestCompleteMessage.h"
#include "serverNetworkMessages/ProfilerOperationMessage.h"
#include "serverNetworkMessages/RandomName.h"
#include "serverNetworkMessages/ReleaseAuthoritativeMessage.h"
#include "serverNetworkMessages/RenameCharacterMessage.h"
#include "serverNetworkMessages/RequestChunkMessage.h"
#include "serverNetworkMessages/RequestGameServerForLoginMessage.h"
#include "serverNetworkMessages/RequestObjectIdsMessage.h"
#include "serverNetworkMessages/RequestObjectMessage.h"
#include "serverNetworkMessages/RestartServerMessage.h"
#include "serverNetworkMessages/SceneTransferMessages.h"
#include "serverNetworkMessages/ServerDeleteCharacterMessage.h"
#include "serverNetworkMessages/SetConnectionServerPublic.h"
#include "serverNetworkMessages/SetObjectPositionMessage.h"
#include "serverNetworkMessages/TaskProcessDiedMessage.h"
#include "serverNetworkMessages/TaskSpawnProcess.h"
#include "serverNetworkMessages/TransferCharacterData.h"
#include "serverNetworkMessages/TransferCharacterDataArchive.h"
#include "serverNetworkMessages/UnloadObjectMessage.h"
#include "serverNetworkMessages/UpdateLoginConnectionServerStatus.h"
#include "serverNetworkMessages/UpdatePlayerCountMessage.h"
#include "serverNetworkMessages/ValidateAccountMessage.h"
#include "serverNetworkMessages/ValidateAccountReplyMessage.h"
#include "serverNetworkMessages/ValidateCharacterForLoginMessage.h"
#include "serverNetworkMessages/ValidateCharacterForLoginReplyMessage.h"
#include "serverNetworkMessages/VerifyAndLockName.h"
#include "sharedNetworkMessages/ConsoleChannelMessages.h"
#include "serverUtility/ClusterWideDataManagerList.h"
#include "serverUtility/ServerClock.h"
#include "serverUtility/SetupServerUtility.h"
#include "sharedDebug/Profiler.h"
#include "sharedFile/Iff.h"
#include "sharedFoundation/ApplicationVersion.h"
#include "sharedFoundation/CalendarTime.h"
#include "sharedFoundation/Clock.h"
#include "sharedFoundation/ConfigFile.h"
#include "sharedFoundation/FormattedString.h"
#include "sharedFoundation/Os.h"
#include "sharedFoundation/Timer.h"
#include "sharedLog/Log.h"
#include "sharedLog/SetupSharedLog.h"
#include "sharedMathArchive/VectorArchive.h"
#include "sharedNetwork/Connection.h"
#include "sharedNetwork/NetworkSetupData.h"
#include "sharedNetwork/Service.h"
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
#include "sharedNetworkMessages/NameErrors.h"
#include "sharedNetworkMessages/NetworkStringIds.h"
#include "sharedUtility/SetupSharedUtility.h"
#include "sharedUtility/StartingLocationData.h"
#include "sharedUtility/StartingLocationManager.h"
#include "TransferServerConnection.h"
#include "StationPlayersCollectorConnection.h"
#include "AuctionTransferClient.h"
#include "unicodeArchive/UnicodeArchive.h"
#include <stdio.h>
#include "sharedFoundation/CrcConstexpr.hpp"
#include "webAPI.h"
using namespace StellaBellum;
namespace CentralServerNamespace
{
bool gs_connectionServersPublic=false;
bool gs_clusterIsLocked=false;
bool gs_clusterIsSecret=false;
std::map<std::string, std::string> ms_sceneToHostMap;
// every connection server started on the cluster will
// have a entry in this vector which contains the node
// on which to start that particular connection server;
// every connection server that is started will be assigned
// a 1-based number, which corresponds to its position
// in this vector; that is, the connection server assigned
// number 1 will be at position 0 in this vector
std::vector<std::string> s_connectionServerHostList;
std::pair<std::string, unsigned short> s_customerServiceServerChatServerServiceAddress;
std::pair<std::string, unsigned short> s_customerServiceServerGameServerServiceAddress;
Service * s_commodityServerService = 0;
bool s_retryTransferServerConnection = false;
bool s_retryStationPlayersCollectorConnection = false;
std::map<StationId, uint32> m_purgeAccountToLoginServerMap;
std::map<NetworkId, std::pair<time_t, int> > s_pendingRenameCharacter;
}
using namespace CentralServerNamespace;
// ======================================================================
CentralServer::CentralObject::CentralObject() :
m_sceneId(),
m_authoritativeProcess(0),
m_proxyProcessList()
{
}
//-----------------------------------------------------------------------
CentralServer::CentralObject::CentralObject(const CentralServer::SceneId & sceneId, uint32 authProcess) :
m_sceneId(sceneId),
m_authoritativeProcess(authProcess),
m_proxyProcessList()
{
}
//-----------------------------------------------------------------------
CentralServer::CentralServer() :
Singleton<CentralServer>(),
MessageDispatch::Receiver(),
m_connectionServerConnections(),
m_chatServerConnections(),
m_csServerConnections(),
m_gameServers(),
m_accountConnectionMap(),
m_dbProcessServerProcessId(0),
m_done(false),
m_gameService(0),
m_chatService(0),
m_connService(0),
m_csService(0),
m_planetService(0),
m_consoleService(0),
m_transferServerConnection(0),
m_stationPlayersCollectorConnection(0),
m_gameServerConnections(),
m_gameServerConnectionsList(),
m_playerSceneMap(),
m_loginServerConnectionMap(),
m_loginServerKeys(0),
m_loginService(0),
m_pAuctionTransferClient(0),
m_messagesWaitingForPlanetServer(),
m_pendingLoadingObjects(),
m_pendingNewObjects(),
m_nextFreeProcessId(0),
m_taskManager(0),
m_taskService(0),
m_commandLine(),
m_pendingTransfers(),
m_totalPlayerCount(0),
m_totalFreeTrialCount(0),
m_totalEmptySceneCount(0),
m_totalTutorialSceneCount(0),
m_totalFalconSceneCount(0),
m_metricsData(0),
m_pendingPlanetServers(),
m_planetsWaitingForPreload(),
m_planetServers(),
m_nextPlanetWatcherPort(ConfigCentralServer::getFirstPlanetWatcherPort()),
m_databaseBacklogged(false),
m_lastLoadingStateTime(0),
m_timeClusterStarted(time(0)),
m_clusterStartupTime(-1),
m_timeClusterWentIntoLoadingState(time(0)),
m_clusterId(0),
m_serverPings(),
m_shutdownTotalTime(0),
m_shutdownMaxTime(0),
m_shutdownSystemMessage(Unicode::narrowToWide("")),
m_shutdownPhase(0),
m_shutdownHaveDatabaseSaveStart(false),
m_shutdownHaveDatabaseComplete(false),
m_curTime(0),
m_lastTimeSystemTimeMismatchNotification(0),
m_lastTimeSystemTimeMismatchNotificationDescription(),
m_disconnectedTaskManagerList(),
m_populationStatistics(),
m_timePopulationStatisticsRefresh(0),
m_timePopulationStatisticsNextRefresh(0),
m_gcwScoreStatistics(),
m_timeGcwScoreStatisticsRefresh(0),
m_timeGcwScoreStatisticsNextRefresh(0),
m_lastLoginTimeStatistics(),
m_createTimeStatistics(),
m_timeLastLoginTimeStatisticsRefresh(0),
m_timeLastLoginTimeStatisticsNextRefresh(0),
m_numberOfCharacterMatchRequests(0),
m_numberOfCharacterMatchResults(0),
m_timeSpentOnCharacterMatchRequestsMs(0),
m_timeCharacterMatchStatisticsNextRefresh(0)
{
m_curTime = static_cast<uint32>(time(0));
m_loginServerKeys = new KeyServer(20);
connectToMessage("LoginKeyPush");
//Connection Messages
connectToMessage("ClientConnectionClosed"); // from connection server
connectToMessage("ConnectionOpened");
connectToMessage("RequestClusterShutdown");
connectToMessage("GameConnectionClosed");
connectToMessage("CentralGameServerConnect");
connectToMessage("CentralGameServerDbProcessServerProcessId");
connectToMessage("LoginConnectionOpened");
connectToMessage("LoginConnectionClosed");
connectToMessage("ClusterId");
connectToMessage("GameServerReadyMessage");
connectToMessage("GameServerUniverseLoadedMessage");
connectToMessage("ConnectionServerConnectionClosed");
connectToMessage("ConnectionServerConnectionOpened");
connectToMessage("TaskConnectionClosed");
connectToMessage("TaskConnectionOpened");
connectToMessage("RandomNameRequest"); // from connection server
connectToMessage("RandomNameResponse"); // from game server
connectToMessage("VerifyAndLockNameRequest"); // from connection server
connectToMessage("VerifyAndLockNameResponse"); // from game server
//Object Messages
connectToMessage("RequestObjectMessage");
connectToMessage("RequestChunkMessage");
connectToMessage("ChunkObjectListMessage");
connectToMessage("CreateNewObjectMessage");
connectToMessage("SetObjectPositionMessage");
connectToMessage("FailedToLoadObjectMessage");
connectToMessage("ReleaseAuthoritativeMessage");
connectToMessage("ForceUnloadObjectMessage");
connectToMessage("RequestUnloadObjectMessage");
connectToMessage("MessageToMessage");
connectToMessage("MessageToAckMessage");
connectToMessage("LocateStructureMessage");
//Create character messages
connectToMessage("ConnectionCreateCharacter");
connectToMessage("DatabaseCreateCharacterSuccess");
connectToMessage("GameCreateCharacterFailed");
connectToMessage("RequestGameServerForLoginMessage");
connectToMessage("GameServerForLoginMessage");
connectToMessage("LoginCreateCharacterAckMessage");
connectToMessage("LoginUpgradeAccountMessage");
connectToMessage("LoginRestoreCharacterMessage");
connectToMessage("NewCharacterCreated");
connectToMessage("ServerDeleteCharacterMessage");
connectToMessage("ValidateCharacterForLoginMessage");
connectToMessage("ValidateCharacterForLoginReplyMessage");
connectToMessage("EnableCharacterMessage");
connectToMessage("EnableCharacterReplyMessage");
connectToMessage("DisableCharacterMessage");
connectToMessage("DisableCharacterReplyMessage");
connectToMessage("RequestOIDsMessage");
connectToMessage("AddOIDBlockMessage");
connectToMessage("CharacterListMessage");
connectToMessage("LoggedInMessage");
connectToMessage("CentralPlanetServerConnect");
connectToMessage("PlanetCreateProxy");
connectToMessage("PlanetRemoveProxy");
connectToMessage("RequestSceneTransfer");
connectToMessage("SceneTransferMessage");
connectToMessage("PlanetLoadCharacterMessage");
connectToMessage("ConnSrvDropDupeConns");
connectToMessage("CntrlSrvDropDupeConns");
connectToMessage("PlayerDroppedFromGameServerCrash");
// Station exchange messages
connectToMessage("ExchangeListCreditsMessage");
// Chat Server messages
connectToMessage("ChatServerConnectionOpened");
connectToMessage("ChatServerConnectionClosed");
connectToMessage("ChatServerOnline");
// Chat Server messages
connectToMessage("CustomerServiceConnectionOpened");
connectToMessage("CustomerServiceConnectionClosed");
connectToMessage("ValidateAccountMessage");
connectToMessage("ValidateAccountReplyMessage");
connectToMessage("RequestPreloadListMessage");
connectToMessage("PreloadListMessage");
connectToMessage("PreloadRequestCompleteMessage");
connectToMessage("PreloadFinishedMessage");
connectToMessage("DatabaseBackloggedMessage");
connectToMessage("UpdatePlayerCountMessage");
connectToMessage("RenameCharacterMessageEx");
connectToMessage("PlayerRenameRequestSubmitted");
connectToMessage("PlayerRenameRequestCompleted");
connectToMessage("PopulationListMessage");
connectToMessage("SetConnectionServerPublic");
connectToMessage("ProfilerOperationMessage");
connectToMessage("CentralPingMessage");
connectToMessage("DatabaseSaveStart");
connectToMessage("DatabaseSaveComplete");
connectToMessage("ShutdownCluster");
connectToMessage("AbortShutdown");
connectToMessage("SetSceneForPlayer");
connectToMessage("DatabaseConsoleReplyMessage");
connectToMessage("TaskProcessDiedMessage");
connectToMessage("SystemTimeMismatchNotification");
connectToMessage("DisconnectedTaskManagerMessage");
connectToMessage("TransferServerConnectionClosed");
connectToMessage("PlanetRequestSave");
connectToMessage("TransferReplyLoginLocationData");
connectToMessage("CustomerServiceServerChatServerServiceAddress");
connectToMessage("CustomerServiceServerGameServerServiceAddress");
connectToMessage("ChatClosedConnectionWithGameServer");
connectToMessage("ReconnectToTransferServer");
connectToMessage("ClaimRewardsMessage");
connectToMessage("ClaimRewardsReplyMessage");
connectToMessage("PurgeStructuresForAccountMessage");
connectToMessage("WarnStructuresAboutPurgeMessage");
connectToMessage("PurgeCompleteMessage");
connectToMessage("RestartServerMessage");
connectToMessage("RestartServerByRoleMessage");
connectToMessage("ExcommunicateGameServerMessage");
connectToMessage("RestartPlanetMessage");
// Cluster state
connectToMessage("UpdateClusterLockedAndSecretState");
// Population statistics
connectToMessage("PopStatRsp");
connectToMessage("LLTStatRsp");
// GCW score statistics
connectToMessage("GcwScoreStatRsp");
// Character Search statistics
connectToMessage("LfgStatRsp");
// Account feature Id management
connectToMessage("AdjustAccountFeatureIdResponse");
connectToMessage("AccountFeatureIdResponse");
connectToMessage("FeatureIdTransactionResponse");
// Unlocked Slot management
connectToMessage("OccupyUnlockedSlotRsp");
connectToMessage("VacateUnlockedSlotRsp");
connectToMessage("SwapUnlockedSlotRsp");
// Cluster wide data manager
ClusterWideDataManagerList::install();
ClusterWideDataManagerList::registerMessage(*this);
ClusterWideDataManagerList::setLockTimeoutValue(ConfigCentralServer::getClusterWideDataLockTimeout());
CentralCSHandler::install();
gs_connectionServersPublic = ConfigCentralServer::getStartPublic();
m_metricsData = new CentralServerMetricsData;
MetricsManager::install(m_metricsData, false, "CentralServer", "", 0);
ConsoleManager::install();
CharacterCreationTracker::install();
IGNORE_RETURN(UniverseManager::getInstance()); //make sure it's constructed
}
//-----------------------------------------------------------------------
CentralServer::~CentralServer()
{
ConsoleManager::remove();
CentralCSHandler::remove();
ms_sceneToHostMap.clear();
// released during CentralServer::remove()
m_gameService = 0;
m_chatService = 0;
m_connService = 0;
m_csService = 0;
m_planetService = 0;
m_consoleService = 0;
m_transferServerConnection = 0;
m_stationPlayersCollectorConnection = 0;
m_loginServerKeys = 0; //lint !e423 : leak
m_loginService = 0;
m_taskManager = 0;
m_taskService = 0;
m_metricsData = 0; //lint !e423 : leak
}
//-----------------------------------------------------------------------
void CentralServer::addGameServer(GameServerConnection * newGameServer)
{
uint32 pid = newGameServer->getProcessId();
LOG("ServerStartup", ("Adding server %lu for scene %s", pid, newGameServer->getSceneId().c_str()));
IGNORE_RETURN(m_gameServers.insert(SceneGameMap::value_type(
newGameServer->getSceneId(), newGameServer)));
m_gameServerConnections[pid] = newGameServer;
if (newGameServer->getSceneId() != "database")
{
m_gameServerConnectionsList.push_back(newGameServer);
if (ConfigCentralServer::getGameServerConnectionPendingAllocatedSizeLimit() != 0)
newGameServer->setTcpClientPendingSendAllocatedSizeLimit(ConfigCentralServer::getGameServerConnectionPendingAllocatedSizeLimit());
PlanetManager::addGameServerForScene(newGameServer->getSceneId(), newGameServer);
// Have the new game server connect to the db and notify it of what game servers exist
std::vector<uint32> serverList;
GameServerConnection const * const databaseProcessConnection = CentralServer::getInstance().getGameServer(CentralServer::getInstance().getDbProcessServerProcessId());
FATAL(!databaseProcessConnection, ("Tried to add a game server without a database process connection?"));
for (std::map<uint32, GameServerConnection *>::iterator i = m_gameServerConnections.begin(); i != m_gameServerConnections.end(); ++i)
{
GameServerConnection const * const g = (*i).second;
if (g != databaseProcessConnection && g->getProcessId() != pid)
serverList.push_back(g->getProcessId());
}
GenericValueTypeMessage<std::pair<std::vector<uint32>, std::pair<uint32, std::pair<std::string, uint16> > > > const setupMessage(
"GameServerSetupMessage",
std::make_pair(
serverList,
std::make_pair(
databaseProcessConnection->getProcessId(),
std::make_pair(
databaseProcessConnection->getGameServiceAddress(),
databaseProcessConnection->getGameServicePort()))));
newGameServer->send(setupMessage, true);
}
}
//-----------------------------------------------------------------------
void CentralServer::broadcastToChatServers(const GameNetworkMessage & message) const
{
for (std::set<ChatServerConnection *>::const_iterator i = m_chatServerConnections.begin(); i != m_chatServerConnections.end(); ++i)
(*i)->send(message, true);
}
//-----------------------------------------------------------------------
void CentralServer::broadcastToCustomerServiceServers(const GameNetworkMessage & message) const
{
for (std::set<CustomerServiceConnection *>::const_iterator i = m_csServerConnections.begin(); i != m_csServerConnections.end(); ++i)
(*i)->send(message, true);
}
//-----------------------------------------------------------------------
void CentralServer::broadcastToGameServers(const GameNetworkMessage & message) const
{
for (SceneGameMap::const_iterator i = m_gameServers.begin(); i != m_gameServers.end(); ++i)
(*i).second->send(message, true);
}
//-----------------------------------------------------------------------
// /**
// * Clears the object process map for an object.
// *
// * @param objectId id of object we want to clear
// */
// void CentralServer::clearObjectMap(const NetworkId & objectId)
// {
// IGNORE_RETURN(m_centralObjectMap.erase(objectId));
// }
//-----------------------------------------------------------------------
bool CentralServer::getIsClusterPublic() const
{
return gs_connectionServersPublic;
}
//-----------------------------------------------------------------------
bool CentralServer::getIsClusterLocked() const
{
return gs_clusterIsLocked;
}
//-----------------------------------------------------------------------
bool CentralServer::getIsClusterSecret() const
{
return gs_clusterIsSecret;
}
//-----------------------------------------------------------------------
const std::string& CentralServer::getHostForScene(const std::string& scene) const
{
static std::string anyString = "any";
std::map<std::string, std::string>::iterator i = ms_sceneToHostMap.find(scene);
if (i != ms_sceneToHostMap.end())
{
return i->second;
}
return anyString;
}
// ----------------------------------------------------------------------
/**
* Get the list of game servers that are ready
*/
void CentralServer::getReadyGameServers(std::vector<uint32> &theList)
{
std::map<uint32, GameServerConnection *>::const_iterator i;
for (i = m_gameServerConnections.begin(); i!=m_gameServerConnections.end(); ++i)
{
if ((*i).second->getProcessId()!=getDbProcessServerProcessId() && i->second->getReady())
theList.push_back(i->second->getProcessId());
}
}
//-------------------------------------------------------------
GameServerConnection * CentralServer::getRandomGameServer(void)
{
if (m_gameServerConnectionsList.empty())
return nullptr;
// m_gameServerConnectionsList ***DOES NOT*** contain the DB server so
// we don't have to worry about checking for and excluding the DB server
size_t const listSize = m_gameServerConnectionsList.size();
size_t const indexChosenGameServer = (::rand() % listSize);
if (m_gameServerConnectionsList[indexChosenGameServer]->getReady())
return m_gameServerConnectionsList[indexChosenGameServer];
// randomly chosen game server is not ready, find next ready game server
size_t indexNextGameServer = (indexChosenGameServer + 1);
if (indexNextGameServer == listSize)
indexNextGameServer = 0;
while (indexNextGameServer != indexChosenGameServer)
{
if (m_gameServerConnectionsList[indexNextGameServer]->getReady())
return m_gameServerConnectionsList[indexNextGameServer];
++indexNextGameServer;
if (indexNextGameServer == listSize)
indexNextGameServer = 0;
}
return nullptr;
}
//-----------------------------------------------------------------------
/**
* Returns a game server that is handling a scene.
*
* @param scene the scene
*
* @return a game server connection for that scene
*/
GameServerConnection * CentralServer::getGameServer(const SceneId &scene) const
{
// @todo: we should pick a gameserver based on load
CentralServer::ServersList range = getGameServers(scene);
SceneGameMap::const_iterator iter = range.first;
while (iter != range.second)
{
if (iter->second->getReady())
return iter->second;
++iter;
}
return 0;
}
//-----------------------------------------------------------------------
/**
* Returns the game server for a given process id.
*
* @param processId the process id
*
* @return the game server connection
*/
GameServerConnection * CentralServer::getGameServer(const uint32 processId) const
{
GameServerConnection * result = 0;
std::map<uint32, GameServerConnection *>::const_iterator i = m_gameServerConnections.find(processId);
if(i != m_gameServerConnections.end())
result = (*i).second;
return result;
}
//-----------------------------------------------------------------------
/**
* Returns a list of game servers that are handling a scene.
*
* @param scene the scene
*
* @return an iterator pair for a range of GameServerConnections
*/
const CentralServer::ServersList CentralServer::getGameServers(const SceneId &scene) const
{
return m_gameServers.equal_range(scene); //lint !e64 //Type mismatch
}
//-----------------------------------------------------------------------
void CentralServer::pushAllKeys(ConnectionServerConnection * targetConnectionServer) const
{
DEBUG_FATAL(static_cast<int>(m_loginServerKeys->getKeyCount()) < 0, ("Invalid number of keys (uint overflow) in CentralServer.h"));
for(int i = static_cast<int>(m_loginServerKeys->getKeyCount()) - 1; i >= 0 ; i --)
{
ConnectionKeyPush pk(m_loginServerKeys->getKey(static_cast<uint32>(i)));
targetConnectionServer->send(pk, true);
}
}
//-----------------------------------------------------------------------
void CentralServer::launchStartingProcesses() const
{
static bool startingProcessesLaunched = false;
if (startingProcessesLaunched)
return;
// we can only launch starting processes once we have a connection to the
// TaskManager *AND* have received our cluster id from the LoginServer
if (!m_taskManager || !m_taskManager->isConnected() || (m_clusterId == 0))
{
REPORT_LOG(!m_taskManager, ("CentralServer not launching starting processes because m_taskManager is null\n"));
REPORT_LOG(!m_taskManager->isConnected(), ("CentralServer not launching starting processes because m_taskManager->isConnected() is false\n"));
REPORT_LOG((m_clusterId==0), ("CentralServer not launching starting processes because m_clusterId is 0 - not received from the LoginServer yet\n"));
return;
}
REPORT_LOG(true, ("CentralServer now launching starting processes because has a connection to the TaskManager *AND* has received cluster Id (%u) from the LoginServer\n", m_clusterId));
startingProcessesLaunched = true;
// spawn a connection server
launchStartingConnectionServers();
std::string options = "-s dbProcess centralServerAddress=";
if(getGameService())
{
options += getGameService()->getBindAddress();
}
else
{
options += NetworkHandler::getHostName();
}
TaskSpawnProcess pd(ConfigCentralServer::getDbServerHost(), "SwgDatabaseServer", options);
m_taskManager->send(pd, true);
options = "-s ChatServer centralServerAddress=";
if(getChatService())
{
options += getChatService()->getBindAddress();
}
else
{
options += NetworkHandler::getHostName();
}
options += " clusterName=";
options += ConfigCentralServer::getClusterName();
TaskSpawnProcess pc(ConfigCentralServer::getChatServerHost(), "ChatServer", options);
m_taskManager->send(pc, true);
options = "-s CustomerServiceServer centralServerAddress=";
if(getCustomerService())
{
options += getCustomerService()->getBindAddress();
}
else
{
options += NetworkHandler::getHostName();
}
options += " clusterName=";
options += ConfigCentralServer::getClusterName();
TaskSpawnProcess pcs("any", "CustomerServiceServer", options);
m_taskManager->send(pcs, true);
}
//-----------------------------------------------------------------------
void CentralServer::launchStartingConnectionServers() const
{
// if there are several planets listed explicitly in the config file/command line,
// spawn them
int i = 0;
// check for nesteds
int const numberOfStartingConnectionServers = ConfigCentralServer::getNumberOfStartingConnectionServers();
for (i = 0; i < numberOfStartingConnectionServers; ++i)
{
char const * const host = ConfigCentralServer::getStartingConnectionServer(i);
if (host)
{
char const * listenAddress = strchr(host,':');
DEBUG_FATAL(!listenAddress, ("Could not start up connection server because string %s has no listen address", host));
std::string hostString(host, static_cast<unsigned long>(listenAddress - host));
++listenAddress;
char const * publicPort = strchr(listenAddress,':');
DEBUG_FATAL(!publicPort, ("Could not start up connection server because string %s has no public port", host));
std::string listenString(listenAddress, static_cast<unsigned long>(publicPort - listenAddress));
++publicPort;
char const * privatePort = strchr(publicPort,':');
DEBUG_FATAL(!privatePort, ("Could not start up connection server because string %s has no private port", host));
std::string publicPortString(publicPort, static_cast<unsigned long>(privatePort - publicPort));
++privatePort;
std::string privatePortString(privatePort);
DEBUG_REPORT_LOG(true, ("Spawning Connection Server on %s listen:%s public:%s priv:%s\n", hostString.c_str(), listenString.c_str(), publicPortString.c_str(), privatePortString.c_str()));
// must update s_connectionServerHostList before calling startConnectionServer()
s_connectionServerHostList.push_back(hostString);
startConnectionServer(s_connectionServerHostList.size(), listenString, &publicPortString, &privatePortString, 0);
}
}
// if there were none specified, use defaults
if(i == 0)
{
// must update s_connectionServerHostList before calling startConnectionServer()
s_connectionServerHostList.push_back("node0");
startConnectionServer(s_connectionServerHostList.size(), NetworkHandler::getHostName(), 0, 0, 0);
}
}
//-----------------------------------------------------------------------
void CentralServer::launchStartingPlanetServers()
{
// if there are several planets listed explicitly in the config file/command line,
// spawn them
int i = 0;
int const numberOfStartPlanets = ConfigCentralServer::getNumberOfStartPlanets();
for (i = 0; i < numberOfStartPlanets ; ++i)
{
char const * const p = ConfigCentralServer::getStartPlanet(i);
if (p)
{
FATAL(!*p, ("CentralServer::launchStartingPlanetServers: ConfigCentralServer::getStartPlanet(%d) specified a non-nullptr but empty planet name", i));
std::string planetName;
std::string hostName;
char const * planet = p;
NOT_NULL(planet);
char const * host = strchr(planet,':');
if (host)
{
planetName = std::string(planet, static_cast<unsigned long>(host - planet) );
++host;
hostName = host;
}
else
{
planetName = planet;
}
startPlanetServer(hostName, planetName, 0);
ms_sceneToHostMap[planetName] = hostName;
}
}
// if there were no planets specified, get the configfile default and
// start that planet
if(i == 0)
{
startPlanetServer("any", ConfigCentralServer::getStartPlanet(), 0);
}
}
//-----------------------------------------------------------------------
void CentralServer::launchCommoditiesServer()
{
std::string options;
TaskSpawnProcess csp("any", "CommoditiesServer", options);
CentralServer::getInstance().sendTaskMessage(csp);
}
//-----------------------------------------------------------------------
void CentralServer::receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message)
{
const uint32 messageType = message.getType();
switch (messageType) {
case constcrc("LoginKeyPush") : {
// receiving another key
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const LoginKeyPush k(ri);
m_loginServerKeys->pushKey(k.getKey());
DEBUG_REPORT_LOG(true, ("Received session key.\n"));
ConnectionServerConnectionList::iterator i = m_connectionServerConnections.begin();
for (; i != m_connectionServerConnections.end(); ++i) {
(*i)->send(k, true);
}
break;
}
case constcrc("RequestClusterShutdown") : {
REPORT_LOG(true, ("An admin requested we shutdown the entire cluster\n"));
m_done = true;
break;
}
case constcrc("ClientConnectionClosed") : {
Archive::ReadIterator ri = static_cast<GameNetworkMessage const &>(message).getByteStream().begin();
GenericValueTypeMessage <StationId> const msg(ri);
ConnectionServerSUIDMap::iterator i = m_accountConnectionMap.find(msg.getValue());
if ((i != m_accountConnectionMap.end()) && (i->second == &source)) {
// Erase the entry
m_accountConnectionMap.erase(i);
}
break;
}
case constcrc("ConnectionOpened") : {
if (!dynamic_cast<const LoginServerConnection *>(&source)) {
ServerConnection * newServer = const_cast<ServerConnection *>(safe_cast < ServerConnection const * >
(&source));
uint32 pid = newServer->getProcessId();
CentralGameServerSetProcessId pidMessage(pid, ServerClock::getInstance()
.getSubtractInterval(), ConfigCentralServer::getClusterName());
newServer->send(pidMessage, true);
}
break;
}
case constcrc("ConnectionServerConnectionClosed") : {
DEBUG_REPORT_LOG(true, ("Handling connection server crash.\n"));
ConnectionServerConnection const *c = safe_cast < ConnectionServerConnection const * > (&source);
removeFromAccountConnectionMap(c->getId());
break;
}
case constcrc("GameConnectionClosed") : {
DEBUG_REPORT_LOG(true, ("Game server closed connection\n"));
GameServerConnection const *g = safe_cast < GameServerConnection const * > (&source);
removeGameServer(g);
break;
}
case constcrc("LoginConnectionOpened") : {
LoginServerConnection *l = const_cast<LoginServerConnection *>(safe_cast < LoginServerConnection const * >
(&source));
m_loginServerConnectionMap[l->getProcessId()] = l;
//Send connection server data
ConnectionServerConnectionList::iterator i = m_connectionServerConnections.begin();
for (; i != m_connectionServerConnections.end(); ++i) {
const ConnectionServerConnection *const csc = *i;
if ((csc->getClientServicePortPrivate() != 0) || (csc->getClientServicePortPublic() != 0)) {
const LoginConnectionServerAddress csa(csc->getId(), csc->getClientServiceAddress(), csc
->getClientServicePortPrivate(), csc->getClientServicePortPublic(), csc
->getPlayerCount(), csc->getPingPort());
l->send(csa, true);
}
}
// send preload is finished, if it is
PreloadFinishedMessage msg(isPreloadFinished());
l->send(msg, true);
CharacterCreationTracker::getInstance().retryLoginServerCreates();
break;
}
case constcrc("LoginConnectionClosed") : {
LoginServerConnection const *l = safe_cast<const LoginServerConnection *>(&source);
LoginServerConnectionMapType::iterator i = m_loginServerConnectionMap.find(l->getProcessId());
if (i != m_loginServerConnectionMap.end()) {
m_loginServerConnectionMap.erase(i);
}
// In development mode, try to reconnect to the login server if we aren't shutting down
if (ConfigCentralServer::getDevelopmentMode() && !m_done) {
connectToLoginServer();
}
CharacterCreationTracker::getInstance().onLoginServerDisconnect(l->getProcessId());
break;
}
case constcrc("ClusterId") : {
Archive::ReadIterator ri = static_cast<GameNetworkMessage const &>(message).getByteStream().begin();
GenericValueTypeMessage <uint32> const msg(ri);
if (m_clusterId == 0) {
FATAL(((msg.getValue() < 1) ||
(msg.getValue() > 255)), ("Cluster Id (%lu) must be between 1 and 255 inclusive", msg
.getValue()));
m_clusterId = static_cast<uint8>(msg.getValue());
// start launching processes
launchStartingProcesses();
}
break;
}
case constcrc("CentralGameServerConnect") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
CentralGameServerConnect c(ri);
GameServerConnection *g = const_cast<GameServerConnection *>(safe_cast < GameServerConnection const * >
(&source));
if (g != nullptr) {
FATAL(ConfigCentralServer::getValidateBuildVersionNumber() &&
strcmp(ApplicationVersion::getInternalVersion(), c.getBuildVersionNumber().c_str()),
("Build version number mismatch: central server (%s), remote server %s (%s)",
ApplicationVersion::getInternalVersion(), g->getRemoteAddress().c_str(), c
.getBuildVersionNumber().c_str()));
// a game server (or db process) has connected...
addGameServer(g);
//Send connection server data
ConnectionServerConnectionList::iterator i = m_connectionServerConnections.begin();
for (; i != m_connectionServerConnections.end(); ++i) {
if ((*i)->getGameServicePort() != 0) {
ConnectionServerAddress csa((*i)->getGameServiceAddress(), (*i)->getGameServicePort());
g->send(csa, true);
}
}
std::set<ChatServerConnection *>::const_iterator chatIter;
for (chatIter = m_chatServerConnections.begin();
chatIter != m_chatServerConnections.end(); ++chatIter) {
if ((*chatIter)->getGameServicePort()) {
ChatServerOnline cso((*chatIter)->getRemoteAddress(), (*chatIter)->getGameServicePort());
g->send(cso, true);
}
}
const GenericValueTypeMessage <std::pair<std::string, unsigned short>> address("CustomerServiceServerGameServerServiceAddress", std::make_pair(s_customerServiceServerGameServerServiceAddress
.first, s_customerServiceServerGameServerServiceAddress.second));
g->send(address, true);
}
break;
}
case constcrc("ChatClosedConnectionWithGameServer") : {
GameServerConnection *g = const_cast<GameServerConnection *>(safe_cast < GameServerConnection const * >
(&source));
std::set<ChatServerConnection *>::const_iterator chatIter;
for (chatIter = m_chatServerConnections.begin(); chatIter != m_chatServerConnections.end(); ++chatIter) {
if ((*chatIter)->getGameServicePort()) {
ChatServerOnline cso((*chatIter)->getRemoteAddress(), (*chatIter)->getGameServicePort());
g->send(cso, true);
}
}
break;
}
case constcrc("CustomerServiceServerGameServerServiceAddress") : {
//DEBUG_REPORT_LOG(true, ("CentralServer::receiveMessage(CustomerServiceServerGameServerServiceAddress)\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const GenericValueTypeMessage <std::pair<std::string, unsigned short>> address(ri);
s_customerServiceServerGameServerServiceAddress.first = address.getValue().first;
s_customerServiceServerGameServerServiceAddress.second = address.getValue().second;
broadcastToGameServers(address);
break;
}
case constcrc("TaskConnectionClosed") : {
// Net::getInstance().connect(Network::Address("127.0.0.1", ConfigCentralServer::getTaskManagerPort()), TaskConnection());
break;
}
case constcrc("TaskConnectionOpened") : {
DEBUG_REPORT_LOG(true, ("Task manager connection opened\n"));
break;
}
case constcrc("CentralGameServerDbProcessServerProcessId") : {
DEBUG_REPORT_LOG(true, ("dbProcess connected\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
CentralGameServerDbProcessServerProcessId d(ri);
m_dbProcessServerProcessId = d.getServerProcessId();
ServerClock::getInstance().setSubtractInterval(d.getSubtractInterval());
launchCommoditiesServer();
launchStartingPlanetServers();
}
case constcrc("RequestChunkMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
RequestChunkMessage t(ri);
// Forward this message to the dbProcess
sendToGameServer(m_dbProcessServerProcessId, t, true);
break;
}
case constcrc("LocateStructureMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
LocateStructureMessage t(ri);
sendToPlanetServer(t.getSceneId(), t, true);
break;
}
case constcrc("ForceUnloadObjectMessage") : {
//N.B. This message can come from a game server or from the planet server.
DEBUG_WARNING(true, ("Received ForceUnloadObject. Need to implement this\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ForceUnloadObjectMessage t(ri);
//@todo: figure out some way to handle this (such as forwarding to PlanetServers), or remove every case where it's sent
//forceUnload(t.getId(),t.getPermaDelete());
}
//Character Creation Messages
case constcrc("ConnectionCreateCharacter") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ConnectionCreateCharacter c(ri);
LOG("TraceCharacterCreation", ("%d received ConnectionCreateCharacter", c.getStationId()));
CharacterCreationTracker::getInstance().handleCreateNewCharacter(c);
break;
}
case constcrc("GameCreateCharacterFailed") : {
DEBUG_REPORT_LOG(true, ("Game server advises central that character creation failed\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GameCreateCharacterFailed f(ri);
CharacterCreationTracker::getInstance()
.handleGameCreateCharacterFailed(f.getStationId(), f.getName(), f.getErrorMessage(), f
.getOptionalDetailedErrorMessage());
break;
}
case constcrc("DatabaseCreateCharacterSuccess") : {
DEBUG_REPORT_LOG(true, ("Database Process advises central that character creation succeeded\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
DatabaseCreateCharacterSuccess s(ri);
CharacterCreationTracker::getInstance()
.handleDatabaseCreateCharacterSuccess(s.getStationId(), s.getCharacterName(), s.getObjectId(), s
.getTemplateId(), s.getJedi());
break;
}
case constcrc("LoginCreateCharacterAckMessage") : {
DEBUG_REPORT_LOG(true, ("Login Server advises central that character creation succeeded\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
LoginCreateCharacterAckMessage s(ri);
CharacterCreationTracker::getInstance().handleLoginCreateCharacterAck(s.getStationId());
break;
}
case constcrc("LoginRestoreCharacterMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
LoginRestoreCharacterMessage msg(ri);
IGNORE_RETURN(sendToArbitraryLoginServer(msg));
break;
}
case constcrc("NewCharacterCreated") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <StationId> const ncc(ri);
CentralServer::getInstance().sendToAllConnectionServers(ncc, true);
break;
}
case constcrc("DatabaseConsoleReplyMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<std::string, std::string>> msg(ri);
IGNORE_RETURN(sendToRandomGameServer(msg));
break;
}
case constcrc("LoginUpgradeAccountMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
LoginUpgradeAccountMessage msg(ri);
if (msg.getIsAck()) {
MessageToMessage const reply(
MessageToPayload(msg.getReplyToObject(), NetworkId::cms_invalid, msg.getReplyMessage(), msg
.getPackedMessageData(), 0, false, MessageToPayload::DT_c, NetworkId::cms_invalid, std::string(), 0),
0);
sendToAllGameServers(reply, true);
}
else {
IGNORE_RETURN(sendToArbitraryLoginServer(msg));
}
break;
}
case constcrc("RandomNameRequest") : {
GameServerConnection *gameServer = getRandomGameServer();
if (gameServer) {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
RandomNameRequest crnr(ri);
gameServer->send(crnr, true);
}
break;
}
case constcrc("RandomNameResponse") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
RandomNameResponse crnr(ri);
ConnectionServerConnection *conn = getConnectionServerForAccount(crnr.getStationId());
if (conn) {
conn->send(crnr, true);
}
else {
DEBUG_REPORT_LOG(true, ("Could not send name to client because unable to determine which connection server to use.\n"));
}
break;
}
case constcrc("VerifyAndLockNameRequest") : {
GameServerConnection *gameServer = getRandomGameServer();
if (gameServer) {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
VerifyAndLockNameRequest valnr(ri);
gameServer->send(valnr, true);
}
break;
}
case constcrc("VerifyAndLockNameResponse") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
VerifyAndLockNameResponse valnr(ri);
ConnectionServerConnection *conn = getConnectionServerForAccount(valnr.getStationId());
if (conn) {
conn->send(valnr, true);
}
else {
DEBUG_REPORT_LOG(true, ("Could not send name lock response to client because unable to determine which connection server to use.\n"));
}
}
case constcrc("RequestOIDsMessage") : {
DEBUG_REPORT_LOG(true, ("Got RequestOIDsMessage.\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
RequestOIDsMessage m(ri);
if (m.getLogRequest()) {
LOG("ObjectIdManager", ("Requesting %lu more object ids for pid %lu", m.getHowMany(), m.getServerId()));
}
sendToGameServer(m_dbProcessServerProcessId, m, true);
break;
}
case constcrc("AddOIDBlockMessage") : {
DEBUG_REPORT_LOG(true, ("Got AddOIDBlockMessage.\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
AddOIDBlockMessage m(ri);
if (m.getLogRequest()) {
LOG("ObjectIdManager", ("Returning object ids (%s - %s) for pid %lu", m.getStart().getValueString()
.c_str(), m.getEnd()
.getValueString()
.c_str(), m
.getServerId()));
}
sendToGameServer(m.getServerId(), m, true);
break;
}
case constcrc("LoggedInMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
LoggedInMessage m(ri);
DEBUG_REPORT_LOG(true, ("Pending character %lu is logging in or dropping\n", m.getAccountNumber()));
// Once they're logged in, Central doesn't need to know about them anymore:
removeFromAccountConnectionMap(m.getAccountNumber());
break;
}
case constcrc("CharacterListMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
CharacterListMessage m(ri);
// Find the client connection and send the character to it.
DEBUG_REPORT_LOG(true, ("Got CharacterListMessage for %lu.\n", m.getAccountNumber()));
ConnectionServerConnection *conn = getConnectionServerForAccount(m.getAccountNumber());
if (conn) {
conn->send(m, true);
}
else {
DEBUG_REPORT_LOG(true, ("Warning: received CharacterListMessage for client that is not connected."));
}
break;
}
case constcrc("ValidateCharacterForLoginMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ValidateCharacterForLoginMessage m(ri);
// ask DBProcess to check whether the character is valid
//pendingCharactersLogin[m.getSuid()] = safe_cast<ConnectionServerConnection const *>(&source);
if (hasDBConnection()) {
sendToDBProcess(m, true);
}
break;
}
case constcrc("ValidateCharacterForLoginReplyMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ValidateCharacterForLoginReplyMessage msg(ri);
ConnectionServerConnection *conn = getConnectionServerForAccount(msg.getSuid());
if (conn) {
conn->send(msg, true);
}
else {
DEBUG_REPORT_LOG(true, ("Trying to handle ValidateCharacterForLoginReplyMessage for account %lu, but could not determine which connection server to use.\n", msg
.getSuid()));
}
break;
}
case constcrc("EnableCharacterMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < std::pair < StationId, NetworkId >, std::string > > msg(ri);
LOG("CustomerService", ("EnableCharacter %d, %s request from %s\n", msg.getValue().first.first, msg
.getValue().first.second.getValueString().c_str(), msg.getValue().second.c_str()));
// forward this request to LoginServer
IGNORE_RETURN(sendToArbitraryLoginServer(msg));
break;
}
case constcrc("EnableCharacterReplyMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<std::string, std::string>> msg(ri);
GenericValueTypeMessage <std::pair<std::string, std::string>> reply("DatabaseConsoleReplyMessage", std::make_pair(msg
.getValue().first, msg.getValue().second));
getRandomGameServer()->send(reply, true);
break;
}
case constcrc("DisableCharacterMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < std::pair < StationId, NetworkId >, std::string > > msg(ri);
LOG("CustomerService", ("DisableCharacter %d, %s request from %s\n", msg.getValue().first.first, msg
.getValue().first.second.getValueString().c_str(), msg.getValue().second.c_str()));
// forward this request to LoginServer
IGNORE_RETURN(sendToArbitraryLoginServer(msg));
break;
}
case constcrc("DisableCharacterReplyMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<std::string, std::string>> msg(ri);
GenericValueTypeMessage <std::pair<std::string, std::string>> reply("DatabaseConsoleReplyMessage", std::make_pair(msg
.getValue().first, msg.getValue().second));
getRandomGameServer()->send(reply, true);
break;
}
case constcrc("TransferReplyLoginLocationData") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <TransferCharacterData> reply(ri);
// If this request has a CS Tool Id associated with it, it is an admin request for the CSTool,
// and so we should send it directly to the connection server, and not depend on a
// transfer server existing.
if (reply.getValue().getCSToolId() > 0) {
GenericValueTypeMessage <TransferCharacterData> loginMessage("TransferLoginCharacterToSourceServer", reply
.getValue());
ConnectionServerConnection *conn = getAnyConnectionServer();
if (conn) {
conn->send(loginMessage, true);
}
return;
}
LOG("CustomerService", ("CharacterTransfer: Received TransferReplyLoginLocationData from database for character %s\n", reply
.getValue().getSourceCharacterName().c_str()));
if (reply.getValue().getTransferRequestSource() == TransferRequestMoveValidation::TRS_transfer_server) {
CentralServer::getInstance().sendToTransferServer(reply);
}
else {
// send character to ConnectionServer for login to a game server
ConnectionServerConnection *connectionServer = CentralServer::getInstance().getAnyConnectionServer();
if (connectionServer) {
const GenericValueTypeMessage <TransferCharacterData> login("TransferLoginCharacterToSourceServer", reply
.getValue());
connectionServer->send(login, true);
LOG("CustomerService", ("CharacterTransfer: Sending TransferLoginCharacterToSourceServer to ConnectionServer : %s", login
.getValue().toString().c_str()));
}
}
break;
}
case constcrc("CentralPlanetServerConnect") : {
PlanetServerConnection *g = const_cast<PlanetServerConnection *>(safe_cast < PlanetServerConnection
const
* > (&source));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
CentralPlanetServerConnect msg(ri);
g->setGameServerConnectionData(msg.getConnectionAddress(), msg.getGameServerPort());
PlanetManager::addServer(msg.getSceneId(), g);
std::map < std::string, std::pair < std::pair < std::string, std::string >, time_t > > ::iterator
f = m_pendingPlanetServers.find(msg.getSceneId());
if (f != m_pendingPlanetServers.end()) {
m_pendingPlanetServers.erase(f);
}
IGNORE_RETURN(m_planetServers.insert(std::make_pair(msg.getSceneId(), g)));
if (isPreloadFinished()) {
m_timeClusterWentIntoLoadingState = 0;
}
else if (m_timeClusterWentIntoLoadingState <= 0) {
m_timeClusterWentIntoLoadingState = time(0);
}
// handle planet transfers and logins for planet server that aren't up yet
std::vector<Archive::ByteStream>::iterator t;
for(t = m_messagesWaitingForPlanetServer.begin(); t != m_messagesWaitingForPlanetServer.end();)
{
Archive::ReadIterator tri = t->begin();
const GameNetworkMessage gnm(tri);
tri = t->begin();
if(gnm.isType("RequestGameServerForLoginMessage"))
{
const RequestGameServerForLoginMessage loginMessage(tri);
if (loginMessage.getScene() == msg.getSceneId())
{
t = m_messagesWaitingForPlanetServer.erase(t);
handleRequestGameServerForLoginMessage(loginMessage);
}
}
else if(gnm.isType("RequestSceneTransfer"))
{
const RequestSceneTransfer sceneMessage(tri);
if (sceneMessage.getSceneName() == msg.getSceneId())
{
t = m_messagesWaitingForPlanetServer.erase(t);
handleRequestSceneTransfer(sceneMessage);
}
}
else
{
t = m_messagesWaitingForPlanetServer.erase(t);
WARNING_STRICT_FATAL(true, ("Unknown message type waiting for planet server"));
}
}
break;
}
case constcrc("RequestSceneTransfer") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const RequestSceneTransfer msg(ri);
handleRequestSceneTransfer(msg);
break;
}
case constcrc("SceneTransferMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
SceneTransferMessage msg(ri);
sendToGameServer(msg.getSourceGameServer(), msg, true);
break;
}
case constcrc("GameServerReadyMessage") : {
GameServerConnection const *g = safe_cast < GameServerConnection const * > (&source);
UniverseManager::getInstance().onGameServerReady(*g);
break;
}
case constcrc("GameServerUniverseLoadedMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const GameServerUniverseLoadedMessage msg(ri);
// forward to the universe game server as ack that
// the game server who sent this message has received
// its UniverseCompleteMessage from the universe game server
if (msg.getSourceOfUniverseDataProcessId() != getDbProcessServerProcessId()) {
sendToGameServer(msg.getSourceOfUniverseDataProcessId(), msg, true);
}
CharacterCreationTracker::getInstance().retryGameServerCreates();
break;
}
case constcrc("MessageToMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
MessageToMessage msg(ri);
WARNING_STRICT_FATAL(true, ("CentralServer receieved a messageTo. These should not go to Central anymore. Sender was server %u\n",
safe_cast < GameServerConnection const * > (&source)->getProcessId()));
break;
}
case constcrc("MessageToAckMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
MessageToAckMessage msg(ri);
WARNING_STRICT_FATAL(true, ("CentralServer receieved a MessageToAckMessage. These should not go to Central anymore. Sender was server %u\n",
safe_cast < GameServerConnection const * > (&source)->getProcessId()));
break;
}
case constcrc("ChatServerConnectionOpened") : {
// enumerate servers
ChatServerConnection *chatServer = const_cast<ChatServerConnection *>(safe_cast < ChatServerConnection
const
* > (&source));
IGNORE_RETURN(m_chatServerConnections.insert(chatServer));
const int ct = static_cast<int>(EnumerateServers::CONNECTION_SERVER);
for (ConnectionServerConnectionList::const_iterator ci = m_connectionServerConnections.begin();
ci != m_connectionServerConnections.end(); ++ci) {
ConnectionServerConnection *c = (*ci);
if ((c != nullptr)
&& !c->getChatServiceAddress().empty()
&& (c->getChatServicePort() != 0)) {
EnumerateServers e(true, c->getChatServiceAddress(), c->getChatServicePort(), ct);
chatServer->send(e, true);
}
else {
LOG("ChatServer", ("receiveMessage() ChatServerConnectionOpened - Invalid connection address in m_connectionServerConnections, skipping entry"));
}
}
// Tell the single chat server the address to communicate with the customer service server
if (!s_customerServiceServerChatServerServiceAddress.first.empty()) {
//DEBUG_REPORT_LOG(true, ("CentralServer::receiveMessage(ChatServerConnectionOpened)\n"));
const GenericValueTypeMessage <std::pair<std::string, unsigned short>> msg("CustomerServiceServerChatServerServiceAddress", std::make_pair(s_customerServiceServerChatServerServiceAddress
.first, s_customerServiceServerChatServerServiceAddress.second));
chatServer->send(msg, true);
}
break;
}
case constcrc("CustomerServiceServerChatServerServiceAddress") : {
//DEBUG_REPORT_LOG(true, ("CentralServer::receiveMessage(CustomerServiceServerChatServerServiceAddress)\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const GenericValueTypeMessage <std::pair<std::string, unsigned short>> address(ri);
s_customerServiceServerChatServerServiceAddress.first = address.getValue().first;
s_customerServiceServerChatServerServiceAddress.second = address.getValue().second;
// Tell the chat servers the address to communicate with the customer service server
std::set<ChatServerConnection *>::iterator iterChatServerConnections = m_chatServerConnections.begin();
for (; iterChatServerConnections != m_chatServerConnections.end(); ++iterChatServerConnections) {
ChatServerConnection *connection = (*iterChatServerConnections);
if (connection != nullptr) {
connection->send(address, true);
}
else {
REPORT_LOG(true, ("Trying to send the customer service server: chat server service address to a nullptr chat server\n"));
}
}
break;
}
case constcrc("CustomerServiceConnectionOpened") : {
CustomerServiceConnection *csServer = const_cast<CustomerServiceConnection *>(safe_cast <
CustomerServiceConnection const
* > (&source));
IGNORE_RETURN(m_csServerConnections.insert(csServer));
const int ct = static_cast<int>(EnumerateServers::CONNECTION_SERVER);
for (ConnectionServerConnectionList::const_iterator ci = m_connectionServerConnections.begin();
ci != m_connectionServerConnections.end(); ++ci) {
ConnectionServerConnection *c = (*ci);
if ((c != nullptr)
&& !c->getCustomerServiceAddress().empty()
&& (c->getCustomerServicePort() != 0)) {
EnumerateServers e(true, c->getCustomerServiceAddress(), c->getCustomerServicePort(), ct);
csServer->send(e, true);
}
else {
LOG("CustServ", ("receiveMessage() CustomerServiceConnectionOpened - Invalid connection address in m_connectionServerConnections, skipping entry"));
}
}
break;
}
case constcrc("ChatServerConnectionClosed") : {
ChatServerConnection *chatServer = const_cast<ChatServerConnection *>(safe_cast < ChatServerConnection
const
* > (&source));
IGNORE_RETURN(m_chatServerConnections.erase(chatServer));
// spawn a new chat server!
std::string options = "-s ChatServer centralServerAddress=";
if (CentralServer::getInstance().getChatService()) {
options += CentralServer::getInstance().getChatService()->getBindAddress();
}
else {
options += NetworkHandler::getHostName();
}
options += " clusterName=";
options += ConfigCentralServer::getClusterName();
TaskSpawnProcess pc(ConfigCentralServer::getChatServerHost(), "ChatServer", options, ConfigCentralServer::getChatServerRestartDelayTimeSeconds());
CentralServer::getInstance().sendTaskMessage(pc);
break;
}
case constcrc("CustomerServiceConnectionClosed") : {
CustomerServiceConnection *csServer = const_cast<CustomerServiceConnection *>(safe_cast <
CustomerServiceConnection const
* > (&source));
IGNORE_RETURN(m_csServerConnections.erase(csServer));
// spawn a new cs server!
std::string options = "-s CustomerServiceServer centralServerAddress=";
if (CentralServer::getInstance().getCustomerService()) {
options += CentralServer::getInstance().getCustomerService()->getBindAddress();
}
else {
options += NetworkHandler::getHostName();
}
options += " clusterName=";
options += ConfigCentralServer::getClusterName();
TaskSpawnProcess pc("any", "CustomerServiceServer", options);
CentralServer::getInstance().sendTaskMessage(pc);
break;
}
case constcrc("ChatServerOnline") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ChatServerOnline cso(ri);
ChatServerConnection *csc = const_cast<ChatServerConnection *>(safe_cast < ChatServerConnection const * >
(&source));
csc->setGameServicePort(cso.getPort());
SceneGameMap::const_iterator iter;
for (iter = m_gameServers.begin(); iter != m_gameServers.end(); ++iter) {
GameServerConnection *conn = (*iter).second;
conn->send(cso, true);
}
break;
}
case constcrc("RequestGameServerForLoginMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const RequestGameServerForLoginMessage msg(ri);
time_t const timeNow = ::time(nullptr);
PlayerSceneMapType::const_iterator i = m_playerSceneMap.find(msg.getCharacterId());
if ((i != m_playerSceneMap.end()) && (i->second.second > timeNow)) {
ServerConnection * c = const_cast<ServerConnection *>(safe_cast < ServerConnection const * > (&source));
GenericValueTypeMessage <std::pair<NetworkId, uint32>> const loginDeniedRecentCTS("LoginDeniedRecentCTS", std::make_pair(msg
.getCharacterId(), msg.getStationId()));
c->send(loginDeniedRecentCTS, true);
}
else {
std::map < NetworkId, std::pair < time_t, int > > ::const_iterator
const iterFind = s_pendingRenameCharacter.find(msg.getCharacterId());
if ((iterFind != s_pendingRenameCharacter.end()) && (iterFind->second.first > timeNow)) {
ServerConnection * c = const_cast<ServerConnection *>(safe_cast < ServerConnection const * >
(&source));
GenericValueTypeMessage <std::pair<NetworkId, uint32>> const loginDeniedPendingPlayerRenameRequest("LoginDeniedPendingPlayerRenameRequest", std::make_pair(msg
.getCharacterId(), msg.getStationId()));
c->send(loginDeniedPendingPlayerRenameRequest, true);
}
else {
handleRequestGameServerForLoginMessage(msg);
}
}
break;
}
case constcrc("GameServerForLoginMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GameServerForLoginMessage msg(ri);
handleGameServerForLoginMessage(msg);
break;
}
case constcrc("ExchangeListCreditsMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ExchangeListCreditsMessage msg(ri);
handleExchangeListCreditsMessage(msg);
break;
}
case constcrc("PlanetLoadCharacterMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
PlanetLoadCharacterMessage msg(ri);
// let all the game servers know we are about to load a character from the DB
GenericValueTypeMessage <std::pair<NetworkId, uint32>> const aboutToLoadCharacterFromDB("AboutToLoadCharacterFromDB", std::make_pair(msg
.getCharacterId(), msg.getGameServerId()));
sendToAllGameServersExceptDBProcess(aboutToLoadCharacterFromDB, true);
sendToDBProcess(msg, true);
break;
}
case constcrc("ConnSrvDropDupeConns") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<uint32, std::string>> const msg(ri);
// A ConnectionServer has received and validated a new connection from the client
// request that all other ConnectionServers drop any existing connection(s) for the account
std::string galaxyConnectionInfo = "(";
galaxyConnectionInfo += ConfigCentralServer::getClusterName();
if (!msg.getValue().second.empty()) {
galaxyConnectionInfo += ", ";
galaxyConnectionInfo += msg.getValue().second;
}
galaxyConnectionInfo += ")";
GenericValueTypeMessage <std::pair<uint32, std::string>> const dropDuplicateConnections("CntrlSrvDropDupeConns", std::make_pair(msg
.getValue().first, galaxyConnectionInfo));
sendToAllConnectionServers(dropDuplicateConnections, true, dynamic_cast<Connection const *>(&source));
// request (via LoginServer) that all ConnectionServers on all other galaxies drop any existing connection(s) for the account
if (ConfigCentralServer::getDisconnectDuplicateConnectionsOnOtherGalaxies()) {
IGNORE_RETURN(sendToArbitraryLoginServer(dropDuplicateConnections));
}
break;
}
case constcrc("CntrlSrvDropDupeConns") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<uint32, std::string>> const msg(ri);
// request that all ConnectionServers drop any existing connection(s) for the account
sendToAllConnectionServers(msg, true);
break;
}
case constcrc("PlayerDroppedFromGameServerCrash") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::set<NetworkId>> const m(ri);
std::set<NetworkId>::const_iterator const end = m.getValue().end();
for (std::set<NetworkId>::const_iterator iter = m.getValue().begin(); iter != end; ++iter) {
IGNORE_RETURN(m_playerSceneMap.erase(*iter));
LOG("TRACE_LOGIN", ("Forgetting sceneId (because of game server crash) for character (%s) -- will query the database on next login", iter
->getValueString().c_str()));
}
break;
}
case constcrc("ServerDeleteCharacterMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
LoginServerConnection const *l = safe_cast < LoginServerConnection const * > (&source);
ServerDeleteCharacterMessage msg(ri);
msg.setLoginServerId(l->getProcessId());
if (hasDBConnection()) {
LOG("CustomerService", ("Player:deleted character %s for stationId %u", msg.getCharacterId()
.getValueString()
.c_str(), msg
.getStationId()));
sendToDBProcess(msg, true);
// let the game servers know that the character is being deleted
GenericValueTypeMessage <NetworkId> const msg2("DeleteCharacterNotificationMessage", msg
.getCharacterId());
IGNORE_RETURN(sendToRandomGameServer(msg2));
}
break;
}
case constcrc("UpdatePlayerCountMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
UpdatePlayerCountMessage msg(ri);
ConnectionServerConnection *csc = const_cast<ConnectionServerConnection *>(dynamic_cast<const ConnectionServerConnection *>(&source));
if (csc) {
// Update our stored values for the connection server
csc->setPlayerCount(msg.getCount());
csc->setFreeTrialCount(msg.getFreeTrialCount());
csc->setEmptySceneCount(msg.getEmptySceneCount());
csc->setTutorialSceneCount(msg.getTutorialSceneCount());
csc->setFalconSceneCount(msg.getFalconSceneCount());
// Update the login server with the new values
UpdateLoginConnectionServerStatus ulc(csc->getId(), csc->getClientServicePortPublic(), csc
->getClientServicePortPrivate(), msg.getCount());
sendToAllLoginServers(ulc);
// Update the login servers with new population values
sendPopulationUpdateToLoginServer();
}
else {
WARNING_STRICT_FATAL(true, ("Got UpdatePlayerCountMessage from something that wasn't a ConnectionServer.\n"));
}
break;
}
case constcrc("ValidateAccountMessage") : {
DEBUG_REPORT_LOG(true, ("ValidateAccountMessage\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ValidateAccountMessage msg(ri);
// Store the connection information for the account
addToAccountConnectionMap(msg
.getStationId(), const_cast<ConnectionServerConnection *>(dynamic_cast<const ConnectionServerConnection *>(&source)), msg
.getSubscriptionBits());
// Pass the validation to a LoginServer
IGNORE_RETURN(sendToArbitraryLoginServer(msg));
break;
}
case constcrc("ValidateAccountReplyMessage") : {
DEBUG_REPORT_LOG(true, ("ValidateAccountReplyMessage\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ValidateAccountReplyMessage msg(ri);
ConnectionServerConnection *conn = getConnectionServerForAccount(msg.getStationId());
if (conn) {
conn->send(msg, true);
}
break;
}
case constcrc("PreloadRequestCompleteMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
PreloadRequestCompleteMessage msg(ri);
sendToDBProcess(msg, true);
break;
}
case constcrc("ReconnectToTransferServer") : {
if (ConfigCentralServer::getTransferServerPort()) {
if (!getInstance().m_transferServerConnection) {
getInstance()
.m_transferServerConnection = new TransferServerConnection(ConfigCentralServer::getTransferServerAddress(), ConfigCentralServer::getTransferServerPort());
s_retryTransferServerConnection = true;
}
}
break;
}
case constcrc("ReconnectToStationPlayersCollector") : {
if (ConfigCentralServer::getStationPlayersCollectorPort()) {
if (!getInstance().m_stationPlayersCollectorConnection) {
getInstance()
.m_stationPlayersCollectorConnection = new StationPlayersCollectorConnection(ConfigCentralServer::getStationPlayersCollectorAddress(), ConfigCentralServer::getStationPlayersCollectorPort());
s_retryStationPlayersCollectorConnection = true;
}
}
break;
}
case constcrc("PreloadFinishedMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
PreloadFinishedMessage msg(ri);
const PlanetServerConnection *conn = dynamic_cast<const PlanetServerConnection *>(&source);
WARNING_STRICT_FATAL(!conn, ("Programmer bug: got PreloadFinishedMessaage from something that wasn't a PlanetServer.\n"));
if (conn) {
if (msg.getFinished()) {
IGNORE_RETURN(m_planetsWaitingForPreload.erase(conn->getSceneId()));
if (isPreloadFinished()) {
m_timeClusterWentIntoLoadingState = 0;
DEBUG_REPORT_LOG(true, ("Preload finished on all planets.\n"));
// record how long it took the cluster to come up
if (m_clusterStartupTime == -1) {
m_clusterStartupTime = static_cast<int>(time(0) - m_timeClusterStarted) / 60;
// let all the game servers know that the cluster has completed its initial startup
GenericValueTypeMessage<bool> clusterStartComplete("ClusterStartComplete", true);
sendToAllGameServersExceptDBProcess(clusterStartComplete, true);
}
else {
// let all the game servers know that the cluster has recovered from a crash
GenericValueTypeMessage<bool> clusterStartComplete("ClusterStartComplete", false);
sendToAllGameServersExceptDBProcess(clusterStartComplete, true);
}
sendToAllLoginServers(msg);
sendTaskMessage(msg);
m_lastLoadingStateTime = time(0);
// connect to the character transfer server
if (ConfigCentralServer::getTransferServerPort()) {
if (!getInstance().m_transferServerConnection) {
getInstance()
.m_transferServerConnection = new TransferServerConnection(ConfigCentralServer::getTransferServerAddress(), ConfigCentralServer::getTransferServerPort());
s_retryTransferServerConnection = true;
}
}
// connect to the station players collector
if (ConfigCentralServer::getStationPlayersCollectorPort()) {
if (!getInstance().m_stationPlayersCollectorConnection) {
getInstance()
.m_stationPlayersCollectorConnection = new StationPlayersCollectorConnection(ConfigCentralServer::getStationPlayersCollectorAddress(), ConfigCentralServer::getStationPlayersCollectorPort());
s_retryStationPlayersCollectorConnection = true;
}
}
}
else if (m_timeClusterWentIntoLoadingState <= 0) {
m_timeClusterWentIntoLoadingState = time(0);
}
}
else {
if (getInstance().m_transferServerConnection != nullptr) {
getInstance().m_transferServerConnection->disconnect();
getInstance().m_transferServerConnection = 0;
s_retryTransferServerConnection = false;
}
if (getInstance().m_stationPlayersCollectorConnection != nullptr) {
getInstance().m_stationPlayersCollectorConnection->disconnect();
getInstance().m_stationPlayersCollectorConnection = 0;
s_retryStationPlayersCollectorConnection = false;
}
IGNORE_RETURN(m_planetsWaitingForPreload.insert(conn->getSceneId()));
if (isPreloadFinished()) {
m_timeClusterWentIntoLoadingState = 0;
}
else if (m_timeClusterWentIntoLoadingState <= 0) {
m_timeClusterWentIntoLoadingState = time(0);
}
sendToAllLoginServers(msg);
sendTaskMessage(msg);
}
}
break;
}
case constcrc("RenameCharacterMessageEx") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
RenameCharacterMessageEx msg(ri);
IGNORE_RETURN(sendToArbitraryLoginServer(RenameCharacterMessage(msg.getCharacterId(), msg.getNewName(), msg
.getRequestedBy())));
// if the player requested the rename, also rename the chat avatar, so mail, friends list, and ignore list will migrate
if ((msg.getRenameCharacterMessageSource() == RenameCharacterMessageEx::RCMS_player_request) &&
!msg.getLastNameChangeOnly() &&
!ConfigFile::getKeyBool("CharacterRename", "disableRenameChatAvatar", false)) {
broadcastToChatServers(msg);
}
break;
}
case constcrc("PlayerRenameRequestSubmitted") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < unsigned
int, std::pair < NetworkId, std::pair < std::string, bool > > > >
const msg(ri);
GenericValueTypeMessage<unsigned int> kick("TransferKickConnectedClients", msg.getValue().first);
CentralServer::getInstance().sendToAllLoginServers(kick);
CentralServer::getInstance().sendToAllConnectionServers(kick, true);
// update the list of pending rename requests, to prevent a character with a pending rename request from logging in
std::map < NetworkId, std::pair < time_t, int > > ::iterator
const iterFind = s_pendingRenameCharacter.find(msg.getValue().second.first);
if (iterFind != s_pendingRenameCharacter.end()) {
++(iterFind->second.second);
iterFind->second.first = ::time(nullptr) + 3600; // 1 hour timeout
}
else {
s_pendingRenameCharacter
.insert(std::make_pair(msg.getValue().second.first, std::make_pair((::time(nullptr) +
3600), 1))); // 1 hour timeout
}
// tell the chat server to destroy any avatar with the new name, but only if the first name changed
if (!msg.getValue().second.second.second) {
GenericValueTypeMessage <std::string> const chatDestroyAvatar("ChatDestroyAvatar", msg.getValue().second
.second.first);
broadcastToChatServers(chatDestroyAvatar);
}
break;
}
case constcrc("PlayerRenameRequestCompleted") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<unsigned int, NetworkId>> const msg(ri);
// update the list of pending rename requests, to prevent a character with a pending rename request from logging in
std::map < NetworkId, std::pair < time_t, int > > ::iterator
const iterFind = s_pendingRenameCharacter.find(msg.getValue().second);
if (iterFind != s_pendingRenameCharacter.end()) {
if (iterFind->second.second <= 1) {
s_pendingRenameCharacter.erase(iterFind);
}
else {
--(iterFind->second.second);
}
}
break;
}
case constcrc("SetConnectionServerPublic") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
SetConnectionServerPublic msg(ri);
if (msg.getIsPublic()) {
gs_connectionServersPublic = true;
}
else {
gs_connectionServersPublic = false;
}
sendToAllConnectionServers(msg, true);
break;
}
case constcrc("ProfilerOperationMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ProfilerOperationMessage msg(ri);
sendToAllGameServers(msg, true);
sendToAllPlanetServers(msg, true);
sendToAllConnectionServers(msg, true);
break;
}
case constcrc("PopulationListMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
PopulationListMessage msg(ri);
sendToAllGameServers(msg, true);
break;
}
case constcrc("CentralPingMessage") : {
GameServerConnection const *g = safe_cast < GameServerConnection const * > (&source);
NOT_NULL(g);
LOG("CentralServerPings", ("Got reply from %lu", g->getProcessId()));
IGNORE_RETURN(m_serverPings.erase(g->getProcessId()));
break;
}
case constcrc("DatabaseBackloggedMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage<bool> dbbacklog(ri);
m_databaseBacklogged = dbbacklog.getValue();
bool const preloadFinished = isPreloadFinished();
if (preloadFinished) {
m_timeClusterWentIntoLoadingState = 0;
}
else if (m_timeClusterWentIntoLoadingState <= 0) {
m_timeClusterWentIntoLoadingState = time(0);
}
PreloadFinishedMessage msg(preloadFinished);
sendToAllLoginServers(msg);
sendTaskMessage(msg);
break;
}
case constcrc("DatabaseSaveStart") : {
LOG("CentralServer", ("Received DatabaseSaveStart network message."));
if (m_shutdownPhase == 4) {
LOG("CentralServerShutdown", ("Shutdown Phase %d: Setting indicator for receipt of DatabaseSaveStart.", m_shutdownPhase));
m_shutdownHaveDatabaseSaveStart = true;
checkShutdownProcess();
}
else if (m_shutdownPhase == 5 && m_shutdownHaveDatabaseSaveStart) {
LOG("CentralServerShutdown", ("Shutdown Phase %d: Setting indicator that final database save cycle is complete.", m_shutdownPhase));
m_shutdownHaveDatabaseComplete = true;
checkShutdownProcess();
}
break;
}
case constcrc("DatabaseSaveComplete") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage<int> msg(ri);
LOG("CentralServer", ("Received DatabaseSaveComplete network message."));
// tell all the Planet Servers that the save finished
sendToAllPlanetServers(msg, true);
// don't want to indicate this yet until we are at the beginning of a full cycle
if (m_shutdownPhase == 5 && m_shutdownHaveDatabaseSaveStart) {
LOG("CentralServerShutdown", ("Shutdown Phase %d: Setting indicator that final database save cycle is complete.", m_shutdownPhase));
m_shutdownHaveDatabaseComplete = true;
checkShutdownProcess();
}
break;
}
case constcrc("PlanetRequestSave") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GameNetworkMessage msg(ri);
sendToDBProcess(msg, true);
break;
}
case constcrc("ShutdownCluster") : {
LOG("CentralServerShutdown", ("Received ShutdownCluster network message."));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ShutdownCluster m(ri);
startShutdownProcess(m.getTimeToShutdown(), m.getMaxTime(), m.getSystemMessage());
break;
}
case constcrc("AbortShutdown") : {
LOG("CentralServerShutdown", ("Received AbortShutdown network message."));
abortShutdownProcess();
break;
}
case constcrc("SetSceneForPlayer") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < NetworkId, std::pair < std::string, bool > > > ssfp(ri);
if (ssfp.getValue().second.first.empty()) {
static const std::string loginTrace("TRACE_LOGIN");
IGNORE_RETURN(m_playerSceneMap.erase(ssfp.getValue().first));
LOG(loginTrace, ("Forgetting sceneId for character (%s) -- will query the database on next login", ssfp
.getValue().first.getValueString().c_str()));
}
else {
PlayerSceneMapType::iterator i = m_playerSceneMap.find(ssfp.getValue().first);
if (i != m_playerSceneMap.end()) {
i->second.first = ssfp.getValue().second.first;
if (ssfp.getValue().second.second) {
i->second.second = ::time(nullptr) +
static_cast<time_t>(ConfigCentralServer::getCtsDenyLoginThresholdSeconds());
}
}
else {
m_playerSceneMap[ssfp.getValue().first] = std::make_pair(ssfp.getValue().second
.first, (ssfp.getValue().second.second
? (::time(nullptr) +
static_cast<time_t>(ConfigCentralServer::getCtsDenyLoginThresholdSeconds()))
: 0));
}
}
break;
}
case constcrc("TaskProcessDiedMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
TaskProcessDiedMessage died(ri);
LOG("TaskProcessDied", ("received TaskProcessDied for %s:%i", died.getProcessName().c_str(), died
.getProcessId()));;
if (died.getProcessName().find("SwgGameServer") != std::string::npos) {
LOG("TaskProcessDied", ("Dead process %i is a game server", died.getProcessId()));
// extract sceneId
size_t pos = died.getProcessName().find("sceneID=");
if (pos != std::string::npos) {
pos += std::string("sceneID=").length();
size_t end = died.getProcessName().find_first_of(' ', pos);
if (end != std::string::npos) {
std::string scene = died.getProcessName().substr(pos, end - pos);
LOG("TaskProcessDied", ("Dead game server process %i was running sceneID %s, advising planet server", died
.getProcessId(), scene.c_str()));
std::map<std::string, PlanetServerConnection *>::iterator f = m_planetServers.find(scene);
if (f != m_planetServers.end()) {
f->second->send(died, true);
}
}
}
}
break;
}
case constcrc("SystemTimeMismatchNotification") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::string> msg(ri);
m_lastTimeSystemTimeMismatchNotification = time(0);
m_lastTimeSystemTimeMismatchNotificationDescription = msg.getValue();
break;
}
case constcrc("DisconnectedTaskManagerMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::string> msg(ri);
m_disconnectedTaskManagerList = msg.getValue();
break;
}
case constcrc("TransferServerConnectionClosed") : {
// connect to the character transfer server
if (ConfigCentralServer::getTransferServerPort()) {
if (s_retryTransferServerConnection) {
getInstance()
.m_transferServerConnection = new TransferServerConnection(ConfigCentralServer::getTransferServerAddress(), ConfigCentralServer::getTransferServerPort());
}
}
break;
}
case constcrc("StationPlayersCollectorConnectionClosed") : {
// connect to the station players collector
if (ConfigCentralServer::getStationPlayersCollectorPort()) {
if (s_retryStationPlayersCollectorConnection) {
getInstance()
.m_stationPlayersCollectorConnection = new StationPlayersCollectorConnection(ConfigCentralServer::getStationPlayersCollectorAddress(), ConfigCentralServer::getStationPlayersCollectorPort());
}
}
break;
}
case constcrc("ClaimRewardsMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ClaimRewardsMessage msg(ri);
sendToArbitraryLoginServer(msg);
break;
}
case constcrc("ClaimRewardsReplyMessage") : {
DEBUG_REPORT_LOG(true, ("Central got ClaimRewardsReplyMessage\n"));
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ClaimRewardsReplyMessage msg(ri);
sendToGameServer(msg.getGameServer(), msg, true);
break;
}
case constcrc("PurgeStructuresForAccountMessage") :
case constcrc("WarnStructuresAboutPurgeMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <StationId> msg(ri);
LoginServerConnection const *l = dynamic_cast<LoginServerConnection const *>(&source);
if (l) {
m_purgeAccountToLoginServerMap[msg.getValue()] = l
->getProcessId(); // remember which login server is handling this purge
sendToDBProcess(msg, true);
}
break;
}
case constcrc("PurgeCompleteMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <StationId> msg(ri);
std::map<StationId, uint32>::iterator i = m_purgeAccountToLoginServerMap.find(msg.getValue());
if (i != m_purgeAccountToLoginServerMap.end()) {
sendToLoginServer(i->second, msg);
m_purgeAccountToLoginServerMap.erase(i);
}
break;
}
case constcrc("RestartServerMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
RestartServerMessage msg(ri);
sendToPlanetServer(msg.getScene(), msg, true);
break;
}
case constcrc("RestartServerByRoleMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<std::string, uint32>> msg(ri);
sendToPlanetServer(msg.getValue().first, msg, true);
break;
}
case constcrc("ExcommunicateGameServerMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ExcommunicateGameServerMessage msg(ri);
excommunicateServer(msg);
break;
}
case constcrc("RestartPlanetMessage") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::string> msg(ri);
GenericValueTypeMessage<int> const shutdownMsg("ShutdownMessage", 0);
sendToPlanetServer(msg.getValue(), shutdownMsg, true);
break;
}
case constcrc("UpdateClusterLockedAndSecretState") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<bool, bool>> const msg(ri);
gs_clusterIsLocked = msg.getValue().first;
gs_clusterIsSecret = msg.getValue().second;
break;
}
case constcrc("PopStatRsp") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::map<std::string, int>> const msg(ri);
m_timePopulationStatisticsRefresh = ::time(nullptr);
m_populationStatistics = msg.getValue();
break;
}
case constcrc("GcwScoreStatRsp") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < std::map < std::string, int >, std::pair < std::map < std::string,
std::pair < int64, int64 > >, std::map < std::string, std::pair < int64, int64 > > > > >
const msg(ri);
m_timeGcwScoreStatisticsRefresh = ::time(nullptr);
std::string const timeGcwScoreStatisticsRefreshStr = CalendarTime::convertEpochToTimeStringLocal(m_timeGcwScoreStatisticsRefresh);
std::map<std::string, int> const &gcwImperialScorePercentile = msg.getValue().first;
std::map <std::string, std::pair<int64, int64>> const &gcwImperialScore = msg.getValue().second.first;
std::map <std::string, std::pair<int64, int64>> const &gcwRebelScore = msg.getValue().second.second;
std::map < std::string, std::pair < int64, int64 > > ::const_iterator
iterFind;
std::map<std::string, int>::const_iterator iterFindImperialScorePercentile;
std::pair < std::map < std::string, std::pair < int, std::pair < std::string, std::string > > > ::iterator,
bool > iterInsertion;
int imperialScorePercentile;
std::string scoreText, scoreTextDesc;
for (std::map < std::string, std::pair < int64, int64 > > ::const_iterator iterImp = gcwImperialScore
.begin(); iterImp != gcwImperialScore.end();
++iterImp)
{
iterFindImperialScorePercentile = gcwImperialScorePercentile.find(iterImp->first);
if (iterFindImperialScorePercentile != gcwImperialScorePercentile.end()) {
imperialScorePercentile = iterFindImperialScorePercentile->second;
}
else {
imperialScorePercentile = 0;
}
iterFind = gcwRebelScore.find(iterImp->first);
if (iterFind != gcwRebelScore.end()) {
scoreText = FormattedString<1024>()
.sprintf("Imp (%s, %s) Reb (%s, %s) - ", NetworkId(iterImp->second.first).getValueString()
.c_str(), NetworkId(iterImp
->second.second).getValueString().c_str(), NetworkId(iterFind->second.first)
.getValueString().c_str(), NetworkId(iterFind->second.second).getValueString()
.c_str());
}
else {
scoreText = FormattedString<1024>()
.sprintf("Imp (%s, %s) Reb (0, 0) - ", NetworkId(iterImp->second.first).getValueString()
.c_str(), NetworkId(iterImp
->second.second).getValueString().c_str());
}
scoreTextDesc = scoreText + timeGcwScoreStatisticsRefreshStr;
iterInsertion = m_gcwScoreStatistics.insert(std::make_pair("gcwScore." +
iterImp->first, std::make_pair(imperialScorePercentile, std::make_pair(scoreText, scoreTextDesc))));
if (!iterInsertion.second) {
if (iterInsertion.first->second.second.first != scoreText) {
iterInsertion.first->second.first = imperialScorePercentile;
iterInsertion.first->second.second.first = scoreText;
iterInsertion.first->second.second.second = scoreTextDesc;
}
}
}
for (std::map < std::string, std::pair < int64, int64 > > ::const_iterator iterReb = gcwRebelScore.begin();
iterReb != gcwRebelScore.end();
++iterReb)
{
iterFind = gcwImperialScore.find(iterReb->first);
if (iterFind == gcwImperialScore.end()) {
iterFindImperialScorePercentile = gcwImperialScorePercentile.find(iterReb->first);
if (iterFindImperialScorePercentile != gcwImperialScorePercentile.end()) {
imperialScorePercentile = iterFindImperialScorePercentile->second;
}
else {
imperialScorePercentile = 0;
}
scoreText = FormattedString<1024>()
.sprintf("Imp (0, 0) Reb (%s, %s) - ", NetworkId(iterReb->second.first).getValueString()
.c_str(), NetworkId(iterReb
->second.second).getValueString().c_str());
scoreTextDesc = scoreText + timeGcwScoreStatisticsRefreshStr;
iterInsertion = m_gcwScoreStatistics.insert(std::make_pair("gcwScore." +
iterReb->first, std::make_pair(imperialScorePercentile, std::make_pair(scoreText, scoreTextDesc))));
if (!iterInsertion.second) {
if (iterInsertion.first->second.second.first != scoreText) {
iterInsertion.first->second.first = imperialScorePercentile;
iterInsertion.first->second.second.first = scoreText;
iterInsertion.first->second.second.second = scoreTextDesc;
}
}
}
}
break;
}
case constcrc("LLTStatRsp") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < std::map < int, std::pair < std::string, int > >, std::map < int,
std::pair < std::string, int > > > >
const msg(ri);
m_timeLastLoginTimeStatisticsRefresh = ::time(nullptr);
m_lastLoginTimeStatistics = msg.getValue().first;
m_createTimeStatistics = msg.getValue().second;
break;
}
case constcrc("LfgStatRsp") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage <std::pair<unsigned long, std::pair<unsigned long, unsigned long> >> const msg(ri);
m_numberOfCharacterMatchRequests += msg.getValue().first;
m_numberOfCharacterMatchResults += msg.getValue().second.first;
m_timeSpentOnCharacterMatchRequestsMs += msg.getValue().second.second;
break;
}
case constcrc("OccupyUnlockedSlotRsp") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < std::pair < int, NetworkId >, uint32 > >
const occupyUnlockedSlotRsp(ri);
// CS log the response
LoginUpgradeAccountMessage::OccupyUnlockedSlotResponse const response = static_cast<LoginUpgradeAccountMessage::OccupyUnlockedSlotResponse>(occupyUnlockedSlotRsp
.getValue().first.first);
if (response == LoginUpgradeAccountMessage::OUSR_success) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s OccupyUnlockedSlot request SUCCESS", occupyUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else if (response == LoginUpgradeAccountMessage::OUSR_db_error) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s OccupyUnlockedSlot request FAILED - internal db error", occupyUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else if (response == LoginUpgradeAccountMessage::OUSR_account_has_no_unlocked_slot) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s OccupyUnlockedSlot request FAILED - account doesn't have an unlocked slot", occupyUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else if (response == LoginUpgradeAccountMessage::OUSR_account_has_no_unoccupied_unlocked_slot) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s OccupyUnlockedSlot request FAILED - account has no unoccupied unlocked slot", occupyUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else if (response == LoginUpgradeAccountMessage::OUSR_cluster_already_has_unlocked_slot_character) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s OccupyUnlockedSlot request FAILED - cluster already has an unlocked slot character", occupyUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else {
LOG("CustomerService", ("JediUnlockedSlot:Player %s OccupyUnlockedSlot request FAILED - unknown result code (%d)", occupyUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), occupyUnlockedSlotRsp.getValue().first
.first));
}
GameServerConnection *gs = getGameServer(occupyUnlockedSlotRsp.getValue().second);
if (!gs) {
gs = getRandomGameServer();
}
if (gs) {
gs->send(occupyUnlockedSlotRsp, true);
}
break;
}
case constcrc("VacateUnlockedSlotRsp") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < std::pair < int, NetworkId >, std::pair < uint32, uint32 > > >
const vacateUnlockedSlotRsp(ri);
// CS log the response
LoginUpgradeAccountMessage::VacateUnlockedSlotResponse const response = static_cast<LoginUpgradeAccountMessage::VacateUnlockedSlotResponse>(vacateUnlockedSlotRsp
.getValue().first.first);
if (response == LoginUpgradeAccountMessage::VUSR_success) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s VacateUnlockedSlot request SUCCESS", vacateUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
// to safeguard against any sort of timing exploit to create another normal
// slot character while this one is being converted to normal, thus allowing
// the account to have more normal slot characters on this cluster than is
// allowed, make it look like a normal character has been created on this
// account on this cluster, and that mechanism will prevent such an exploit
// let all connection servers know that a new character has been created for the station account
GenericValueTypeMessage <StationId> const ncc("NewCharacterCreated", static_cast<StationId>(vacateUnlockedSlotRsp
.getValue().second.first));
CentralServer::getInstance().sendToAllConnectionServers(ncc, true);
}
else if (response == LoginUpgradeAccountMessage::VUSR_db_error) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s VacateUnlockedSlot request FAILED - internal db error", vacateUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else if (response == LoginUpgradeAccountMessage::VUSR_account_has_no_unlocked_slot) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s VacateUnlockedSlot request FAILED - account doesn't have an unlocked slot", vacateUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else if (response == LoginUpgradeAccountMessage::VUSR_not_unlocked_slot_character) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s VacateUnlockedSlot request FAILED - character is not an unlocked slot character", vacateUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else if (response == LoginUpgradeAccountMessage::VUSR_no_available_normal_character_slot) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s VacateUnlockedSlot request FAILED - no available normal character slot for the account on this galaxy", vacateUnlockedSlotRsp
.getValue().first.second.getValueString().c_str()));
}
else {
LOG("CustomerService", ("JediUnlockedSlot:Player %s VacateUnlockedSlot request FAILED - unknown result code (%d)", vacateUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), vacateUnlockedSlotRsp.getValue().first
.first));
}
GameServerConnection *gs = getGameServer(vacateUnlockedSlotRsp.getValue().second.second);
if (!gs) {
gs = getRandomGameServer();
}
if (gs) {
gs->send(vacateUnlockedSlotRsp, true);
}
break;
}
case constcrc("SwapUnlockedSlotRsp") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage < std::pair < std::pair < int, NetworkId >, std::pair < uint32, std::pair <
NetworkId,
std::string > > > >
const swapUnlockedSlotRsp(ri);
// CS log the response
LoginUpgradeAccountMessage::SwapUnlockedSlotResponse const response = static_cast<LoginUpgradeAccountMessage::SwapUnlockedSlotResponse>(swapUnlockedSlotRsp
.getValue().first.first);
if (response == LoginUpgradeAccountMessage::SUSR_success) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s SwapUnlockedSlot with %s (%s) request SUCCESS", swapUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), swapUnlockedSlotRsp.getValue().second.second
.first.getValueString()
.c_str(), swapUnlockedSlotRsp
.getValue().second.second.second.c_str()));
}
else if (response == LoginUpgradeAccountMessage::SUSR_db_error) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s SwapUnlockedSlot with %s request FAILED - internal db error", swapUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), swapUnlockedSlotRsp.getValue().second.second
.first.getValueString()
.c_str()));
}
else if (response == LoginUpgradeAccountMessage::SUSR_account_has_no_unlocked_slot) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s SwapUnlockedSlot with %s request FAILED - account doesn't have an unlocked slot", swapUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), swapUnlockedSlotRsp.getValue().second.second
.first.getValueString()
.c_str()));
}
else if (response == LoginUpgradeAccountMessage::SUSR_not_unlocked_slot_character) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s SwapUnlockedSlot with %s request FAILED - source character is not an unlocked slot character", swapUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), swapUnlockedSlotRsp.getValue().second.second
.first.getValueString()
.c_str()));
}
else if (response == LoginUpgradeAccountMessage::SUSR_invalid_target_character) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s SwapUnlockedSlot with %s request FAILED - target character is either not valid, not on the same account, or not on this galaxy", swapUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), swapUnlockedSlotRsp.getValue().second.second
.first.getValueString()
.c_str()));
}
else if (response == LoginUpgradeAccountMessage::SUSR_target_character_already_unlocked_slot_character) {
LOG("CustomerService", ("JediUnlockedSlot:Player %s SwapUnlockedSlot with %s request FAILED - target character is already an unlocked slot character", swapUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), swapUnlockedSlotRsp.getValue().second.second
.first.getValueString()
.c_str()));
}
else {
LOG("CustomerService", ("JediUnlockedSlot:Player %s SwapUnlockedSlot with %s request FAILED - unknown result code (%d)", swapUnlockedSlotRsp
.getValue().first.second.getValueString().c_str(), swapUnlockedSlotRsp.getValue().second.second
.first.getValueString()
.c_str(), swapUnlockedSlotRsp
.getValue().first.first));
}
GameServerConnection *gs = getGameServer(swapUnlockedSlotRsp.getValue().second.first);
if (!gs) {
gs = getRandomGameServer();
}
if (gs) {
gs->send(swapUnlockedSlotRsp, true);
}
break;
}
case constcrc("AdjustAccountFeatureIdResponse") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
AdjustAccountFeatureIdResponse const msg(ri);
GameServerConnection *gs = getGameServer(msg.getGameServer());
if (!gs) {
gs = getRandomGameServer();
}
if (gs) {
gs->send(msg, true);
}
break;
}
case constcrc("AccountFeatureIdResponse") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
AccountFeatureIdResponse const msg(ri);
GameServerConnection *gs = getGameServer(msg.getGameServer());
if (!gs) {
gs = getRandomGameServer();
}
if (gs) {
gs->send(msg, true);
}
break;
}
case constcrc("FeatureIdTransactionResponse") : {
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
FeatureIdTransactionResponse const msg(ri);
GameServerConnection *gs = getGameServer(msg.getGameServer());
if (!gs) {
gs = getRandomGameServer();
}
if (gs) {
gs->send(msg, true);
}
break;
}
default :
if (ClusterWideDataManagerList::handleMessage(source, message)) {
// nothing else to do with the message since it was
// handled by the Cluster wide data manager
}
break;
}
}
//-----------------------------------------------------------------------
void CentralServer::removeConnectionServerConnection(const ConnectionServerConnection * conn)
{
DEBUG_REPORT_LOG(true, ("Removing connection server connection\n"));
if (conn)
{
startConnectionServer(conn->getConnectionServerNumber(),
conn->getGameServiceAddress(),
conn->getClientServicePortPublic(),
conn->getClientServicePortPrivate(), true,
ConfigCentralServer::getConnectionServerRestartDelayTimeSeconds());
ConnectionServerConnectionList::iterator i = m_connectionServerConnections.begin();
for(;i != m_connectionServerConnections.end();++i)
{
if (conn->getId() == (*i)->getId())
{
ConnectionServerDown c((*i)->getId());
sendToAllLoginServers(c);
IGNORE_RETURN(m_connectionServerConnections.erase(i));
return;
}
}
// Clean up all the account info for the connection
removeFromAccountConnectionMap(conn->getId());
}
}
//-----------------------------------------------------------------------
/**
* Removes all references to a game server from the server.
*
* @param gameServer the game server connection to remove
*/
void CentralServer::removeGameServer(GameServerConnection const *gameServer)
{
if (gameServer != nullptr)
{
uint32 const pid = gameServer->getProcessId();
LOG("ServerStartup", ("Game Server %lu went down", pid));
//@todo this whole function needs a re-write.
NOT_NULL(gameServer);
std::map<uint32, GameServerConnection *>::iterator const i = m_gameServerConnections.find(pid);
if (i == m_gameServerConnections.end())
return;
for (std::vector<GameServerConnection *>::iterator ii = m_gameServerConnectionsList.begin(); ii != m_gameServerConnectionsList.end();)
{
if ((*ii) == i->second)
ii = m_gameServerConnectionsList.erase(ii);
else
++ii;
}
m_gameServerConnections.erase(i);
/** @todo: this is slow, find a better way (probably by creating reverse
* lookup table(s))
*/
if (pid == m_dbProcessServerProcessId)
{
DEBUG_REPORT_LOG(true, ("Database process died -- Central will exit and let the cluster restart\n"));
m_done = true;
return; //lint !e527 Unreachable
}
DEBUG_WARNING(true, ("Game server %lu crashed", pid));
for (SceneGameMap::iterator j = m_gameServers.begin(); j != m_gameServers.end();)
{
if ((*j).second == gameServer)
m_gameServers.erase(j++);
else
++j;
}
UniverseManager::getInstance().onGameServerDisconnect(*gameServer);
PlanetManager::onGameServerDisconnect(gameServer);
CharacterCreationTracker::getInstance().onGameServerDisconnect(pid);
ClusterWideDataManagerList::onGameServerDisconnect(pid);
ExcommunicateGameServerMessage const excommunicateMessage(pid, 0, "");
sendToAllGameServersExceptDBProcess(excommunicateMessage, true);
}
else
{
DEBUG_WARNING(true, ("A game server crashed but our process ID ptr is nullptr."));
}
}
//-----------------------------------------------------------------------
void CentralServer::done()
{
getInstance().m_done = true;
}
//-----------------------------------------------------------------------
void CentralServer::run(void)
{
SetupSharedLog::install("CentralServer");
SetupSharedUtility::Data utilityData;
SetupSharedUtility::install (utilityData);
SetupServerUtility::install();
gs_connectionServersPublic = ConfigCentralServer::getStartPublic();
// listen for game server connect requests
static CentralServer & cserver = getInstance();
NetworkSetupData setup;
setup.maxConnections = 100;
setup.bindInterface = ConfigCentralServer::getGameServiceBindInterface();
setup.port = ConfigCentralServer::getGameServicePort();
Service * gs = new Service(ConnectionAllocator<GameServerConnection>(), setup);
cserver.m_gameService = gs;
// listen for planet server connection requests
setup.bindInterface = ConfigCentralServer::getPlanetServiceBindInterface();
setup.port = ConfigCentralServer::getPlanetServicePort();
Service * ps = new Service(ConnectionAllocator<PlanetServerConnection>(), setup);
cserver.m_planetService = ps;
setup.port = ConfigCentralServer::getChatServicePort();
setup.maxConnections = 32;
setup.bindInterface = ConfigCentralServer::getChatServiceBindInterface();
Service * cs = new Service(ConnectionAllocator<ChatServerConnection>(), setup);
cserver.m_chatService = cs;
setup.port = ConfigCentralServer::getCustomerServicePort();
setup.bindInterface = ConfigCentralServer::getCustomerServiceBindInterface();
Service * css = new Service(ConnectionAllocator<CustomerServiceConnection>(), setup);
cserver.m_csService = css;
setup.port = ConfigCentralServer::getConnectionServicePort();
setup.bindInterface = ConfigCentralServer::getConnectionServiceBindInterface();
Service * cons = new Service(ConnectionAllocator<ConnectionServerConnection>(), setup);
NOT_NULL(cons);
cserver.m_connService = cons;
setup.port = ConfigCentralServer::getConsoleServicePort();
setup.bindInterface = ConfigCentralServer::getConsoleServiceBindInterface();
cserver.m_consoleService = new Service(ConnectionAllocator<ConsoleConnection>(), setup);
setup.port = ConfigCentralServer::getCommodityServerServicePort();
setup.bindInterface = ConfigCentralServer::getCommodityServerServiceBindInterface();
s_commodityServerService = new Service(ConnectionAllocator<ServerConnection>(), setup);
setup.port = ConfigCentralServer::getLoginServicePort();
if (ConfigCentralServer::getDevelopmentMode())
cserver.connectToLoginServer();
else
{
setup.bindInterface = ConfigCentralServer::getLoginServiceBindInterface();
cserver.m_loginService = new Service(ConnectionAllocator<LoginServerConnection>(), setup);
}
// connect to the task manager
cserver.m_taskManager = new TaskConnection("127.0.0.1", ConfigCentralServer::getTaskManagerPort());
unsigned long startTime = Clock::timeMs();
unsigned long nextLoadingLogTime=0;
unsigned long nextPingTime=0;
unsigned long nextPopulationLogTime = 0;
LOG("ServerStartup",("CentralServer starting"));
#ifndef WIN32
if( FileExists( ".shutdown") )
{
LOG("CentralServer", ("Removing stale .shutdown file."));
IGNORE_RETURN(::remove( ".shutdown" ));
}
if( FileExists( ".abortshutdown" ) )
{
LOG("CentralServer", ("Removing stale .abortshutdown file."));
IGNORE_RETURN(::remove( ".abortshutdown" ));
}
if( FileExists( ".startanymissingplanet") )
{
LOG("CentralServer", ("Removing stale .startanymissingplanet file."));
IGNORE_RETURN(::remove( ".startanymissingplanet" ));
}
if( FileExists( ".startanymissinggameserver") )
{
LOG("CentralServer", ("Removing stale .startanymissinggameserver file."));
IGNORE_RETURN(::remove( ".startanymissinggameserver" ));
}
#endif
while (!cserver.m_done)
{
unsigned long lastFrameTime = 0;
unsigned long frameStartTime = Clock::timeMs();
PROFILER_AUTO_BLOCK_DEFINE("main loop");
bool barrierReached = true;
do
{
PROFILER_AUTO_BLOCK_DEFINE("BarrierWait");
if (!Os::update())
cserver.m_done = true;
{
PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::update");
NetworkHandler::update();
}
{
PROFILER_AUTO_BLOCK_DEFINE("MetricsManager::update");
unsigned long curTime = Clock::timeMs();
MetricsManager::update(static_cast<float>(curTime - startTime));
startTime = curTime;
}
if (ConfigCentralServer::getShouldSleep())
{
PROFILER_AUTO_BLOCK_DEFINE("Os::sleep");
Os::sleep(1);
}
} while (!barrierReached && !cserver.m_done);
//@todo Central needs to run a clock so we can schedule re-tries with the login server.
{
PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::dispatch");
NetworkHandler::dispatch();
}
{
PROFILER_AUTO_BLOCK_DEFINE("update");
cserver.update();
}
NetworkHandler::clearBytesThisFrame();
{
//Peridically log loading status
unsigned long curTime = Clock::timeMs();
if (curTime > nextLoadingLogTime)
{
nextLoadingLogTime = curTime + 10000;
for (std::set<std::string>::const_iterator i=getInstance().m_planetsWaitingForPreload.begin(); i!=getInstance().m_planetsWaitingForPreload.end(); ++i)
LOG("Preload",("Waiting for planet %s",i->c_str()));
}
//Perodically ping all the servers
if (ConfigCentralServer::getServerPingTimeout() != 0 && static_cast<int>(curTime-nextPingTime) > 0)
{
nextPingTime = curTime + static_cast<uint32>(ConfigCentralServer::getServerPingTimeout() * 1000);
getInstance().doServerPings();
}
//Periodically write out population to a log every 5 minutes
if (curTime > nextPopulationLogTime)
{
nextPopulationLogTime = curTime + 300000;
LOG("PopulationLog", ("Current Population: %d", getInstance().getPlayerCount()));
}
}
ServerClock::getInstance().incrementServerFrame();
if(ConfigCentralServer::getShutdown())
{
cserver.done();
}
unsigned long currentTime = Clock::timeMs();
lastFrameTime = currentTime - frameStartTime;
if(lastFrameTime > 1000)
{
LOG("profile", ("Long loop (%u ms):\n%s", lastFrameTime, PROFILER_GET_LAST_FRAME_DATA()));
}
}
LOG("ServerStartup",("CentralServer exiting"));
SetupSharedLog::remove();
CentralServer::remove();
}
// ----------------------------------------------------------------------
/**
* Called every frame to do any required updates.
*/
void CentralServer::update()
{
static int loopCount = 0;
static int apiLoopCount = 0;
static int shutdownCheckLoopCount = 0;
m_curTime = static_cast<uint32>(time(0));
// Tell the LoginServers if necessary
if ((++loopCount > ConfigCentralServer::getUpdatePlayerCountFrequency()))
{
loopCount = 0;
// Update the population on the server
sendPopulationUpdateToLoginServer();
}
// update the webAPI if specified
int webUpdateIntervalSeconds = ConfigCentralServer::getWebUpdateIntervalSeconds();
// assuming that every 5th frame is ~1 second, we can multiply and then check
if (webUpdateIntervalSeconds && (++apiLoopCount > (webUpdateIntervalSeconds*1000)) )
{
apiLoopCount = 0;
// update the web api
sendMetricsToWebAPI();
}
if ( ConfigCentralServer::getAuctionEnabled() ) // allow auctions?
{
if ( m_pAuctionTransferClient == nullptr )
{
const char* hostName[1] = { ConfigCentralServer::getAuctionServer() };
const short port[1] = { (short)ConfigCentralServer::getAuctionPort() };
std::string s_id = ConfigCentralServer::getAuctionIDPrefix();
s_id += ConfigCentralServer::getClusterName();
const char *identifier[1];
identifier[ 0 ] = s_id.c_str();
m_pAuctionTransferClient = new AuctionTransferClient( hostName, port, 1, identifier, 1 );
}
m_pAuctionTransferClient->process();
}
else if ( m_pAuctionTransferClient )
{
delete( m_pAuctionTransferClient );
m_pAuctionTransferClient = 0;
}
// check every 5th frame (one second roughly?)
if ( ++shutdownCheckLoopCount > 5 )
{
shutdownCheckLoopCount = 0;
checkShutdownProcess();
}
// update cluster wide data manager
ClusterWideDataManagerList::update(Clock::frameTime());
}
// ----------------------------------------------------------------------
void CentralServer::sendPopulationUpdateToLoginServer()
{
// Add up all the population totals (these are referenced by the metrics data)
m_totalPlayerCount = 0;
m_totalFreeTrialCount = 0;
m_totalEmptySceneCount = 0;
m_totalTutorialSceneCount = 0;
m_totalFalconSceneCount = 0;
ConnectionServerConnectionList::const_iterator i;
for (i=m_connectionServerConnections.begin(); i!=m_connectionServerConnections.end(); ++i)
{
if (*i)
{
m_totalPlayerCount += (**i).getPlayerCount();
m_totalFreeTrialCount += (**i).getFreeTrialCount();
m_totalEmptySceneCount += (**i).getEmptySceneCount();
m_totalTutorialSceneCount += (**i).getTutorialSceneCount();
m_totalFalconSceneCount += (**i).getFalconSceneCount();
}
}
bool loadedRecently=false;
if (!isPreloadFinished() || (time(0)-m_lastLoadingStateTime < static_cast<time_t>(ConfigCentralServer::getRecentLoadingStateSeconds())))
loadedRecently=true;
UpdatePlayerCountMessage upm(loadedRecently, m_totalPlayerCount, m_totalFreeTrialCount, m_totalEmptySceneCount, m_totalTutorialSceneCount, m_totalFalconSceneCount);
sendToAllLoginServers(upm);
}
void CentralServer::sendMetricsToWebAPI()
{
static const std::string metricsURL(ConfigCentralServer::getMetricsDataURL());
if (!metricsURL.empty())
{
// create the object
webAPI api(metricsURL);
// add our data
api.addJsonData<int>("totalPlayerCount", m_totalPlayerCount);
api.addJsonData<int>("totalGameServers", (m_gameServers.size() - 1));
api.addJsonData<int>("totalPlanetServers", m_planetServers.size());
api.addJsonData<int>("totalTutorialSceneCount", m_totalTutorialSceneCount);
#ifdef _DEBUG
if (api.submit()) {
bool status = api.getNullableValue<bool>("status");
if (status)
{
std::string message = api.getString("message");
if (message.empty())
{
message = "No message returned.";
}
WARNING(true, ("Error sending stats: %s", message.c_str()));
}
WARNING(true, ("Success sending server stats to API."));
}
else
{
WARNING(true, ("Error sending stats."));
}
#else
api.submit();
#endif
}
}
//-----------------------------------------------------------------------
void CentralServer::sendTaskMessage(const GameNetworkMessage & source)
{
if(m_taskManager)
m_taskManager->send(source, true);
DEBUG_REPORT_LOG(!m_taskManager, ("There is no task manager connection, but app is attempting to send to one\n"));
}
//-----------------------------------------------------------------------
void CentralServer::sendToGameServer(const uint32 gameServerProcessId, const GameNetworkMessage & message, const bool reliable) const
{
GameServerConnection * g = getGameServer(gameServerProcessId);
if(g)
{
g->send(message, reliable);
}
else
{
DEBUG_WARNING(true,("Attempted to send to game server %i, without connection.\n",gameServerProcessId));
}
}
// ----------------------------------------------------------------------
void CentralServer::sendToLoginServer(uint32 loginServerId, const GameNetworkMessage &message)
{
LoginServerConnectionMapType::iterator i=m_loginServerConnectionMap.find(loginServerId);
if (i!=m_loginServerConnectionMap.end() && i->second)
i->second->send(message,true);
}
// ----------------------------------------------------------------------
void CentralServer::sendToAllGameServers(const GameNetworkMessage & message, const bool reliable)
{
for (std::map<uint32, GameServerConnection *>::const_iterator i = m_gameServerConnections.begin(); i!=m_gameServerConnections.end(); ++i)
{
(*i).second->send(message, reliable);
}
}
// ----------------------------------------------------------------------
void CentralServer::sendToAllGameServersExceptDBProcess(const GameNetworkMessage & message, const bool reliable)
{
for (std::map<uint32, GameServerConnection *>::const_iterator i = m_gameServerConnections.begin(); i!=m_gameServerConnections.end(); ++i)
{
if ((*i).first != getDbProcessServerProcessId())
(*i).second->send(message, reliable);
}
}
// ----------------------------------------------------------------------
void CentralServer::sendToAllPlanetServers(const GameNetworkMessage & message, const bool reliable)
{
for (std::map<std::string, PlanetServerConnection *>::const_iterator i = m_planetServers.begin(); i != m_planetServers.end(); ++i)
(*i).second->send(message, reliable);
}
// ----------------------------------------------------------------------
bool CentralServer::hasPlanetServer(const std::string & sceneName) const
{
for (std::map<std::string, PlanetServerConnection *>::const_iterator i = m_planetServers.begin(); i != m_planetServers.end(); ++i)
if (i->second->getSceneId() == sceneName)
return true;
return false;
}
// ----------------------------------------------------------------------
void CentralServer::sendToPlanetServer(const std::string &sceneId, const GameNetworkMessage & message, const bool reliable)
{
for (std::map<std::string, PlanetServerConnection *>::const_iterator i = m_planetServers.begin(); i != m_planetServers.end(); ++i)
if (i->second->getSceneId() == sceneId)
(*i).second->send(message, reliable);
}
// ----------------------------------------------------------------------
void CentralServer::sendToAllConnectionServers(const GameNetworkMessage & message, const bool reliable, Connection const * exclude /*= nullptr*/)
{
// send to all connection servers
for (ConnectionServerConnectionList::const_iterator i = m_connectionServerConnections.begin(); i != m_connectionServerConnections.end(); ++i)
{
if ((*i) && (*i != exclude))
(*i)->send(message, reliable);
}
}
//-----------------------------------------------------------------------
void CentralServer::sendToDBProcess(const GameNetworkMessage & message, const bool reliable) const
{
GameServerConnection * g = getGameServer(getDbProcessServerProcessId());
if(g)
{
g->send(message, reliable);
}
else
{
DEBUG_FATAL(!m_done,("Attempted to send message to DBProcess without connection (there should always be a connection to DBProcess)."));
}
}
// ----------------------------------------------------------------------
bool CentralServer::hasDBConnection() const
{
const GameServerConnection * g = getGameServer(getDbProcessServerProcessId());
return (g != nullptr);
}
//-----------------------------------------------------------------------
void CentralServer::setTaskManager(TaskConnection * newTaskManager)
{
if (m_taskManager && newTaskManager != m_taskManager)
{
m_taskManager->setDisconnectReason("CentralServer::setTaskManager called");
m_taskManager->disconnect();
}
m_taskManager = newTaskManager;
}
// ----------------------------------------------------------------------
void CentralServer::removePlanetServer(const PlanetServerConnection * p)
{
for (std::map<std::string, PlanetServerConnection *>::iterator i = m_planetServers.begin(); i != m_planetServers.end();)
{
if ((*i).second == p)
{
DEBUG_REPORT_LOG(true,("Central lost connection to Planet Server %s\n",p->getSceneId().c_str()));
i = m_planetServers.erase(i);
if (isPreloadFinished())
m_timeClusterWentIntoLoadingState = 0;
else if (m_timeClusterWentIntoLoadingState <= 0)
m_timeClusterWentIntoLoadingState = time(0);
return;
}
else
{
++i;
}
}
}
// ----------------------------------------------------------------------
const std::string & CentralServer::getCommandLine() const
{
return m_commandLine;
}
//-----------------------------------------------------------------------
void CentralServer::setCommandLine(const std::string & c)
{
m_commandLine = c;
}
//-----------------------------------------------------------------------
const bool getStartLocation(const std::string & name, std::string & planetName, Vector & coordinates, NetworkId & cellId)
{
const StartingLocationData * const sld = StartingLocationManager::findLocationByName (name);
if (sld)
{
planetName = sld->planet;
coordinates.x = sld->x;
coordinates.y = sld->y;
coordinates.z = sld->z;
cellId = NetworkId (sld->cellId);
return true;
}
REPORT_LOG(true, ("The start location \"%s\" could not be found in StartingLocationManager", name.c_str()));
return false;
}
//-----------------------------------------------------------------------
void CentralServer::startPlanetServer(const std::string & host, const std::string & sceneId, const SpawnDelaySeconds spawnDelay)
{
FATAL(sceneId.empty(), ("CentralServer::startPlanetServer: empty sceneId, host='%s'", host.c_str()));
std::map<std::string, std::pair<std::pair<std::string, std::string>, time_t> >::const_iterator f = m_pendingPlanetServers.find(sceneId);
if(f == m_pendingPlanetServers.end())
{
std::map<std::string, PlanetServerConnection *>::const_iterator pf = m_planetServers.find(sceneId);
if(pf == m_planetServers.end())
{
std::string options = "-s PlanetServer centralServerAddress=";
if(m_planetService)
{
options += m_planetService->getBindAddress();
}
else
{
options += NetworkHandler::getHostName();
}
options += " sceneID=";
options += sceneId;
char buffer[20];
IGNORE_RETURN(_itoa(m_nextPlanetWatcherPort++,buffer,10));
options += " watcherServicePort=";
options += buffer;
static unsigned int portBase = 0;
IGNORE_RETURN(_itoa(portBase,buffer,10));
portBase += 100;
options += " gameServerDebuggingPortBase=";
options += buffer;
TaskSpawnProcess spawn(host.empty() ? std::string("any") : host, "PlanetServer", options, spawnDelay);
CentralServer::getInstance().sendTaskMessage(spawn);
m_pendingPlanetServers[sceneId] = std::make_pair(std::make_pair(host, options), ::time(nullptr));
IGNORE_RETURN(m_planetsWaitingForPreload.insert(sceneId));
bool const preloadFinished = isPreloadFinished();
if (preloadFinished)
m_timeClusterWentIntoLoadingState = 0;
else if (m_timeClusterWentIntoLoadingState <= 0)
m_timeClusterWentIntoLoadingState = time(0);
if(getInstance().m_transferServerConnection != nullptr && !preloadFinished)
{
getInstance().m_transferServerConnection->disconnect();
getInstance().m_transferServerConnection = 0;
s_retryTransferServerConnection = false;
}
if(getInstance().m_stationPlayersCollectorConnection != nullptr && ! isPreloadFinished())
{
getInstance().m_stationPlayersCollectorConnection->disconnect();
getInstance().m_stationPlayersCollectorConnection = 0;
s_retryStationPlayersCollectorConnection = false;
}
PreloadFinishedMessage msg(preloadFinished);
sendToAllLoginServers(msg);
}
}
}
//-----------------------------------------------------------------------
/**
* Forward the RequestGameServerForLoginMessage to the appropriate PlanetServer
*/
void CentralServer::handleRequestGameServerForLoginMessage(const RequestGameServerForLoginMessage & msg)
{
static const std::string loginTrace("TRACE_LOGIN");
std::string effectiveScene;
PlayerSceneMapType::iterator i=m_playerSceneMap.find(msg.getCharacterId());
if (i!=m_playerSceneMap.end())
{
effectiveScene=i->second.first;
LOG(loginTrace, ("using sceneId (%s) from memory for character (%s)", effectiveScene.c_str(), msg.getCharacterId().getValueString().c_str()));
}
else
effectiveScene=msg.getScene(); // only use the DB scene if we don't have more recent information
// for CTS, the source character must currently be on one of the 10 original ground planets
if (msg.getForCtsSourceCharacter())
{
static std::set<std::string> s_tenOriginalGroundPlanets;
if (s_tenOriginalGroundPlanets.empty())
{
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("tatooine"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("naboo"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("corellia"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("rori"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("talus"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("yavin4"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("endor"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("lok"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("dantooine"));
IGNORE_RETURN(s_tenOriginalGroundPlanets.insert("dathomir"));
}
if (s_tenOriginalGroundPlanets.count(effectiveScene) <= 0)
{
// fail CTS because source character is not on one of the 10 original ground planets
LOG("CustomerService", ("CharacterTransfer: RequestGameServerForLoginMessage failed for source character (%s) stationId (%lu) because effective scene for the character (%s) is not one of the 10 original ground planets", msg.getCharacterId().getValueString().c_str(), msg.getStationId(), effectiveScene.c_str()));
GenericValueTypeMessage<std::pair<NetworkId, unsigned int> > const failureMsg("CtsSrcCharWrongPlanet", std::make_pair(msg.getCharacterId(), msg.getStationId()));
IGNORE_RETURN(ConnectionServerConnection::sendToPseudoClientConnection(msg.getStationId(), failureMsg));
return;
}
}
PlanetServerConnection *conn = PlanetManager::getPlanetServerForScene(effectiveScene);
if (conn)
{
LOG(loginTrace, ("handling RequestGameServerForLoginMessage(%s)", msg.getCharacterId().getValueString().c_str()));
conn->send(msg,true);
}
else
{
bool isPlanetValid = false;
bool allowDynamicStart = ConfigFile::getKeyBool("CentralServer", "startPlanetsDynamically", false);
std::map<std::string, std::string>::const_iterator f = ms_sceneToHostMap.find(msg.getScene());
if(f != ms_sceneToHostMap.end())
{
isPlanetValid = true;
}
if(isPlanetValid || allowDynamicStart)
{
LOG(loginTrace, ("deferring RequestGameServerForLoginMessage(%s)", msg.getCharacterId().getValueString().c_str()));
DEBUG_REPORT_LOG(true, ("Starting planet server for login"));
startPlanetServer(getHostForScene(msg.getScene()), msg.getScene(), 0);
m_messagesWaitingForPlanetServer.push_back(Archive::ByteStream());
msg.pack(m_messagesWaitingForPlanetServer.back());
}
else
{
WARNING_STRICT_FATAL(true, ("Central received a request to start a planet (name=\"%s\") that is not in the startPlanet= list in the CentralServer config file.", msg.getScene().c_str()));
}
}
}
// ----------------------------------------------------------------------
void CentralServer::handleRequestSceneTransfer(const RequestSceneTransfer &msg)
{
PlanetServerConnection *conn = PlanetManager::getPlanetServerForScene(msg.getSceneName());
if (conn)
{
conn->send(msg,true);
}
else
{
DEBUG_REPORT_LOG(true, ("Starting planet server for login"));
startPlanetServer(getHostForScene(msg.getSceneName()), msg.getSceneName(), 0);
m_messagesWaitingForPlanetServer.push_back(Archive::ByteStream());
msg.pack(m_messagesWaitingForPlanetServer.back());
}
}
// ----------------------------------------------------------------------
/**
* Forward the GameServerForLoginMessage to the appropriate ConnectionServer
*/
void CentralServer::handleGameServerForLoginMessage(const GameServerForLoginMessage &msg)
{
ConnectionServerConnection *conn = getConnectionServerForAccount(msg.getStationId());
if (conn)
{
conn->send(msg,true);
}
else
{
if(! ConnectionServerConnection::sendToPseudoClientConnection(msg.getStationId(), msg))
{
LOG("TRACE_LOGIN", ("Trying to log account %i in, but could not determine which connection server to use.",msg.getStationId()));
}
}
}
// ----------------------------------------------------------------------
void CentralServer::handleExchangeListCreditsMessage(const ExchangeListCreditsMessage &msg)
{
LOG("Exchange", ("Central Server got exchange list credits %d.",msg.getCredits()));
if ( m_pAuctionTransferClient == nullptr )
{
// send failure packet
}
//////////////////////m_pAuctionTransferClient->addCoinToAuction( msg );
}
ConnectionServerConnection * CentralServer::getAnyConnectionServer()
{
ConnectionServerConnection * result = 0;
if(! m_connectionServerConnections.empty())
{
result = *(m_connectionServerConnections.begin());
if(result)
{
int leastPlayers = result->getPlayerCount();
std::vector<ConnectionServerConnection *>::iterator i;
for(i = m_connectionServerConnections.begin(); i != m_connectionServerConnections.end(); ++i)
{
if((*i)->getPlayerCount() < leastPlayers)
result = *i;
}
}
}
return result;
}
// ----------------------------------------------------------------------
ConnectionServerConnection * CentralServer::getConnectionServerForAccount(StationId suid)
{
ConnectionServerConnection * result = nullptr;
ConnectionServerSUIDMap::iterator i=m_accountConnectionMap.find(suid);
if (i!=m_accountConnectionMap.end())
{
result = (*i).second;
}
else
{
result = ConnectionServerConnection::getConnectionForAccount(suid);
}
return result;
}
// ----------------------------------------------------------------------
void CentralServer::addToAccountConnectionMap(StationId suid, ConnectionServerConnection * cconn, uint32 subscriptionBits)
{
m_accountConnectionMap[suid] = cconn;
// remove corresponding "pseudo client connection"
ConnectionServerConnection::removeFromAccountConnectionMap(suid);
}
// ----------------------------------------------------------------------
void CentralServer::removeFromAccountConnectionMap(StationId suid)
{
ConnectionServerSUIDMap::iterator i=m_accountConnectionMap.find(suid);
if (i!=m_accountConnectionMap.end())
{
// Erase the entry
m_accountConnectionMap.erase(i++);
}
}
// ----------------------------------------------------------------------
void CentralServer::removeFromAccountConnectionMap(int connectionServerConnectionId)
{
ConnectionServerSUIDMap::iterator i;
for(i = m_accountConnectionMap.begin(); i != m_accountConnectionMap.end();)
{
const ConnectionServerConnection* cconn = (*i).second;
if(cconn && (cconn->getId() == connectionServerConnectionId))
{
// Erase the entry
m_accountConnectionMap.erase(i++);
}
else
{
++i;
}
}
// We could update the LoginServer population but people
// leaving the server are not as important as people connecting
// and so we will wait for update() to handle things
}
//--------------------------------------------------------------------------------
const Service * CentralServer::getChatService() const
{
return m_chatService;
}
//--------------------------------------------------------------------------------
const Service * CentralServer::getCustomerService() const
{
return m_csService;
}
//--------------------------------------------------------------------------------
const Service * CentralServer::getGameService() const
{
return m_gameService;
}
//--------------------------------------------------------------------------------
void CentralServer::startConnectionServer(int connectionServerNumber, const std::string& listenAddress, uint16 publicPort, uint16 internalPort, bool, const SpawnDelaySeconds spawnDelay) const
{
char tmp1[32];
char tmp2[32];
std::string publicString = _itoa(publicPort, tmp1, 10);
std::string privateString = _itoa(internalPort, tmp2, 10);
startConnectionServer(connectionServerNumber, listenAddress, &publicString, &privateString, spawnDelay);
}
//--------------------------------------------------------------------------------
void CentralServer::startConnectionServer(int connectionServerNumber, const std::string& listenAddress, const std::string * publicPort, const std::string * internalPort, const SpawnDelaySeconds spawnDelay) const
{
UNREF(listenAddress); //once we add dual nic support, we can use this
if(!m_connService)
{
DEBUG_WARNING(true, ("Could not start connection server because there was no service for it"));
return;
}
if (!ConfigCentralServer::getAllowZeroConnectionServerPort())
{
if (!publicPort)
{
WARNING(true, ("Could not start connection server because there were not client ports specified"));
return;
}
if (*publicPort == "0")
{
WARNING(true, ("Could not start connection server because one or more client ports were 0."));
return;
}
}
// there should already be an entry in s_connectionServerHostList that
// contains the node on which to start this particular connection server
if ((connectionServerNumber <= 0) || (connectionServerNumber > static_cast<int>(s_connectionServerHostList.size())))
{
WARNING(true, ("Could not start connection server because connectionServerNumber=%d and s_connectionServerHostList.size()=%d", connectionServerNumber, static_cast<int>(s_connectionServerHostList.size())));
return;
}
// spawn a connection server
std::string options = "-s ConnectionServer clusterName=";
options += ConfigCentralServer::getClusterName();
if(gs_connectionServersPublic)
options += " startPublicServer=true";
options += " centralServerAddress=";
options += m_connService->getBindAddress();
char servicePort[32];
options += " centralServerPort=";
options += _itoa(m_connService->getBindPort(), servicePort, 10);
if (publicPort != 0)
{
options += " clientServicePortPublic=";
options += *publicPort;
}
if (internalPort != 0)
{
options += " clientServicePortPrivate=";
options += *internalPort;
}
options += " connectionServerNumber=";
options += _itoa(connectionServerNumber, servicePort, 10);
DEBUG_REPORT_LOG(true, ("Spawning Connection server with options %s\n", options.c_str()));
TaskSpawnProcess p(s_connectionServerHostList[connectionServerNumber-1], "ConnectionServer", options, spawnDelay);
CentralServer::getInstance().sendTaskMessage(p);
}
// ----------------------------------------------------------------------
void CentralServer::sendToAllLoginServers(const GameNetworkMessage &message)
{
for (LoginServerConnectionMapType::iterator i=m_loginServerConnectionMap.begin(); i!=m_loginServerConnectionMap.end(); ++i)
i->second->send(message,true);
}
// ----------------------------------------------------------------------
uint32 CentralServer::sendToArbitraryLoginServer(const GameNetworkMessage &message, bool roundRobin /*= true*/)
{
static unsigned int nextServer = 0;
if (m_loginServerConnectionMap.empty())
return 0;
LoginServerConnectionMapType::const_iterator i = m_loginServerConnectionMap.begin();
if (!roundRobin)
{
i->second->send(message,true);
return i->second->getProcessId();
}
++nextServer;
if (nextServer >= m_loginServerConnectionMap.size())
nextServer = 0;
std::advance(i, nextServer);
i->second->send(message,true);
return i->second->getProcessId();
}
// ----------------------------------------------------------------------
/**
* In development mode, connect to a login server.
*/
void CentralServer::connectToLoginServer() const
{
IGNORE_RETURN(new LoginServerConnection(ConfigCentralServer::getLoginServerAddress(), ConfigCentralServer::getLoginServerPort()));
}
//-----------------------------------------------------------------------
void CentralServer::remove()
{
CentralServer & cs = getInstance();
MetricsManager::remove();
delete cs.m_metricsData;
cs.m_metricsData = 0;
delete cs.m_loginServerKeys;
cs.m_messagesWaitingForPlanetServer.clear();
cs.m_gameServerConnections.clear();
cs.m_gameServerConnectionsList.clear();
cs.m_connectionServerConnections.clear();
delete cs.m_gameService;
delete cs.m_chatService;
delete cs.m_csService;
delete cs.m_connService;
delete cs.m_planetService;
delete cs.m_loginService;
if(cs.m_taskManager)
{
cs.m_taskManager->setDisconnectReason("CentralServer::remove");
cs.m_taskManager->disconnect();
}
delete cs.m_taskService;
delete cs.m_consoleService;
}
//-----------------------------------------------------------------------
std::vector<const GameServerConnection *> CentralServer::getGameServers() const
{
std::vector<const GameServerConnection *> result;
std::map<uint32, GameServerConnection *>::const_iterator i;
for(i = m_gameServerConnections.begin(); i != m_gameServerConnections.end(); ++i)
{
result.push_back((*i).second);
}
return result;
}
// ----------------------------------------------------------------------
void CentralServer::sendToConnectionServerForAccount(StationId account, const GameNetworkMessage & message, const bool reliable)
{
ConnectionServerConnection *conn = getConnectionServerForAccount(account);
if (conn)
conn->send(message,reliable);
}
// ----------------------------------------------------------------------
uint32 CentralServer::sendToRandomGameServer(const GameNetworkMessage &message)
{
GameServerConnection *conn = getRandomGameServer();
if (conn)
{
conn->send(message,true);
return conn->getProcessId();
}
else
return 0;
}
// ----------------------------------------------------------------------
void CentralServer::doServerPings()
{
for (std::set<uint32>::iterator i=m_serverPings.begin(); i!=m_serverPings.end(); ++i)
{
LOG("CentralServerPings",("Dropping server %lu because it hasn't responded to CentralPingMessage",*i));
GameServerConnection *conn=getGameServer(*i);
if (conn)
{
// tell task manager to kill the offending process (in case it's hung)
ExcommunicateGameServerMessage msg(*i, conn->getOsProcessId(), conn->getRemoteAddress());
// disconnect, if it's not hung, it should die
conn->setDisconnectReason("CentralServer::doServerPings, failure to respond to CentralPingMessage");
conn->disconnect();
sendTaskMessage(msg);
excommunicateServer(msg);
}
else
{
LOG("CentralServerPings",("Didn't have connection to %lu to drop",*i));
ExcommunicateGameServerMessage const excommunicateMessage(*i, 0, "");
excommunicateServer(excommunicateMessage); // haven't received a reply (see CentralPingMessage, above)
}
}
m_serverPings.clear();
CentralPingMessage ping;
for (std::map<uint32, GameServerConnection *>::iterator j=m_gameServerConnections.begin(); j!=m_gameServerConnections.end(); ++j)
{
if (j->first != getDbProcessServerProcessId())
{
LOG("CentralServerPings",("Pinging %lu",j->first));
IGNORE_RETURN(m_serverPings.insert(j->first));
j->second->send(ping,true);
}
}
}
// ----------------------------------------------------------------------
/**
* Declare a game server anathema. All other servers will shun this server.
*/
void CentralServer::excommunicateServer(const ExcommunicateGameServerMessage & msg)
{
sendToAllGameServers(msg,true);
sendToAllPlanetServers(msg,true);
sendToAllConnectionServers(msg,true);
}
//-----------------------------------------------------------------------
void CentralServer::startShutdownProcess(const uint32 timeToShutdown, const uint32 maxTime, const Unicode::String &systemMessage)
{
// sanity checking
if ( m_shutdownPhase > 6)
{
WARNING("CentralServerShutdown", ("startShutdownProcess: invalid value in CentralServer::m_shutdownPhase. Current value is %d. Resetting.", m_shutdownPhase));
m_shutdownPhase = 0;
}
// server already in the process of shutting down
if ( m_shutdownPhase > 0 )
{
LOG("CentralServerShutdown", ("startShutdownProcess: Server already in the process of shutting down. Current shutdown phase is %d.", m_shutdownPhase));
return;
}
if( timeToShutdown>maxTime )
{
LOG("CentralServerShutdown", ("startShutdownProcess: time to shutdown is greater than max time."));
return;
}
m_shutdownPhase = 1;
LOG("CentralServerShutdown",("Shutdown Phase %d: Shutdown sequence starting now.", m_shutdownPhase));
LOG("CentralServerShutdown",("Shutdown Phase %d: time to shutdown=%dsec, max time to wait=%dsec, broadcast warning message=\"%s\".", m_shutdownPhase, timeToShutdown, maxTime, Unicode::wideToNarrow(systemMessage).c_str()));
// this is the time in the future that we need to start the shutdown process.
m_shutdownTotalTime = m_curTime + timeToShutdown;
m_shutdownMaxTime = m_curTime + maxTime;
m_shutdownSystemMessage = systemMessage;
m_shutdownHaveDatabaseSaveStart = false;
m_shutdownHaveDatabaseComplete = false;
checkShutdownProcess();
}
//------------------------------------------------------------------------
void CentralServer::checkShutdownProcess()
{
static bool warn30 = false;
static bool warn10 = false;
static bool warn0 = false;
static uint32 lastWarn = m_curTime;
static uint32 nextAbortCheck = m_curTime+1;
static uint32 nextShutdownCheck = m_curTime+30;
if( !m_shutdownPhase )
{
#ifndef WIN32
// check if a .shutdown file exists
if( m_curTime > nextShutdownCheck )
{
nextShutdownCheck = m_curTime + 30;
if( FileExists(".shutdown") )
{
StdioFile shutdownFile(".shutdown", "r");
if(shutdownFile.isOpen())
{
char destBuffer[64] = {"\0"};
if(shutdownFile.read(destBuffer, sizeof(destBuffer)) > 0)
{
destBuffer[sizeof(destBuffer) - 1] = 0;
int shutdownTime = atoi(destBuffer);
if(shutdownTime > 0)
{
LOG("CentralServerShutdown", ("Detected a .shutdown file. Initiating shutdown sequence."));
startShutdownProcess(shutdownTime, 7200, Unicode::narrowToWide("The server will be shutting down soon. Please find a safe place to logout."));
}
}
shutdownFile.close();
}
IGNORE_RETURN(::remove(".shutdown"));
return;
}
if( FileExists( ".startanymissingplanet") )
{
LOG("CentralServer", ("Detected a .startanymissingplanet file. Checking for any planets that are not started, and starting them."));
for (std::map<std::string, std::string>::const_iterator iter = ms_sceneToHostMap.begin(); iter != ms_sceneToHostMap.end(); ++iter)
{
if (!hasPlanetServer(iter->first))
{
std::map<std::string, std::pair<std::pair<std::string, std::string>, time_t> >::iterator iterPendingPlanetServer = m_pendingPlanetServers.find(iter->first);
if (iterPendingPlanetServer != m_pendingPlanetServers.end())
{
// if it's been "awhile" since we requested to restart the PlanetServer,
// then assume that something has gone wrong, and try the restart again
time_t const timeNow = ::time(nullptr);
if ((iterPendingPlanetServer->second.second + static_cast<time_t>(ConfigCentralServer::getMaxTimeToWaitForPlanetServerStartSeconds())) < timeNow)
{
LOG("CentralServer", ("startanymissingplanet:Starting missing planet %s because it has been %s since we requested it to be started, which has exceeded the %s limit.", iter->first.c_str(), CalendarTime::convertSecondsToMS(static_cast<unsigned int>(timeNow - iterPendingPlanetServer->second.second)).c_str(), CalendarTime::convertSecondsToMS(static_cast<unsigned int>(ConfigCentralServer::getMaxTimeToWaitForPlanetServerStartSeconds())).c_str()));
iterPendingPlanetServer->second.second = timeNow;
TaskSpawnProcess spawn(iterPendingPlanetServer->second.first.first.empty() ? std::string("any") : iterPendingPlanetServer->second.first.first, "PlanetServer", iterPendingPlanetServer->second.first.second);
CentralServer::getInstance().sendTaskMessage(spawn);
}
else
{
LOG("CentralServer", ("startanymissingplanet:It's only been %s since we requested for planet %s to be started, and we must wait %s before we can start it again.", CalendarTime::convertSecondsToMS(static_cast<unsigned int>(timeNow - iterPendingPlanetServer->second.second)).c_str(), iter->first.c_str(), CalendarTime::convertSecondsToMS(static_cast<unsigned int>(ConfigCentralServer::getMaxTimeToWaitForPlanetServerStartSeconds())).c_str()));
}
}
else
{
LOG("CentralServer", ("startanymissingplanet:Hmmm... planet %s is missing, but also is not in the list of planets we have attempted to start, so we're ***NOT*** going to start it.", iter->first.c_str()));
}
}
}
IGNORE_RETURN(::remove( ".startanymissingplanet" ));
return;
}
if( FileExists( ".startanymissinggameserver") )
{
LOG("CentralServer", ("Detected a .startanymissinggameserver file. Checking for any game servers that are not started, and starting them."));
const GenericValueTypeMessage<uint8> startAnyMissingGameServer("SAMGS", 0);
sendToAllPlanetServers(startAnyMissingGameServer, true);
IGNORE_RETURN(::remove( ".startanymissinggameserver" ));
return;
}
}
#endif
return;
}
// should we abort?
if( m_curTime > nextAbortCheck )
{
#ifndef WIN32
nextAbortCheck = m_curTime + 1;
if( FileExists(".abortshutdown") )
{
IGNORE_RETURN(::remove(".abortshutdown"));
LOG("CentralServerShutdown", ("Shutdown Phase %d: Detected a .abortshutdown file. Aborting shutdown sequence.", m_shutdownPhase));
abortShutdownProcess();
return;
}
#endif
}
// Phase 1 is an immediate broadcast to players for the first shutdown warning
if( m_shutdownPhase == 1 )
{
char strTimeLeft[1024];
uint32 timeLeft = m_shutdownTotalTime - m_curTime;
if( timeLeft < 1 )
{
strTimeLeft[0] = '\0';
}
else if( timeLeft < 60 )
{
sprintf(strTimeLeft, " ( %lusec left )", timeLeft);
}
else
{
sprintf(strTimeLeft, " ( %lumin left )", timeLeft/60);
}
LOG("CentralServerShutdown",("Shutdown Phase %d: Broadcasting first shutdown message to players: \"%s %s\"", m_shutdownPhase, Unicode::wideToNarrow(m_shutdownSystemMessage).c_str(), strTimeLeft));
ConGenericMessage const msg("game any systemMessage " + Unicode::wideToNarrow(m_shutdownSystemMessage)+strTimeLeft, 0);
IGNORE_RETURN(sendToRandomGameServer(msg));
m_shutdownPhase = 2;
lastWarn = m_curTime;
}
// Phase 2 is broadcasting a system message every 60sec while we wait for time to expire
else if( m_shutdownPhase == 2 )
{
if( m_curTime >= m_shutdownTotalTime)
{
LOG("CentralServerShutdown",("Shutdown Phase %d: Done broadcasting shutdown warning message to players . Advancing shutdown phase.", m_shutdownPhase));
m_shutdownPhase = 3;
warn30 = false;
warn10 = false;
warn0 = false;
}
// broadcast warning message to players every 60sec.
else if( m_curTime >= lastWarn+60 )
{
char strTimeLeft[1024];
uint32 timeLeft = m_shutdownTotalTime - m_curTime;
if( timeLeft < 1 )
{
strTimeLeft[0] = '\0';
}
else if( timeLeft < 60 )
{
sprintf(strTimeLeft, " ( %lusec left)", timeLeft);
}
else
{
sprintf(strTimeLeft, " ( %lumin left)", timeLeft/60);
}
LOG("CentralServerShutdown",("Shutdown Phase %d: Broadcasting shutdown message to players: \"%s %s\"", m_shutdownPhase, Unicode::wideToNarrow(m_shutdownSystemMessage).c_str(), strTimeLeft));
ConGenericMessage const msg("game any systemMessage " + Unicode::wideToNarrow(m_shutdownSystemMessage)+strTimeLeft, 0);
IGNORE_RETURN(sendToRandomGameServer(msg));
lastWarn = m_curTime;
}
}
// warn the players they are about to be disconnected
else if( m_shutdownPhase == 3 )
{
if( m_curTime>=m_shutdownTotalTime && !warn30 )
{
LOG("CentralServerShutdown",("Shutdown Phase %d: Broadcasting 30sec disconnect warning message to players.", m_shutdownPhase));
ConGenericMessage const msg("game any systemMessage You will be disconnected in 30sec so the server can perform a final save before shutting down. Please find a safe place to logout now.", 0);
IGNORE_RETURN(sendToRandomGameServer(msg));
warn30 = true;
}
else if( m_curTime >= (m_shutdownTotalTime+20) && !warn10 )
{
LOG("CentralServerShutdown",("Shutdown Phase %d: Broadcasting 10sec disconnect warning message to players.", m_shutdownPhase));
ConGenericMessage const msg("game any systemMessage You will be disconnected in 10sec so the server can perform a final save before shutting down. Please find a safe place to logout now.", 0);
IGNORE_RETURN(sendToRandomGameServer(msg));
warn10 = true;
}
else if( m_curTime >= (m_shutdownTotalTime+30) && !warn0 )
{
LOG("CentralServerShutdown",("Shutdown Phase %d: Broadcasting final disconnect warning message to players.", m_shutdownPhase));
ConGenericMessage const msg("game any systemMessage You will now be disconnected so the server can perform a final save before shutting down.", 0);
IGNORE_RETURN(sendToRandomGameServer(msg));
warn0 = true;
}
else if( m_curTime >= (m_shutdownTotalTime+35) )
{
LOG("CentralServerShutdown",("Shutdown Phase %d: Setting server to private, disconnecting the players and now waiting for next database save cycle to begin.", m_shutdownPhase));
gs_connectionServersPublic = false;
SetConnectionServerPublic const msg(false);
sendToAllConnectionServers(msg, true);
m_shutdownPhase = 4;
// send a message to the DB telling it we're shutting down
GenericValueTypeMessage<bool> const msg2("ClusterShutdownMessage", true);
sendToDBProcess(msg2, true);
// send a save start to the DB; must be sent after the above
// "ClusterShutdownMessage" message so the DB will know this
// is the final save before cluster shutdown
GenericValueTypeMessage<NetworkId> const msg3("StartSaveMessage", NetworkId::cms_invalid);
sendToDBProcess(msg3, true);
}
}
else if( m_shutdownPhase == 4 )
{
if( m_shutdownHaveDatabaseSaveStart )
{
LOG("CentralServerShutdown",("Shutdown Phase %d: Starting final database save cycle. Advancing shutdown phase.", m_shutdownPhase));
m_shutdownPhase = 5;
}
}
else if( m_shutdownPhase == 5 )
{
// we are done. Shut it down. Shut it down now!
if( m_shutdownHaveDatabaseComplete)
{
LOG("CentralServerShutdown",("Shutdown Phase %d: Completed final database save cycle. Advancing shutdown phase.", m_shutdownPhase));
m_shutdownPhase = 6;
LOG("CentralServerShutdown",("Shutdown Phase %d: Instructing TaskManager to shutdown the cluster without restarting.", m_shutdownPhase));
ConGenericMessage const msg("stop", 0);
sendTaskMessage(msg);
}
}
// maximum amount of time to wait has expired. Shut it down now without waiting for anything else.
else if( m_curTime >= m_shutdownMaxTime )
{
LOG("CentralServerShutdown",("Shutdown Phase %d: Maximum time has gone by. Forcing a shutdown now.", m_shutdownPhase));
m_shutdownPhase = 6;
LOG("CentralServerShutdown",("Shutdown Phase %d: Instructing TaskManager to shutdown the cluster without restarting.", m_shutdownPhase));
ConGenericMessage const msg("stop", 0);
sendTaskMessage(msg);
}
}
void CentralServer::abortShutdownProcess()
{
LOG("CentralServerShutdown", ("Shutdown Phase %d: Aborting shutdown sequence.", m_shutdownPhase));
if( m_shutdownPhase > 0 )
{
ConGenericMessage const msg("game any systemMessage The server shutdown has been aborted. The server is no longer shutting down.", 0);
IGNORE_RETURN(sendToRandomGameServer(msg));
}
// send a message to the DB telling it we're no longer shutting down
GenericValueTypeMessage<bool> const msg("ClusterShutdownMessage", false);
sendToDBProcess(msg, true);
m_shutdownPhase = 0;
m_shutdownTotalTime = 0;
m_shutdownMaxTime = 0;
m_shutdownSystemMessage = Unicode::narrowToWide("");
m_shutdownHaveDatabaseSaveStart = false;
m_shutdownHaveDatabaseComplete = false;
}
//-----------------------------------------------------------------------
void CentralServer::sendToTransferServer(const GameNetworkMessage & msg) const
{
if(getInstance().m_transferServerConnection)
{
getInstance().m_transferServerConnection->send(msg, true);
}
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
void CentralServer::sendToStationPlayersCollector(const GameNetworkMessage & msg) const
{
if(getInstance().m_stationPlayersCollectorConnection)
{
getInstance().m_stationPlayersCollectorConnection->send(msg, true);
}
}
//-----------------------------------------------------------------------
int CentralServer::getPlanetServersCount() const
{
return m_planetServers.size();
}
//-----------------------------------------------------------------------
const Service * CentralServer::getCommoditiesService()
{
return s_commodityServerService;
}
//-----------------------------------------------------------------------
int CentralServer::getClusterStartupTime() const
{
if (m_clusterStartupTime != -1)
return m_clusterStartupTime;
else
// return how long the cluster has been taking to come up
return int((time(0) - m_timeClusterStarted) / 60);
}
//-----------------------------------------------------------------------
int CentralServer::getSecondsClusterHasBeenInLoadingState() const
{
if (m_timeClusterWentIntoLoadingState > 0)
return (time(0) - m_timeClusterWentIntoLoadingState);
else
return 0;
}
// ----------------------------------------------------------------------
const std::map<std::string, int> & CentralServer::getPopulationStatistics(time_t & refreshTime)
{
// periodically request updated statistics from the game server
time_t const timeNow = ::time(nullptr);
if (m_timePopulationStatisticsNextRefresh <= timeNow)
{
GameServerConnection * universeGameServerConnection = getGameServer(UniverseManager::getInstance().getUniverseProcess());
if (universeGameServerConnection)
{
const GenericValueTypeMessage<uint8> populationStatisticsRequest("PopStatReq", 0);
universeGameServerConnection->send(populationStatisticsRequest, true);
m_timePopulationStatisticsNextRefresh = timeNow + 60;
}
}
refreshTime = m_timePopulationStatisticsRefresh;
return m_populationStatistics;
}
// ----------------------------------------------------------------------
const std::map<std::string, std::pair<int, std::pair<std::string, std::string> > > & CentralServer::getGcwScoreStatistics(time_t & refreshTime)
{
// periodically request updated statistics from the game server
time_t const timeNow = ::time(nullptr);
if (m_timeGcwScoreStatisticsNextRefresh <= timeNow)
{
GameServerConnection * universeGameServerConnection = getGameServer(UniverseManager::getInstance().getUniverseProcess());
if (universeGameServerConnection)
{
const GenericValueTypeMessage<uint8> gcwScoreStatisticsRequest("GcwScoreStatReq", 0);
universeGameServerConnection->send(gcwScoreStatisticsRequest, true);
m_timeGcwScoreStatisticsNextRefresh = timeNow + 60;
}
}
refreshTime = m_timeGcwScoreStatisticsRefresh;
return m_gcwScoreStatistics;
}
// ----------------------------------------------------------------------
std::pair<std::map<int, std::pair<std::string, int> > const *, std::map<int, std::pair<std::string, int> > const *> CentralServer::getLastLoginTimeStatistics(time_t & refreshTime)
{
// periodically request updated statistics from the game server
time_t const timeNow = ::time(nullptr);
if (m_timeLastLoginTimeStatisticsNextRefresh <= timeNow)
{
GameServerConnection * universeGameServerConnection = getGameServer(UniverseManager::getInstance().getUniverseProcess());
if (universeGameServerConnection)
{
const GenericValueTypeMessage<uint8> lastLoginTimeStatisticsRequest("LLTStatReq", 0);
universeGameServerConnection->send(lastLoginTimeStatisticsRequest, true);
m_timeLastLoginTimeStatisticsNextRefresh = timeNow + (15 * 60);
}
}
refreshTime = m_timeLastLoginTimeStatisticsRefresh;
return std::make_pair(&m_lastLoginTimeStatistics, &m_createTimeStatistics);
}
// ----------------------------------------------------------------------
void CentralServer::getCharacterMatchStatistics(int & numberOfCharacterMatchRequests, int & numberOfCharacterMatchResultsPerRequest, int & timeSpentPerCharacterMatchRequestMs)
{
// periodically request updated statistics from the game server
time_t const timeNow = ::time(nullptr);
if (m_timeCharacterMatchStatisticsNextRefresh <= timeNow)
{
const GenericValueTypeMessage<uint8> characterMatchStatisticsRequest("LfgStatReq", 0);
sendToAllGameServersExceptDBProcess(characterMatchStatisticsRequest, true);
m_timeCharacterMatchStatisticsNextRefresh = timeNow + 60;
}
// wraparound, reset statistics
if (m_numberOfCharacterMatchRequests < 0)
{
m_numberOfCharacterMatchRequests = 0;
m_numberOfCharacterMatchResults = 0;
m_timeSpentOnCharacterMatchRequestsMs = 0;
}
numberOfCharacterMatchRequests = m_numberOfCharacterMatchRequests;
numberOfCharacterMatchResultsPerRequest = (m_numberOfCharacterMatchRequests ? static_cast<int>(m_numberOfCharacterMatchResults / m_numberOfCharacterMatchRequests) : 0);
timeSpentPerCharacterMatchRequestMs = (m_numberOfCharacterMatchRequests ? static_cast<int>(m_timeSpentOnCharacterMatchRequestsMs / m_numberOfCharacterMatchRequests) : 0);
}
// ======================================================================
//lint -e766 // todo : TRF Fix this