Files
src-1.2/engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp
T

1620 lines
53 KiB
C++
Executable File

// ConnectionServer.cpp
// copyright 2001 Verant Interactive
//-----------------------------------------------------------------------
#include "FirstConnectionServer.h"
#include "ConnectionServer.h"
#include "Archive/ByteStream.h"
#include "CentralConnection.h"
#include "ChatServerConnection.h"
#include "ClientConnection.h"
#include "ConfigConnectionServer.h"
#include "ConnectionServerMetricsData.h"
#include "CustomerServiceConnection.h"
#include "GameConnection.h"
#include "PseudoClientConnection.h"
#include "SessionApiClient.h"
#include "UdpLibrary.h"
#include "UnicodeUtils.h"
#include "serverKeyShare/KeyShare.h"
#include "serverMetrics/MetricsManager.h"
#include "serverNetworkMessages/CentralConnectionServerMessages.h"
#include "serverNetworkMessages/CharacterListMessage.h"
#include "serverNetworkMessages/ExcommunicateGameServerMessage.h"
#include "serverNetworkMessages/GameConnectionServerMessages.h"
#include "serverNetworkMessages/GameServerForLoginMessage.h"
#include "serverNetworkMessages/LoginKeyPush.h"
#include "serverNetworkMessages/NewClient.h"
#include "serverNetworkMessages/ProfilerOperationMessage.h"
#include "serverNetworkMessages/RandomName.h"
#include "serverNetworkMessages/SetConnectionServerPublic.h"
#include "serverNetworkMessages/UpdateConnectionServerStatus.h"
#include "serverNetworkMessages/UpdatePlayerCountMessage.h"
#include "serverNetworkMessages/ValidateAccountReplyMessage.h"
#include "serverNetworkMessages/ValidateCharacterForLoginReplyMessage.h"
#include "serverNetworkMessages/VerifyAndLockName.h"
#include "serverUtility/AdminAccountManager.h"
#include "sharedDebug/Profiler.h"
#include "sharedFoundation/Clock.h"
#include "sharedFoundation/ConfigFile.h"
#include "sharedFoundation/Os.h"
#include "sharedFoundation/Timer.h"
#include "sharedGame/PlatformFeatureBits.h"
#include "sharedLog/Log.h"
#include "sharedLog/LogManager.h"
#include "sharedLog/SetupSharedLog.h"
#include "sharedNetwork/NetworkSetupData.h"
#include "sharedNetwork/Service.h"
#include "sharedNetwork/UdpSock.h"
#include "sharedNetworkMessages/ClientCentralMessages.h"
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
#include "sharedUtility/DataTableManager.h"
#include <vector>
#include "sharedFoundation/CrcConstexpr.hpp"
// ======================================================================
namespace ConnectionServerNamespace
{
ConnectionServer * s_connectionServer = 0;
NetworkSetupData * s_clientServiceSetup = 0;
const std::string SCENE_NAME_TUTORIAL = "tutorial";
const std::string SCENE_NAME_FALCON_PREFIX = "space_npe_falcon";
};
using namespace ConnectionServerNamespace;
// ======================================================================
ConnectionServer & ConnectionServer::instance()
{
return *s_connectionServer;
}
//-----------------------------------------------------------------------
ConnectionServer::ConnectionServer() :
MessageDispatch::Receiver(),
chatService(0),
customerService(0),
clientServicePrivate(0),
clientServicePublic(0),
gameService(0),
loginServerKeys(0),
done(false),
m_id(0),
m_metricsData(0),
centralConnection(0),
chatServers(),
customerServiceServers(),
clientMap(),
connectedMap(),
gameServerMap(),
freeTrials(),
networkBarrier(0),
pingSocket(new UdpSock),
m_recoverTime(0),
m_sessionApiClient(0),
m_pingTrafficNumBytes(0),
m_recoveringClientList()
{
if (s_clientServiceSetup == 0)
s_clientServiceSetup = new NetworkSetupData;
s_clientServiceSetup->maxOutstandingPackets = ConfigConnectionServer::getClientMaxOutstandingPackets();
s_clientServiceSetup->maxRawPacketSize = ConfigConnectionServer::getClientMaxRawPacketSize();
s_clientServiceSetup->maxConnections = ConfigConnectionServer::getClientMaxConnections();
s_clientServiceSetup->fragmentSize = ConfigConnectionServer::getClientFragmentSize();
s_clientServiceSetup->maxDataHoldTime = ConfigConnectionServer::getClientMaxDataHoldTime();
s_clientServiceSetup->hashTableSize = ConfigConnectionServer::getClientHashTableSize();
s_clientServiceSetup->port = ConfigConnectionServer::getClientServicePortPublic();
s_clientServiceSetup->maxConnectionsPerIP = ConfigConnectionServer::getMaxConnectionsPerIP();
s_clientServiceSetup->compress = ConfigConnectionServer::getCompressClientNetworkTraffic();
s_clientServiceSetup->useTcp = false;
loginServerKeys = new KeyServer(20);
Address a("", ConfigConnectionServer::getPingPort());
IGNORE_RETURN(pingSocket->bind(a));
if (ConfigConnectionServer::getValidateStationKey())
{
installSessionValidation();
}
}
//-----------------------------------------------------------------------
ConnectionServer::~ConnectionServer()
{
delete pingSocket;
pingSocket = 0;
delete loginServerKeys;
loginServerKeys = 0;
centralConnection = 0;
chatServers.clear();
customerServiceServers.clear();
connectedMap.clear();
clientServicePublic = 0;
clientServicePrivate = 0;
chatService = 0;
customerService = 0;
gameService = 0;
gameServerMap.clear();
freeTrials.clear();
delete s_clientServiceSetup;
}
//-----------------------------------------------------------------------
const CustomerServiceConnection * ConnectionServer::getCustomerServiceConnection()
{
if (!instance().customerServiceServers.empty())
{
return (*(instance().customerServiceServers.begin()));
}
return nullptr;
}
//-----------------------------------------------------------------------
//TODO: This assumes that all characters that aren't on a game server
// are waiting for this gameserver. This assumption is bad: they could
// be waiting on a different gameserver, or they could be in the process
// of being validated
void ConnectionServer::addGameConnection(unsigned long gameServerId, GameConnection* gc)
{
static ConnectionServer & cs = instance();
cs.gameServerMap[gameServerId] = gc;//@todo check for dupe
// find characters pending for THIS gameserver
SuidMap::iterator i;
for (i = cs.connectedMap.begin(); i != cs.connectedMap.end(); ++i)
{
ClientConnection * c = (*i).second;
if (!c->getHasBeenSentToGameServer())
IGNORE_RETURN(c->sendToGameServer());
}
}
//-----------------------------------------------------------------------
bool ConnectionServer::decryptToken(const KeyShare::Token & token, uint32 & stationUserId, bool & secure, std::string & accountName)
{
static ConnectionServer & cs = instance();
//Also the sizeof(int) is likewise magic from the session api
uint32 len = sizeof(uint32) + sizeof(bool) + MAX_ACCOUNT_NAME_LENGTH + 1;
unsigned char * keyBuffer = new unsigned char[len];
unsigned char * keyBufferPointer = keyBuffer;
memset(keyBuffer, 0, len);
bool retval = cs.loginServerKeys->decipherToken(token, keyBuffer, len);
if (!retval)
return retval;
char *tmpBuffer = new char[MAX_ACCOUNT_NAME_LENGTH + 1];
memset(tmpBuffer, 0, MAX_ACCOUNT_NAME_LENGTH + 1);
memcpy(&stationUserId, keyBufferPointer, sizeof(uint32));
keyBufferPointer += sizeof(uint32);
memcpy(&secure, keyBufferPointer, sizeof(bool));
keyBufferPointer += sizeof(bool);
memcpy(tmpBuffer, keyBufferPointer, MAX_ACCOUNT_NAME_LENGTH);
accountName = tmpBuffer;
delete[] tmpBuffer;
delete[] keyBuffer;
return retval;
}
bool ConnectionServer::decryptToken(const KeyShare::Token & token, char* sessionKey, StationId & stationId)
{
static ConnectionServer & cs = instance();
uint32 len = apiSessionIdWidth + sizeof(StationId);
unsigned char * keyBuffer = new unsigned char[len + 1];
unsigned char * keyBufferPointer = keyBuffer;
memset(keyBuffer, 0, sizeof(*keyBuffer));
bool retval = cs.loginServerKeys->decipherToken(token, keyBuffer, len);
if (!retval)
return retval;
memcpy(sessionKey, keyBufferPointer, len);
keyBufferPointer += apiSessionIdWidth;
memcpy(&stationId, keyBufferPointer, sizeof(StationId));
delete[] keyBuffer;
return retval;
}
//-----------------------------------------------------------------------
const Service * ConnectionServer::getChatService()
{
static ConnectionServer & cs = instance();
return cs.chatService;
}
//-----------------------------------------------------------------------
const Service * ConnectionServer::getCustomerService()
{
static ConnectionServer & cs = instance();
return cs.customerService;
}
//-----------------------------------------------------------------------
Service * ConnectionServer::getClientServicePrivate()
{
static ConnectionServer & cs = instance();
return cs.clientServicePrivate;
}
//-----------------------------------------------------------------------
Service * ConnectionServer::getClientServicePublic()
{
static ConnectionServer & cs = instance();
return cs.clientServicePublic;
}
//-----------------------------------------------------------------------
KeyShare::Token ConnectionServer::makeToken(const unsigned char * newData, const uint32 dataLen)
{
static ConnectionServer & cs = instance();
return cs.loginServerKeys->makeToken(newData, dataLen);
}
//-----------------------------------------------------------------------
void ConnectionServer::pushKey(const KeyShare::Key & newKey)
{
static ConnectionServer & cs = instance();
cs.loginServerKeys->pushKey(newKey);
}
//-----------------------------------------------------------------------
void ConnectionServer::addNewClient(ClientConnection* cconn, const NetworkId &oid, GameConnection* gconn, const std::string &, bool sendToStarport)
{
static ConnectionServer & cs = instance();
ClientMap::iterator i = cs.clientMap.find(oid);
if (i != cs.clientMap.end())
{
if (cconn->getClient())
{
WARNING_STRICT_FATAL(true, ("Client already connected, attempting to drop old one\n"));
dropClient(cconn, "Duplicate Login");
return;
}
else
{
// stale connection in map
cs.removeFromClientMap(oid);
}
}
// Create a new entry in the client map
cs.addToClientMap(oid, cconn);
// Get the client that was just created
Client * newClient = cs.getClient(oid);
NOT_NULL(newClient);
// select a chat server connection for the client
// if non exists, then the client will be notified when
// one starts
if (!cs.chatServers.empty())
{
//ChatServerConnection * c = (*chatServers.begin());
// find chat server with least load
size_t max = 0xFFFFFFFF;
ChatServerConnection * candidate = 0;
std::set<ChatServerConnection *>::const_iterator iter;
for (iter = cs.chatServers.begin(); iter != cs.chatServers.end(); ++iter)
{
if ((*iter)->getClients().size() <= max)
{
candidate = (*iter);
max = (*iter)->getClients().size();
}
}
if (candidate)
newClient->setChatConnection(candidate);
}
// select a cs server connection for the client
// if non exists, then the client will be notified when
// one starts
if (!cs.customerServiceServers.empty())
{
//ChatServerConnection * c = (*chatServers.begin());
// find chat server with least load
size_t max = 0xFFFFFFFF;
CustomerServiceConnection * candidate = 0;
std::set<CustomerServiceConnection *>::const_iterator iter;
for (iter = cs.customerServiceServers.begin(); iter != cs.customerServiceServers.end(); ++iter)
{
if ((*iter)->getClients().size() <= max)
{
candidate = (*iter);
max = (*iter)->getClients().size();
}
}
if (candidate)
newClient->setCustomerServiceConnection(candidate);
}
//send the game server a message about this client.
static const std::string loginTrace("TRACE_LOGIN");
LOG(loginTrace, ("NewClient(%d, %s, %s)", cconn->getSUID(), oid.getValueString().c_str(), cconn->getAccountName().c_str()));
NewClient m(oid, cconn->getAccountName(), cconn->getRemoteAddress(), cconn->getIsSecure(), false, cconn->getSUID(), nullptr, cconn->getGameFeatures(), cconn->getSubscriptionFeatures(), cconn->getEntitlementTotalTime(), cconn->getEntitlementEntitledTime(), cconn->getEntitlementTotalTimeSinceLastLogin(), cconn->getEntitlementEntitledTimeSinceLastLogin(), cconn->getBuddyPoints(), cconn->getConsumedRewardEvents(), cconn->getClaimedRewardItems(), cconn->isUsingAdminLogin(), cconn->getCanSkipTutorial(), sendToStarport);
gconn->send(m, true);
//@todo move this to ClientConnection.cpp
}
//-----------------------------------------------------------------------
void ConnectionServer::dropClient(ClientConnection * conn, const std::string& description)
{
DEBUG_FATAL(!conn, ("Cannot call dropClient with nullptr connection"));
if (!conn) //lint !e774 // boolean within 'if' always evaluates to False //suppresed because this is only relevant in DEBUG builds
return;
static ConnectionServer & cs = instance();
//Client dropped. Tell game server if they've logged in.
LOG("ClientDisconnect", ("Dropping client for SUID %d\n", conn->getSUID()));
LOG("CustomerService", ("Login:%s Dropped Reason: %s. Character: %s (%s). Play time: %s. Active play time: %s", ClientConnection::describeAccount(conn).c_str(), description.c_str(), conn->getCharacterName().c_str(), conn->getCharacterId().getValueString().c_str(), conn->getPlayTimeDuration().c_str(), conn->getActivePlayTimeDuration().c_str()));
Client *client = conn->getClient();
if (client)
{
DropClient msg(client->getNetworkId());
GameConnection* gconn = client->getGameConnection();
//Don't worry about sending a message to a non-existant game server
if (gconn)
gconn->send(msg, true);
else
DEBUG_REPORT_LOG(true, ("Could not find game server to drop this client\n"));
// Remove the entry from the client map
cs.removeFromClientMap(client->getNetworkId());
}
else
{
//If they aren't connected to the game yet, they're probably on the pending list.
//@todo ensure we don't need to send a cleanup message to central
// removePendingCharacter(conn->getSUID());
}
// Remove entry from the connected map
cs.removeFromConnectedMap(conn->getSUID());
conn->disconnect();
}
//-----------------------------------------------------------------------
// void ConnectionServer::addPendingCharacter(uint32 suid, ClientConnection* conn)
// {
// if (pendingMap.find(suid) == pendingMap.end())
// pendingMap[suid] = conn;
// else
// {
// WARNING_STRICT_FATAL(true, ("Attepting to add duplicate pending chatacter"));
// pendingMap[suid] = conn;
// }
// }
//-----------------------------------------------------------------------
void ConnectionServer::addConnectedClient(uint32 suid, ClientConnection* conn)
{
static ConnectionServer & cs = instance();
cs.addToConnectedMap(suid, conn);
}
//-----------------------------------------------------------------------
ClientConnection* ConnectionServer::getClientConnection(const uint32 suid)
{
static ConnectionServer & cs = instance();
ClientConnection * result = 0;
SuidMap::const_iterator i = cs.connectedMap.find(suid);
if (i != cs.connectedMap.end())
{
result = (*i).second;
}
return result;
}
// ----------------------------------------------------------------------
const Service * ConnectionServer::getGameService()
{
static ConnectionServer & cs = instance();
return cs.gameService;
}
// ----------------------------------------------------------------------
GameConnection* ConnectionServer::getGameConnection(const std::string &sceneName)
{
static ConnectionServer & cs = instance();
GameServerMap::iterator i = cs.gameServerMap.begin();
for (; i != cs.gameServerMap.end(); ++i)
{
if (sceneName == (*i).second->getSceneName())
{
return (*i).second;
}
}
return nullptr;
}
// ----------------------------------------------------------------------
void ConnectionServer::handleConnectionServerIdMessage(const ConnectionServerId & m)
{
// Connection established with central server. Set everything up.
m_id = m.getId();
const Service * const servicePrivate = getClientServicePrivate();
const Service * const servicePublic = getClientServicePublic();
FATAL(servicePrivate == nullptr && servicePublic == nullptr, ("No client service is active!"));
const Service * const g = getGameService();
FATAL(g == nullptr, ("No game service is active!"));
const Service * const c = getChatService();
const Service * const cs = getCustomerService();
const uint16 pingPort = getPingPort();
if ((servicePrivate != nullptr || servicePublic != nullptr) && g != nullptr) //lint !e774 // always evaluates to false // suppresed because the FATAL macro triggers this lint warning
{
uint16 chatPort = 0;
uint16 csPort = 0;
if (c)
{
chatPort = c->getBindPort();
}
if (cs)
{
csPort = cs->getBindPort();
}
uint16 publicPort = 0;
if (servicePublic)
{
publicPort = servicePublic->getBindPort();
}
uint16 privatePort = 0;
if (servicePrivate)
{
privatePort = servicePrivate->getBindPort();
}
std::string clientServicePublicBindAddress = ConfigConnectionServer::getPublicBindAddress();
if (clientServicePublicBindAddress.empty())
{
clientServicePublicBindAddress = NetworkHandler::getHostName();
}
NOT_NULL(gameService);
NOT_NULL(chatService);
NOT_NULL(customerService);
const NewCentralConnectionServer ncs(gameService->getBindAddress(), clientServicePublicBindAddress, chatService->getBindAddress(), customerService->getBindAddress(), privatePort, publicPort, g->getBindPort(), chatPort, csPort, pingPort, ConfigConnectionServer::getConnectionServerNumber());
sendToCentralProcess(ncs);
}
else
{
FATAL(true, ("Error in connection server startup"));
}
}
// ----------------------------------------------------------------------
void ConnectionServer::receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message)
{
// it's reasonably safe to cast, message type verified
// determine message type
const uint32 messageType = message.getType();
switch(messageType) {
case constcrc("GameConnectionOpened") :
{
DEBUG_REPORT_LOG(true, ("Game Connection opened\n"));
break;
}
case constcrc("GameConnectionClosed") :
{
//@todo handle case where game server drops and we have users connected to it.
//Drop all connected clients.
const GameConnection & downConnection = static_cast<const GameConnection &>(source);
DEBUG_REPORT_LOG(true, ("Game Server connection went down. Dropping clients.\n"));
//remove Game Conection from list
PseudoClientConnection::gameConnectionClosed(&downConnection);
GameServerMap::iterator j = gameServerMap.find(downConnection.getGameServerId());
if (j != gameServerMap.end())
{
gameServerMap.erase(j);
}
break;
}
case constcrc("CentralConnectionOpened") :
{
DEBUG_REPORT_LOG(true, ("Opened connection with central\n"));
centralConnection = const_cast<CentralConnection *>(static_cast<const CentralConnection *>(&source));//lint !e826 // info: Suspiscious pointer-to-pointer conversion (area too small)
if (ConfigConnectionServer::getStartPublicServer())
{
s_clientServiceSetup->port = ConfigConnectionServer::getClientServicePortPublic();
s_clientServiceSetup->maxConnectionsPerIP = ConfigConnectionServer::getMaxConnectionsPerIP();
clientServicePublic = new Service(ConnectionAllocator<ClientConnection>(), *s_clientServiceSetup);
}
s_clientServiceSetup->port = ConfigConnectionServer::getClientServicePortPrivate();
s_clientServiceSetup->maxConnectionsPerIP = 0;
clientServicePrivate = new Service(ConnectionAllocator<ClientConnection>(), *s_clientServiceSetup);
connectToMessage("ClientConnectionOpened");
connectToMessage("ClientConnectionClosed");
break;
}
case constcrc("CentralConnectionClosed") :
{
centralConnection = const_cast<CentralConnection *>(static_cast<const CentralConnection *>(&source));//lint !e826 // info: Suspiscious pointer-to-pointer conversion (area too small)
setDone("CentralConnectionClosed: %s", centralConnection ? centralConnection->getDisconnectReason().c_str() : "");
centralConnection = 0;
DEBUG_REPORT_LOG(true, ("CentralDied. So we will too\n"));
//@todo Drop all pending clients.
break;
}
case constcrc("ClientConnectionOpened") :
{
DEBUG_REPORT_LOG(true, ("Opened connection with client\n"));
break;
}
case constcrc("ClientConnectionClosed") :
{
DEBUG_REPORT_LOG(true, ("Client is Dropping connection\n"));
ClientConnection * cconn = const_cast<ClientConnection *>(static_cast<const ClientConnection*>(&source));//lint !e826 // info: Suspiscious pointer-to-pointer conversion (area too small)
//tell CentralServer
if (centralConnection)
{
GenericValueTypeMessage<StationId> const msg("ClientConnectionClosed", cconn->getSUID());
centralConnection->send(msg, true);
}
//Client dropped. Tell game server if they've logged in.
Client *client = cconn->getClient();
if (client)
{
DropClient msg(client->getNetworkId());
GameConnection* gconn = client->getGameConnection();
//Don't worry about sending a message to a non-existant game server
if (gconn)
gconn->send(msg, true);
// Remove the entry from the client map
removeFromClientMap(client->getNetworkId());
}
else
{
// TODO: wtf is this? @Darth, fix it!
//If they aren't connected to the game yet, they're probably on the pending list.
//removePendingCharacter(cconn->getSUID());
}
removeFromConnectedMap(cconn->getSUID());
break;
}
case constcrc("ConnectionServerId") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ConnectionServerId m(ri);
handleConnectionServerIdMessage(m);
break;
}
case constcrc("ConnectionKeyPush") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ConnectionKeyPush pk(ri);
loginServerKeys->pushKey(pk.getKey());
break;
}
case constcrc("LoginKeyPush") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const LoginKeyPush k(ri);
loginServerKeys->pushKey(k.getKey());
break;
}
case constcrc("CharacterListMessage") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const CharacterListMessage msg(ri);
WARNING_STRICT_FATAL(true, ("CharacterListMessage is deprecated on the ConnectionServer -- fix whoever is sending it.\n"));
break;
}
case constcrc("ConnectionCreateCharacterSuccess") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const ConnectionCreateCharacterSuccess msg(ri);
LOG("TraceCharacterCreation", ("Received ConnectionCreateCharacterSuccess for %d", msg.getStationId()));
ClientConnection* const client = getClientConnection(msg.getStationId());
if (client)
{
const ClientCreateCharacterSuccess m(msg.getNetworkId());
client->send(m, true);
}
else
{
LOG("CustomerService", ("CharacterTransfer: Trying to deliver ConnectionCreateCharacterSuccess to PsuedoClientConnection(%d)", msg.getStationId()));
PseudoClientConnection::tryToDeliverMessageTo(static_cast<unsigned int>(msg.getStationId()), static_cast<const GameNetworkMessage &>(message).getByteStream());
}
break;
}
case constcrc("ConnectionCreateCharacterFailed") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const ConnectionCreateCharacterFailed msg(ri);
ClientConnection* client = getClientConnection(msg.getStationId());
if (client)
{
client->setHasRequestedCharacterCreate(false);
ClientCreateCharacterFailed m(msg.getName(), msg.getErrorMessage()); //lint !e1013 !e1055 !e746 (Symbol 'getErrorMessage' not a member of class 'const ConnectionCreateCharacterFailed') // supressed because it IS a member of that class. //lint !e1055 //lint !e746
client->send(m, true);
}
else
{
PseudoClientConnection::tryToDeliverMessageTo(static_cast<unsigned int>(msg.getStationId()), static_cast<const GameNetworkMessage &>(message).getByteStream());
}
break;
}
case constcrc("NewCharacterCreated") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage<StationId> const msg(ri);
ClientConnection* client = getClientConnection(msg.getValue());
if (client)
{
// don't allow this client to request another character create;
// this will forced the client to disconnect and reconnect at which time
// a check will be done (taking the newly created character into account)
// to see if the client is allowed to create another character on this account
client->setHasCreatedCharacter(true);
}
break;
}
case constcrc("RandomNameResponse") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
RandomNameResponse connMsg(ri);
ClientConnection* cconn = getClientConnection(connMsg.getStationId());
ClientRandomNameResponse cnr(connMsg.getCreatureTemplate(), connMsg.getName(), connMsg.getErrorMessage());//lint !e1013 (Symbol 'getErrorMessage' not a member of class 'RandomNameResponse') // supressed because it IS a member of that class.
if (cconn)
cconn->send(cnr, true);
break;
}
case constcrc("VerifyAndLockNameResponse") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
VerifyAndLockNameResponse connMsg(ri);
ClientConnection* cconn = getClientConnection(connMsg.getStationId());
ClientVerifyAndLockNameResponse cvalnr(connMsg.getCharacterName(), connMsg.getErrorMessage());
if (cconn)
cconn->send(cvalnr, true);
break;
}
case constcrc("ChatServerConnectionOpened") :
{
ChatServerConnection * c = const_cast<ChatServerConnection *>(static_cast<const ChatServerConnection *>(&source));//lint !e826 suspiscious pointer-to-pointer conversion // suppressed, you bet it is
IGNORE_RETURN(chatServers.insert(c));
break;
}
case constcrc("ChatServerConnectionClosed") :
{
ChatServerConnection * c = const_cast<ChatServerConnection *>(static_cast<const ChatServerConnection *>(&source));//lint !e826 suspiscious pointer-to-pointer conversion // suppressed, you bet it is
std::set<ChatServerConnection *>::iterator f = chatServers.find(c);
if (f != chatServers.end())
{
// migrate players that were on this chat server
// to another chat server
// now remove the server from the set
chatServers.erase(f);
if (!chatServers.empty())
{
std::set<ChatServerConnection *>::iterator ic = chatServers.begin();
const std::set<Client *> & clients = c->getClients();
std::set<Client *>::const_iterator i;
for (i = clients.begin(); i != clients.end(); ++i)
{
ChatServerConnection * newConn = (*ic);
Client * cl = (*i);
cl->setChatConnection(newConn);
++ic;
if (ic == chatServers.end())
ic = chatServers.begin();
}
}
else
{
const std::set<Client *> & clients = c->getClients();
std::set<Client *>::const_iterator i;
for (i = clients.begin(); i != clients.end(); ++i)
{
(*i)->setChatConnection(nullptr);
}
}
}
break;
}
case constcrc("CustomerServiceConnectionOpened") :
{
CustomerServiceConnection * c = const_cast<CustomerServiceConnection *>(static_cast<const CustomerServiceConnection *>(&source));//lint !e826 suspiscious pointer-to-pointer conversion // suppressed, you bet it is
IGNORE_RETURN(customerServiceServers.insert(c));
break;
}
case constcrc("CustomerServiceConnectionClosed") :
{
CustomerServiceConnection * c = const_cast<CustomerServiceConnection *>(static_cast<const CustomerServiceConnection *>(&source));//lint !e826 suspiscious pointer-to-pointer conversion // suppressed, you bet it is
std::set<CustomerServiceConnection *>::iterator f = customerServiceServers.find(c);
if (f != customerServiceServers.end())
{
// migrate players that were on this chat server
// to another chat server
// now remove the server from the set
customerServiceServers.erase(f);
if (!customerServiceServers.empty())
{
std::set<CustomerServiceConnection *>::iterator ic = customerServiceServers.begin();
const std::set<Client *> & clients = c->getClients();
std::set<Client *>::const_iterator i;
for (i = clients.begin(); i != clients.end(); ++i)
{
CustomerServiceConnection * newConn = (*ic);
Client * cl = (*i);
cl->setCustomerServiceConnection(newConn);
++ic;
if (ic == customerServiceServers.end())
ic = customerServiceServers.begin();
}
}
else
{
const std::set<Client *> & clients = c->getClients();
std::set<Client *>::const_iterator i;
for (i = clients.begin(); i != clients.end(); ++i)
{
(*i)->setCustomerServiceConnection(nullptr);
}
}
}
break;
}
case constcrc("GameServerForLoginMessage") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const GameServerForLoginMessage msg(ri);
ClientConnection* client = getClientConnection(msg.getStationId());
// see if the client is for the same character as the login message.
// this is required to prevent admin login via the CS Tool from
// disconnecting other characters logged in from the same account
// (but not the same character) as the character we're administratively
// logging in.
bool clientIsForSameCharacterId = false;
if (client)
{
clientIsForSameCharacterId = client->getCharacterId() == msg.getCharacterId();
}
bool handledByPseudoClient = false;
// if the character id in the message doesn't match the character id for
// the existing client, it may be for a pseudoclient. Check, and if so,
// handle there.
if (!clientIsForSameCharacterId)
{
PseudoClientConnection * pcc = PseudoClientConnection::getPseudoClientConnection(msg.getCharacterId());
// hand off to the PCC only if we do have a pseudoclient, and either we don't have a client, or
// the pseudoclient has a tool id, telling us that it's for cs tool login.
if (pcc && ((!client) || (pcc->getTransferCharacterData().getCSToolId() > 0)))
{
handledByPseudoClient = true;
bool result;
result = PseudoClientConnection::tryToDeliverMessageTo(msg.getStationId(), static_cast<const GameNetworkMessage &>(message).getByteStream());
UNREF(result);
DEBUG_REPORT_LOG(!result, ("Received GameServerForLoginMessage for %lu, who was not connected.\n", msg.getStationId()));
}
}
if (client && (!handledByPseudoClient))
{
static const std::string loginTrace("TRACE_LOGIN");
LOG(loginTrace, ("GameServerForLoginMessage(%d, %s)", client->getSUID(), client->getCharacterId().getValueString().c_str()));
client->handleGameServerForLoginMessage(msg.getServer());
}
break;
}
case constcrc("ValidateCharacterForLoginReplyMessage") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const ValidateCharacterForLoginReplyMessage msg(ri);
ClientConnection* cconn = getClientConnection(msg.getSuid());
if (!cconn)
DEBUG_REPORT_LOG(true, ("Received ValidateCharacterForLoginReplyMessage for account %lu, which is no longer connected.\n", msg.getSuid()));
else
{
cconn->onCharacterValidated(msg.getApproved(), msg.getCharacterId(), Unicode::wideToNarrow(msg.getCharacterName()), msg.getContainerId(), msg.getScene(), msg.getCoordinates());
}
break;
}
case constcrc("ValidateAccountReplyMessage") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
const ValidateAccountReplyMessage msg(ri);
ClientConnection* cconn = getClientConnection(msg.getStationId());
if (!cconn)
DEBUG_REPORT_LOG(true, ("Received ValidateAccountReplyMessage for account %lu, which is no longer connected.\n", msg.getStationId()));
else
{
cconn->onIdValidated(msg.getCanLogin(), msg.getCanCreateRegular(), msg.getCanCreateJedi(), msg.getCanSkipTutorial(), msg.getConsumedRewardEvents(), msg.getClaimedRewardItems());
}
break;
}
case constcrc("SetConnectionServerPublic") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
SetConnectionServerPublic p(ri);//lint !e40 !e522 !e10 // Undeclared identifier 'SetConnectionServerPublic' // suppressed because it IS declared
bool statusChanged = false;
DEBUG_REPORT_LOG(true, ("Conn Server: attempting to chang status\n"));
if (p.getIsPublic())//lint !e40 !e1013 !e10 // Undeclared identifier 'p' // suppressed because it IS declared
{
if (!clientServicePublic)
{
statusChanged = true;
}
}
else
{
delete clientServicePublic;
clientServicePublic = 0;
statusChanged = true;
}
if (statusChanged && centralConnection)
{
const Service * publicService = getClientServicePublic();
const Service * privateService = getClientServicePrivate();
if (publicService || privateService)
{
uint16 publicServicePort = 0;
uint16 privateServicePort = 0;
if (publicService)
{
publicServicePort = publicService->getBindPort();
}
if (privateService)
{
privateServicePort = privateService->getBindPort();
}
const UpdateConnectionServerStatus ucs(publicServicePort, privateServicePort);
centralConnection->send(ucs, true);
}
}
break;
}//lint !e529 // Symbol 'ri' not subsequently referenced // suppressed because it IS referenced. I think lint is very confused for some reason.
case constcrc("ProfilerOperationMessage") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ProfilerOperationMessage msg(ri);
unsigned int processId = msg.getProcessId();
if (!processId)
Profiler::handleOperation(msg.getOperation().c_str());
break;
}
case constcrc("ExcommunicateGameServerMessage") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
ExcommunicateGameServerMessage msg(ri);
LOG("GameGameConnect", ("Told to drop connection to %lu by Central", msg.getServerId()));
GameConnection *conn = getGameConnection(msg.getServerId());
if (conn)
conn->disconnect();
break;
}
case constcrc("CntrlSrvDropDupeConns") :
{
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
GenericValueTypeMessage<std::pair<uint32, std::string> > const msg(ri);
ClientConnection* client = getClientConnection(msg.getValue().first);
if (client && !client->isUsingAdminLogin() && !client->getIsSecure())
{
std::string s = "New Connection on galaxy ";
s += msg.getValue().second;
dropClient(client, s);
}
break;
}
}
}
//-----------------------------------------------------------------------
void ConnectionServer::install()
{
s_connectionServer = new ConnectionServer;
char tmp[128] = { "\0" };
IGNORE_RETURN(snprintf(tmp, sizeof(tmp), "ConnectionServer:%d", Os::getProcessId()));
SetupSharedLog::install(tmp);
s_connectionServer->setupConnections();
s_connectionServer->m_metricsData = new ConnectionServerMetricsData;
MetricsManager::install(s_connectionServer->m_metricsData, true, "ConnectionServer", "", ConfigConnectionServer::getConnectionServerNumber());
DataTableManager::install();
AdminAccountManager::install(ConfigConnectionServer::getAdminAccountDataTable());
}
//-----------------------------------------------------------------------
void ConnectionServer::remove()
{
MetricsManager::remove();
delete s_connectionServer->m_metricsData;
s_connectionServer->m_metricsData = 0;
// explicitly delete all connections that were setup from setupConnections rather than letting
// Connection::remove do it. There are connections that require a valid s_connectionServer which is deleted
// and set to nullptr.
s_connectionServer->unsetupConnections();
SetupSharedLog::remove();
delete s_connectionServer;
s_connectionServer = 0;
}
//-----------------------------------------------------------------------
void ConnectionServer::run(void)
{
static const bool shouldSleep = ConfigConnectionServer::getShouldSleep();
static ConnectionServer & cserver = instance();
DEBUG_FATAL(!cserver.m_metricsData, ("Connection server not installed properly"));
unsigned long startTime = Clock::timeMs();
Clock::setFrameRateLimit(50.0f);
LOG("ServerStartup", ("ConnectionServer starting on %s", NetworkHandler::getHostName().c_str()));
while (!cserver.done)
{
PROFILER_AUTO_BLOCK_DEFINE("main loop");
//bool barrierReached = true;
do
{
{
PROFILER_AUTO_BLOCK_DEFINE("Os::update");
if (!Os::update())
cserver.setDone("Os condition (Parent pid change)");
}
{
PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::update1");
NetworkHandler::update();
}
{
GenericValueTypeMessage<std::pair<uint16, uint32> > const syncStampMessage("SetSyncStamp", std::make_pair(static_cast<uint16>(UdpMisc::LocalSyncStampShort()), static_cast<uint32>(UdpMisc::LocalSyncStampLong())));
GameServerMap::iterator end = cserver.gameServerMap.end();
for (GameServerMap::iterator iter = cserver.gameServerMap.begin(); iter != end; ++iter)
iter->second->send(syncStampMessage, true);
}
{
PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::dispatch");
NetworkHandler::dispatch();
}
{
PROFILER_AUTO_BLOCK_DEFINE("update");
cserver.update();
}
{
PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::update2");
NetworkHandler::update();
}
{
PROFILER_AUTO_BLOCK_DEFINE("MetricsManager::update");
unsigned long curTime = Clock::timeMs();
MetricsManager::update(static_cast<float>(curTime - startTime));
startTime = curTime;
}
if (shouldSleep)
{
PROFILER_AUTO_BLOCK_DEFINE("Os::sleep");
Os::sleep(1);
}
} while (!cserver.done);
NetworkHandler::clearBytesThisFrame();
cserver.updateRecoveringClientList(static_cast<uint>(Clock::frameTime()*1000.0f));
}
}
// ----------------------------------------------------------------------
/**
* Invoked every frame to do whatever updates are needed
*/
void ConnectionServer::update()
{
if (centralConnection)
{
static Timer t(5.0f);
if (t.updateZero(Clock::frameTime()))
{
// Update the population on the central server
updatePopulationOnCentralServer();
if (getNumberOfClients() >= ConfigConnectionServer::getMaxClients() - 1)
{
//@todo need an Alert here
WARNING(true, ("We've reached maximum client capacity on a connection server with %d clients", getNumberOfClients()));
}
}
}
if (m_sessionApiClient)
{
m_sessionApiClient->update();
}
static const int ping_throttle_max = 1024;
static char buffer[4];
for (int throttle = 0; pingSocket->canRecv() && throttle < ping_throttle_max; ++throttle)
{
Address addr;
const uint32 count = pingSocket->recvFrom(addr, buffer, 4);
m_pingTrafficNumBytes += static_cast<int>(count);
if (m_pingTrafficNumBytes < 0)
m_pingTrafficNumBytes = 0;
IGNORE_RETURN(pingSocket->sendTo(addr, buffer, count));
}
}
// ----------------------------------------------------------------------
/**
* The recovering client list holds players who are trying to recover from a server
* crash. If another game server takes authority within a few frames, the player
* is OK and we don't do anything. If no game server takes authority for them,
* we drop them and force them to log in again.
*/
void ConnectionServer::updateRecoveringClientList(uint elapsedTime)
{
if (!m_recoveringClientList.empty())
{
m_recoverTime += elapsedTime;
static std::set<NetworkId> listPlayersDropped;
for (RecoveringClientListType::iterator i = m_recoveringClientList.begin(); i != m_recoveringClientList.end();)
{
if (m_recoverTime > i->first)
{
Client *client = getClient(i->second);
if (client)
{
if (client->getGameConnection())
DEBUG_REPORT_LOG(true, ("Player %s recovered from a server crash and will not be dropped.\n", i->second.getValueString().c_str()));
else
{
LOG("Network", ("Dropping player %s because game server crashed and no other server took authority.\n", i->second.getValueString().c_str()));
dropClient(client->getClientConnection(), "Game Server Crash");
IGNORE_RETURN(listPlayersDropped.insert(i->second));
}
}
i = m_recoveringClientList.erase(i);
}
else
++i;
}
if (!listPlayersDropped.empty())
{
GenericValueTypeMessage<std::set<NetworkId> > const m("PlayerDroppedFromGameServerCrash", listPlayersDropped);
// let one of the game server know that we dropped the player(s) due to a game server crash
GameConnection * const anyGameConnection = getAnyGameConnection();
if (anyGameConnection)
anyGameConnection->send(m, true);
// let CentralServer know that we dropped the player(s) due to a game server crash
sendToCentralProcess(m);
listPlayersDropped.clear();
}
if (m_recoveringClientList.empty())
m_recoverTime = 0; // prevent rollover and other problems I don't want to worry about
}
}
// ----------------------------------------------------------------------
void ConnectionServer::addRecoveringClient(const NetworkId& networkId)
{
instance().m_recoveringClientList.push_back(std::pair<int, NetworkId>(static_cast<int>(instance().m_recoverTime) + ConfigConnectionServer::getCrashRecoveryTimeout(), networkId));//lint !e737 !e713 !e1703 !e1025 // going nuts over unsigned/signed conversion and type mismatch on template
}
// ----------------------------------------------------------------------
void ConnectionServer::unsetupConnections()
{
// remove all ClientConnection objects so when Connection::remove() is called we don't crash since
// ConnectionServer no longer exists
while (!instance().connectedMap.empty())
{
SuidMap::iterator i = instance().connectedMap.begin();
if (i != instance().connectedMap.end())
{
ClientConnection * c = (*i).second;
uint32 suid = (*i).first;
IGNORE_RETURN(instance().connectedMap.erase(suid));
if (c)
{
ConnectionServer::dropClient(c, "ConnectionServer shutting down.");
delete c;
}
}
}
if (customerService)
{
delete customerService;
customerService = 0;
}
if (chatService)
{
delete chatService;
chatService = 0;
}
if (centralConnection)
{
delete centralConnection;
centralConnection = 0;
}
if (gameService)
{
delete gameService;
gameService = 0;
}
}
//-----------------------------------------------------------------------
void ConnectionServer::setupConnections()
{
// set up message connections
connectToMessage("ConnectionGameServerConnect");
connectToMessage("SetConnectionServerPublic");
// set up port to listen for clients, central, and game servers
connectToMessage("CentralConnectionOpened");
connectToMessage("CentralConnectionClosed");
NetworkSetupData setup;
setup.port = ConfigConnectionServer::getGameServicePort();
setup.bindInterface = ConfigConnectionServer::getGameServiceBindInterface();
setup.maxConnections = 1000;
gameService = new Service(ConnectionAllocator<GameConnection>(), setup);
connectToMessage("GameConnectionOpened");
connectToMessage("GameConnectionClosed");
// connect to central server
centralConnection = new CentralConnection(ConfigConnectionServer::getCentralServerAddress(), ConfigConnectionServer::getCentralServerPort());
setup.port = 0;
setup.bindInterface = ConfigConnectionServer::getChatServiceBindInterface();
chatService = new Service(ConnectionAllocator<ChatServerConnection>(), setup);
connectToMessage("ChatServerConnectionOpened");
connectToMessage("ChatServerConnectionClosed");
setup.bindInterface = ConfigConnectionServer::getCustomerServiceBindInterface();
customerService = new Service(ConnectionAllocator<CustomerServiceConnection>(), setup);
connectToMessage("CustomerServiceConnectionOpened");
connectToMessage("CustomerServiceConnectionClosed");
connectToMessage("ConnectionServerId");
connectToMessage("ConnectionKeyPush");
connectToMessage("LoginKeyPush");
connectToMessage("CharacterListMessage");
//Create Characters Messages
connectToMessage("ClientCreateCharacter");
connectToMessage("ConnectionCreateCharacterSuccess");
connectToMessage("ConnectionCreateCharacterFailed");
connectToMessage("NewCharacterCreated");
connectToMessage("GameServerForLoginMessage");
// name query messages
connectToMessage("ClientRandomNameRequest"); // from client
connectToMessage("RandomNameResponse"); // from game server
connectToMessage("ClientVerifyAndLockNameRequest"); // from client
connectToMessage("VerifyAndLockNameResponse"); // from game server
connectToMessage("ValidateCharacterForLoginReplyMessage");
connectToMessage("ValidateAccountReplyMessage");
connectToMessage("ProfilerOperationMessage");
connectToMessage("CentralConnectionClosed");
connectToMessage("CentralConnectionOpened");
connectToMessage("ChunkCompleteMessage");
connectToMessage("FrameEndMessage");
connectToMessage("GameConnectionClosed");
connectToMessage("GameGameServerConnect");
connectToMessage("GameServerReadyMessage");
connectToMessage("GameServerUniverseLoadedMessage");
connectToMessage("PersistedPlayerMessage");
connectToMessage("PreloadListMessage");
connectToMessage("PreloadRequestCompleteMessage");
connectToMessage("ProfilerOperationMessage");
connectToMessage("RequestGameServerForLoginMessage");
connectToMessage("RequestSceneTransfer");
connectToMessage("ShutdownMessage");
connectToMessage("TaskConnectionOpened");
connectToMessage("UnloadedPlayerMessage");
connectToMessage("WatcherConnectionClosed");
connectToMessage("WatcherConnectionOpened");
connectToMessage("ExcommunicateGameServerMessage");
connectToMessage("CntrlSrvDropDupeConns");
}
//----------------------------------------------------------------------
uint16 ConnectionServer::getPingPort()
{
static ConnectionServer & cs = instance();
return cs.pingSocket->getBindAddress().getHostPort();
}
//-----------------------------------------------------------------------
Client* ConnectionServer::getClient(const NetworkId & oid)
{
static ConnectionServer & cs = instance();
ClientMap::const_iterator iter = cs.clientMap.find(oid);
if (iter != cs.clientMap.end())
return (*iter).second;
return 0;
}
//-----------------------------------------------------------------------
const ConnectionServer::ClientMap & ConnectionServer::getClientMap()
{
return instance().clientMap;
}
//-----------------------------------------------------------------------
GameConnection* ConnectionServer::getGameConnection(uint32 gameServerId)
{
static ConnectionServer & cs = instance();
const GameServerMap::const_iterator i = cs.gameServerMap.find(gameServerId);
if (i != cs.gameServerMap.end())
return (*i).second;
return nullptr;
}
//-----------------------------------------------------------------------
GameConnection* ConnectionServer::getAnyGameConnection()
{
static ConnectionServer & cs = instance();
if (!cs.gameServerMap.empty())
return cs.gameServerMap.begin()->second;
return nullptr;
}
//-----------------------------------------------------------------------
int ConnectionServer::getPingTrafficNumBytes()
{
static ConnectionServer & cs = instance();
return cs.m_pingTrafficNumBytes;
}
//-----------------------------------------------------------------------
int ConnectionServer::getNumberOfClients()
{
static ConnectionServer & cs = instance();
return static_cast<int>(cs.connectedMap.size());
}
//-----------------------------------------------------------------------
int ConnectionServer::getNumberOfFreeTrials()
{
static ConnectionServer & cs = instance();
return static_cast<int>(cs.freeTrials.size());
}
//-----------------------------------------------------------------------
int ConnectionServer::getNumberOfGameServers()
{
static ConnectionServer & cs = instance();
return static_cast<int>(cs.gameServerMap.size());
}
//-----------------------------------------------------------------------
void ConnectionServer::removeConnectedCharacter(uint32 suid)
{
static ConnectionServer & cs = instance();
cs.removeFromConnectedMap(suid);
}
//-----------------------------------------------------------------------
void ConnectionServer::sendToCentralProcess(const GameNetworkMessage & msg)
{
static ConnectionServer & cs = instance();
if (cs.centralConnection)
cs.centralConnection->send(msg, true);
else
WARNING(true, ("Connection tried to send a message Central, but there is no Central Connection"));
}
//-----------------------------------------------------------------------
CentralConnection * ConnectionServer::getCentralConnection()
{
static ConnectionServer & cs = instance();
return cs.centralConnection;
}
//-----------------------------------------------------------------------
void ConnectionServer::installSessionValidation()
{
int i = 0;
std::vector<char const *> sessionServers;
int const numberOfSessionServers = ConfigConnectionServer::getNumberOfSessionServers();
for (i = 0; i < numberOfSessionServers; ++i)
{
char const * const p = ConfigConnectionServer::getSessionServer(i);
if (p)
{
REPORT_LOG(true, ("Using session server %s\n", p));
sessionServers.push_back(p);
}
}
// if there were none specified, use defaults
FATAL(i == 0, ("No session servers specified for session API"));
m_sessionApiClient = new SessionApiClient(&sessionServers[0], i);
}
// ----------------------------------------------------------------------
void ConnectionServer::addToClientMap(const NetworkId &oid, ClientConnection* cconn)
{
Client * newClient = new Client(cconn, oid);
clientMap[oid] = newClient;
//associate the ClientConnection object.
cconn->setClient(newClient);
}
// ----------------------------------------------------------------------
void ConnectionServer::removeFromClientMap(const NetworkId &oid)
{
ClientMap::iterator i = clientMap.find(oid);
if (i != clientMap.end())
{
clientMap.erase(i);
}
}
//-----------------------------------------------------------------------
void ConnectionServer::addToConnectedMap(uint32 suid, ClientConnection* cconn)
{
SuidMap::iterator i = connectedMap.find(suid);
if (i == connectedMap.end())
{
connectedMap[suid] = cconn;
}
else
{
WARNING_STRICT_FATAL(true, ("Attepting to add duplicate connected chatacter"));
connectedMap[suid] = cconn;
}
if (((cconn->getSubscriptionFeatures() & ClientSubscriptionFeature::FreeTrial) != 0)
&& ((cconn->getSubscriptionFeatures() & ClientSubscriptionFeature::Base) == 0))
{
freeTrials.insert(suid);
}
// Update the population on the CentralServer immediately
// since we are trying to avoid people "rushing" the server
updatePopulationOnCentralServer();
}
// ----------------------------------------------------------------------
void ConnectionServer::removeFromConnectedMap(uint32 suid)
{
SuidMap::iterator i = connectedMap.find(suid);
if (i != connectedMap.end())
{
connectedMap.erase(i);
}
FreeTrialsSet::iterator j = freeTrials.find(suid);
if (j != freeTrials.end())
{
freeTrials.erase(j);
}
// We could update the CentralServer population but people
// leaving the server are not as important as people connecting
// and so we will wait for update() to handle things
}
// ----------------------------------------------------------------------
void ConnectionServer::updatePopulationOnCentralServer()
{
if (centralConnection)
{
// Total number of clients and how many of those are free trials
const int numPlayers = getNumberOfClients();
const int numFreeTrial = getNumberOfFreeTrials();
// We are concerned about too many people piling up at the beginning
// of the tutorial, so count how many players could be a problem
int numPlayersEmptyScene = 0;
int numPlayersTutorialScene = 0;
int numPlayersFalconScene = 0;
// Walk through the clients and evaluate what scene they are in
SuidMap::const_iterator i;
for (i = connectedMap.begin(); i != connectedMap.end(); ++i)
{
const ClientConnection * const conn = (*i).second;
const Client * const client = conn->getClient();
if (client && client->getGameConnection())
{
const std::string& scene = client->getGameConnection()->getSceneName();
if (scene.empty())
{
numPlayersEmptyScene += 1;
}
else if (scene == SCENE_NAME_TUTORIAL)
{
numPlayersTutorialScene += 1;
}
else if (scene.substr(0, SCENE_NAME_FALCON_PREFIX.length()) == SCENE_NAME_FALCON_PREFIX)
{
numPlayersFalconScene += 1;
}
}
else
{
numPlayersEmptyScene += 1;
}
}
const UpdatePlayerCountMessage msg(false, numPlayers, numFreeTrial, numPlayersEmptyScene, numPlayersTutorialScene, numPlayersFalconScene);
centralConnection->send(msg, true);
}
}
// ----------------------------------------------------------------------
SessionApiClient* ConnectionServer::getSessionApiClient()
{
// this is causing crashes when ConnectionServer is shutdown and something calls this function
// because instance() returns 0.
if (s_connectionServer)
{
return instance().m_sessionApiClient;
}
else
{
return 0;
}
}
// ----------------------------------------------------------------------
void ConnectionServer::setDone(char const *reasonfmt, ...)
{
if (!done)
{
char reason[1024];
va_list ap;
va_start(ap, reasonfmt);
IGNORE_RETURN(_vsnprintf(reason, sizeof(reason), reasonfmt, ap));//lint !e530 Symbol 'ap' not initialized
reason[sizeof(reason) - 1] = '\0';
LOG(
"ServerShutdown",
(
"ConnectionServer (pid %d) shutdown, reason: %s",
static_cast<int>(Os::getProcessId()),
reason));
REPORT_LOG(
true,
(
"ConnectionServer (pid %d) shutdown, reason: %s\n",
static_cast<int>(Os::getProcessId()),
reason));
done = true;
va_end(ap);
}
}
// ======================================================================