mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-07-14 00:01:30 -04:00
1620 lines
53 KiB
C++
Executable File
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);
|
|
}
|
|
}
|
|
|
|
// ======================================================================
|