mirror of
https://bitbucket.org/seefoe/src.git
synced 2026-08-02 03:15:57 -04:00
2014 lines
79 KiB
C++
Executable File
2014 lines
79 KiB
C++
Executable File
// LoginServer.cpp
|
|
// copyright 2000 Verant Interactive
|
|
// Author: Justin Randall
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
#include "FirstLoginServer.h"
|
|
#include "LoginServer.h"
|
|
|
|
#include "Archive/ByteStream.h"
|
|
#include "CentralServerConnection.h"
|
|
#include "ClientConnection.h"
|
|
#include "ConfigLoginServer.h"
|
|
#include "ConsoleManager.h"
|
|
#include "CSToolConnection.h"
|
|
#include "DatabaseConnection.h"
|
|
|
|
#ifdef _DEBUG
|
|
#include "LoginServerRemoteDebugSetup.h"
|
|
#endif
|
|
|
|
#include "MonAPI2/MonitorAPI.h"
|
|
#include "PingConnection.h"
|
|
#include "PurgeManager.h"
|
|
#include "SessionApiClient.h"
|
|
#include "UnicodeUtils.h"
|
|
#include "serverKeyShare/KeyServer.h"
|
|
#include "serverNetworkMessages/AccountFeatureIdRequest.h"
|
|
#include "serverNetworkMessages/AccountFeatureIdResponse.h"
|
|
#include "serverNetworkMessages/AdjustAccountFeatureIdRequest.h"
|
|
#include "serverNetworkMessages/AdjustAccountFeatureIdResponse.h"
|
|
#include "serverNetworkMessages/ClaimRewardsMessage.h"
|
|
#include "serverNetworkMessages/ClaimRewardsReplyMessage.h"
|
|
#include "serverNetworkMessages/ConnectionServerDown.h"
|
|
#include "serverNetworkMessages/FeatureIdTransactionRequest.h"
|
|
#include "serverNetworkMessages/FeatureIdTransactionSyncUpdate.h"
|
|
#include "serverNetworkMessages/LoginClusterName.h"
|
|
#include "serverNetworkMessages/LoginConnectionServerAddress.h"
|
|
#include "serverNetworkMessages/LoginCreateCharacterMessage.h"
|
|
#include "serverNetworkMessages/LoginKeyPush.h"
|
|
#include "serverNetworkMessages/LoginRestoreCharacterMessage.h"
|
|
#include "serverNetworkMessages/LoginUpgradeAccountMessage.h"
|
|
#include "serverNetworkMessages/PreloadFinishedMessage.h"
|
|
#include "serverNetworkMessages/RenameCharacterMessage.h"
|
|
#include "serverNetworkMessages/ServerDeleteCharacterMessage.h"
|
|
#include "serverNetworkMessages/TransferAccountData.h"
|
|
#include "serverNetworkMessages/TransferAccountDataArchive.h"
|
|
#include "serverNetworkMessages/TransferCharacterData.h"
|
|
#include "serverNetworkMessages/TransferCharacterDataArchive.h"
|
|
#include "serverNetworkMessages/TransferReplyMoveValidation.h"
|
|
#include "serverNetworkMessages/TransferRequestMoveValidation.h"
|
|
#include "serverNetworkMessages/UpdateLoginConnectionServerStatus.h"
|
|
#include "serverNetworkMessages/UpdatePlayerCountMessage.h"
|
|
#include "serverNetworkMessages/ValidateAccountMessage.h"
|
|
#include "serverNetworkMessages/ValidateAccountReplyMessage.h"
|
|
#include "serverUtility/AdminAccountManager.h"
|
|
#include "sharedFoundation/ApplicationVersion.h"
|
|
#include "sharedFoundation/Clock.h"
|
|
#include "sharedFoundation/Os.h"
|
|
#include "sharedGame/PlatformFeatureBits.h"
|
|
#include "sharedLog/Log.h"
|
|
#include "sharedLog/SetupSharedLog.h"
|
|
|
|
#include "sharedNetwork/Address.h"
|
|
#include "sharedNetwork/NetworkSetupData.h"
|
|
#include "sharedNetworkMessages/ClientCentralMessages.h"
|
|
#include "sharedNetworkMessages/ClientLoginMessages.h"
|
|
#include "sharedNetworkMessages/DeleteCharacterMessage.h"
|
|
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
|
|
#include "sharedNetworkMessages/LoginClusterStatus.h"
|
|
#include "sharedNetworkMessages/LoginClusterStatusEx.h"
|
|
#include "sharedNetworkMessages/LoginEnumCluster.h"
|
|
#include "sharedUtility/DataTableManager.h"
|
|
|
|
#include "sharedFoundation/CrcConstexpr.hpp"
|
|
|
|
#include <algorithm>
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
namespace LoginServerNamespace
|
|
{
|
|
struct ConnectionServerEntryLessThan : public std::binary_function<LoginServer::ConnectionServerEntry, LoginServer::ConnectionServerEntry, bool>
|
|
{
|
|
bool operator()(const LoginServer::ConnectionServerEntry & x, const LoginServer::ConnectionServerEntry & y) const;
|
|
}; //lint !e1509 // (base class destructor for class 'binary_function' is not virtual -- Effective C++ #14) // binary_function defines 3 typedefs required for the Adaptable Binary Function concept, The destructor is generated by the compiler and beyond our control
|
|
|
|
enum
|
|
{
|
|
WORLD_COUNT_CHANNEL,
|
|
CLUSTER_COUNT_CHANNEL
|
|
};
|
|
|
|
// Very simple function to determine if a "threshold" has been crossed
|
|
bool hasCrossedThreshold(int thresholdValue, int oldValue, int newValue)
|
|
{
|
|
bool result = false;
|
|
|
|
if ((oldValue < thresholdValue) && (newValue >= thresholdValue))
|
|
{
|
|
// Crossed over the threshold
|
|
result = true;
|
|
}
|
|
else if ((oldValue >= thresholdValue) && (newValue < thresholdValue))
|
|
{
|
|
// Crossed back down under the threshold
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// for testing purpose when not using session authentication, store
|
|
// the account feature Ids here, which will get cleared (obviously)
|
|
// when the LoginServer is restarted
|
|
std::map<uint32, std::map<uint32, int> > s_nonSessionTestingAccountSwgFeatureIds;
|
|
std::map<uint32, std::map<uint32, int> > s_nonSessionTestingAccountSwgTcgFeatureIds;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
using namespace LoginServerNamespace;
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool ConnectionServerEntryLessThan::operator()(const LoginServer::ConnectionServerEntry &x, const LoginServer::ConnectionServerEntry &y) const
|
|
{
|
|
return x.numClients < y.numClients;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
LoginServer::LoginServer() :
|
|
Singleton<LoginServer>(),
|
|
MessageDispatch::Receiver(),
|
|
done(false),
|
|
m_centralService(nullptr),
|
|
clientService(0),
|
|
pingService(0),
|
|
keyServer(0),
|
|
m_clientMap(),
|
|
m_clusterList(),
|
|
m_validatedClientMap(),
|
|
m_clusterStatusChanged(false),
|
|
m_soeMonitor(0)
|
|
{
|
|
NetworkSetupData setup;
|
|
setup.port = ConfigLoginServer::getClientServicePort();
|
|
setup.maxConnections = ConfigLoginServer::getMaxClients();
|
|
setup.maxConnectionsPerIP = ConfigLoginServer::getMaxConnectionsPerIP();
|
|
setup.oldestUnacknowledgedTimeout = 30000;
|
|
setup.keepAliveDelay = 45000;
|
|
setup.compress = ConfigLoginServer::getCompressClientNetworkTraffic();
|
|
setup.useTcp = false;
|
|
|
|
clientService = new Service(ConnectionAllocator<ClientConnection>(), setup);
|
|
setup.compress = false;
|
|
|
|
setup.port = ConfigLoginServer::getPingServicePort();
|
|
pingService = new Service(ConnectionAllocator<PingConnection>(), setup);
|
|
setup.useTcp = true;
|
|
|
|
if (ConfigLoginServer::getDevelopmentMode())
|
|
{
|
|
setup.port = ConfigLoginServer::getCentralServicePort();
|
|
m_centralService = new Service(ConnectionAllocator<CentralServerConnection>(), setup);
|
|
}
|
|
|
|
// only start the customer tool connection listener if the port is set (defaults to 0)
|
|
setup.port = ConfigLoginServer::getCSToolPort();
|
|
if (setup.port)
|
|
{
|
|
m_CSService = new Service(ConnectionAllocator<CSToolConnection>(), setup);
|
|
}
|
|
|
|
// set up message connections
|
|
connectToMessage("ClaimRewardsMessage");
|
|
connectToMessage("ConnectionClosed");
|
|
connectToMessage("ConnectionServerDown"); //Sent to login from central when connection server goes down
|
|
connectToMessage("LoginClusterName");
|
|
connectToMessage("LoginClusterName2");
|
|
connectToMessage("LoginConnectionServerAddress"); //Sent to login from central after reconnecting.
|
|
connectToMessage("LoginCreateCharacterMessage");
|
|
connectToMessage("LoginRestoreCharacterMessage");
|
|
connectToMessage("LoginUpgradeAccountMessage");
|
|
connectToMessage("PreloadFinishedMessage");
|
|
connectToMessage("PurgeCompleteMessage");
|
|
connectToMessage("RenameCharacterMessage");
|
|
connectToMessage("TransferRequestMoveValidation");
|
|
connectToMessage("TransferReplyNameValidation");
|
|
connectToMessage("TransferLoginCharacterToDestinationServer");
|
|
connectToMessage("UpdateLoginConnectionServerStatus");
|
|
connectToMessage("UpdatePlayerCountMessage");
|
|
connectToMessage("ValidateAccountMessage");
|
|
connectToMessage("CntrlSrvDropDupeConns");
|
|
connectToMessage("OccupyUnlockedSlotReq");
|
|
connectToMessage("VacateUnlockedSlotReq");
|
|
connectToMessage("SwapUnlockedSlotReq");
|
|
connectToMessage("AdjustAccountFeatureIdRequest");
|
|
connectToMessage("AccountFeatureIdRequest");
|
|
connectToMessage("FeatureIdTransactionRequest");
|
|
connectToMessage("FeatureIdTransactionSyncUpdate");
|
|
keyServer = new KeyServer;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
LoginServer::~LoginServer()
|
|
{
|
|
delete keyServer;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
int LoginServer::addClient(ClientConnection & client)
|
|
{
|
|
DEBUG_FATAL(client.getIsValidated(), ("Tried to add an already validated client?!"));
|
|
//Perhaps add a debug only check to make sure a client connection isn't in twice...I'm not sure how that could happen.
|
|
|
|
static int nextClientId = 0;
|
|
int tmp = ++nextClientId;
|
|
m_clientMap[tmp] = &client;
|
|
return tmp;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
ClientConnection* LoginServer::getValidatedClient(const StationId& clientId)
|
|
{
|
|
std::map<StationId, ClientConnection*>::iterator i = m_validatedClientMap.find(clientId);
|
|
if (i == m_validatedClientMap.end())
|
|
return 0;
|
|
return i->second;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
ClientConnection* LoginServer::getUnvalidatedClient(int clientId)
|
|
{
|
|
WARNING_STRICT_FATAL(clientId == 0, ("Tried to get an unvalidated client with client id == 0"));
|
|
std::map<int, ClientConnection*>::iterator i = m_clientMap.find(clientId);
|
|
if (i == m_clientMap.end())
|
|
return 0;
|
|
WARNING_STRICT_FATAL(i->second->getIsValidated(), ("Returning validated client from call to getUnvalidatedClient()"));
|
|
return i->second;
|
|
}
|
|
//-----------------------------------------------------------------------
|
|
|
|
void LoginServer::removeClient(int clientId)
|
|
{
|
|
std::map<int, ClientConnection*>::iterator i = m_clientMap.find(clientId);
|
|
if (i != m_clientMap.end())
|
|
{
|
|
if (i->second->getIsValidated())
|
|
{
|
|
IGNORE_RETURN(m_validatedClientMap.erase(i->second->getStationId()));
|
|
}
|
|
IGNORE_RETURN(m_clientMap.erase(clientId));
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void LoginServer::installSessionValidation()
|
|
{
|
|
/*int i = 0;
|
|
std::vector<const char* > sessionServers;
|
|
|
|
int numberOfSessionServers = ConfigLoginServer::getNumberOfSessionServers();
|
|
for (i = 0; i < numberOfSessionServers; ++i)
|
|
{
|
|
char const * const p = ConfigLoginServer::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);*/
|
|
|
|
return;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool LoginServer::deleteCharacter(uint32 clusterId, NetworkId const & characterId, StationId suid)
|
|
{
|
|
const ClusterListEntry *cle = findClusterById(clusterId);
|
|
if (cle)
|
|
{
|
|
if (cle->m_centralServerConnection)
|
|
{
|
|
ServerDeleteCharacterMessage smsg(suid, characterId, 0);
|
|
cle->m_centralServerConnection->send(smsg, true);
|
|
|
|
ClientConnection* target = getValidatedClient(suid);
|
|
if (target)
|
|
{
|
|
target->onCharacterDeletedFromCluster(characterId);
|
|
}
|
|
|
|
DatabaseConnection::getInstance().deleteCharacter(clusterId, characterId, suid);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("User %lu requested deleting character %s on cluster %lu, but the cluster is not currently connected.\n", suid, characterId.getValueString().c_str(), clusterId));
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("User %lu requested deleting character %s on cluster %lu, but we have no cluster with that number.\n", suid, characterId.getValueString().c_str(), clusterId));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
const KeyShare::Key & LoginServer::getCurrentKey(void) const
|
|
{
|
|
return keyServer->getKey(0);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
SessionApiClient * LoginServer::getSessionApiClient()
|
|
{
|
|
return m_sessionApiClient;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
KeyShare::Token LoginServer::makeToken(const unsigned char * const data, const uint32 dataLen) const
|
|
{
|
|
return keyServer->makeToken(data, dataLen);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void LoginServer::pushAllKeys(CentralServerConnection * targetGameServer) const
|
|
{
|
|
for (int i = static_cast<int>(keyServer->getKeyCount()) - 1; i >= 0; i--)
|
|
{
|
|
LoginKeyPush pk(keyServer->getKey(static_cast<unsigned int>(i)));
|
|
targetGameServer->send(pk, true);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void LoginServer::pushKeyToAllServers(void)
|
|
{
|
|
KeyShare::Key k = keyServer->getKey(0);
|
|
CentralServerConnection * c;
|
|
|
|
LoginKeyPush msg(k);
|
|
|
|
unsigned char s[128];
|
|
memcpy(s, k.value, 16); //lint !e64 !e119 !e534 // not sure where lint is finding a memcpy(void *, int) prototype
|
|
DEBUG_REPORT_LOG(true, ("Key Exchange ->: "));
|
|
for (int x = 0; x < 16; x++)
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("[%3i]", s[x]));
|
|
}
|
|
DEBUG_REPORT_LOG(true, ("\n"));
|
|
|
|
for (ClusterListType::const_iterator i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
c = (*i)->m_centralServerConnection;
|
|
if (c)
|
|
{
|
|
c->send(msg, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message)
|
|
{
|
|
// determine message type
|
|
const uint32 messageType = message.getType();
|
|
|
|
switch(messageType) {
|
|
case constcrc("LoginClusterName") :
|
|
case constcrc("LoginClusterName2") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
const LoginClusterName msg(ri);
|
|
if (msg.getClusterName().length() > 0)
|
|
{
|
|
CentralServerConnection * connection = const_cast<CentralServerConnection*>(safe_cast<const CentralServerConnection *>(&source));
|
|
DEBUG_REPORT_LOG(true, ("Cluster connection %s opened\n", msg.getClusterName().c_str()));
|
|
ClusterListEntry *cle = nullptr;
|
|
if (ConfigLoginServer::getDevelopmentMode()) {
|
|
// in this mode, we trust the name sent by the cluster
|
|
cle = findClusterByName(msg.getClusterName());
|
|
#ifdef _DEBUG
|
|
// if in debug mode, we dynamically add clusters we don't know about
|
|
// DO NOT USE ON PRODUCTION! This is where other servers are getting hijacked.
|
|
if (!cle)
|
|
{
|
|
cle = addCluster(msg.getClusterName());
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// in this mode, the cluster name has to match what we were expecting
|
|
cle = findClusterByConnection(connection);
|
|
if (!cle)
|
|
DEBUG_FATAL(true, ("PROGRAMMER BUG: Got a connection from %s:%hu, which we weren't expecting. Cluster name is \"%s\".\n", connection->getRemoteAddress().c_str(), connection->getRemotePort(), msg.getClusterName().c_str()));
|
|
else
|
|
if (msg.getClusterName() != cle->m_clusterName)
|
|
{
|
|
WARNING(true, ("Server %i is named \"%s\" in the database. The server at the specified address (%s:%hu) reports its name as \"%s\". It will not be allowed in the service. Either the name in the database or the name in Central's config file should be corrected.\n", cle->m_clusterId, cle->m_clusterName.c_str(), connection->getRemoteAddress().c_str(), connection->getRemotePort(), msg.getClusterName().c_str()));
|
|
disconnectCluster(*cle, true, false);
|
|
cle = nullptr;
|
|
}
|
|
}
|
|
|
|
if (cle)
|
|
{
|
|
cle->m_timeZone = msg.getTimeZone();
|
|
cle->m_centralServerConnection = connection;
|
|
cle->m_connected = true;
|
|
pushAllKeys(connection);
|
|
|
|
if (cle->m_clusterId == 0)
|
|
{
|
|
DEBUG_FATAL(!ConfigLoginServer::getDevelopmentMode(), ("Programmer bug: cle->m_clusterId was 0 in non-development mode. The code before this line should have prevented this.\n"));
|
|
DEBUG_REPORT_LOG(true, ("Cluster was not on the list. Adding it to the database.\n"));
|
|
DatabaseConnection::getInstance().registerNewCluster(msg.getClusterName(), connection->getRemoteAddress());
|
|
}
|
|
else
|
|
cle->m_centralServerConnection->setClusterId(cle->m_clusterId);
|
|
|
|
m_clusterStatusChanged = true;
|
|
|
|
// tell the cluster its cluster id
|
|
if (cle->m_clusterId > 0)
|
|
{
|
|
GenericValueTypeMessage<uint32> const msgClusterId("ClusterId", cle->m_clusterId);
|
|
cle->m_centralServerConnection->send(msgClusterId, true);
|
|
}
|
|
|
|
// tell the cluster about its locked and secret state
|
|
GenericValueTypeMessage<std::pair<bool, bool> > const msgState("UpdateClusterLockedAndSecretState", std::make_pair(cle->m_locked, cle->m_secret));
|
|
cle->m_centralServerConnection->send(msgState, true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("LoginConnectionServerAddress") :
|
|
{
|
|
const CentralServerConnection * cs = safe_cast<const CentralServerConnection *>(&source);
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
|
|
LoginConnectionServerAddress m(ri); //lint !e1774 !e826onServerEntry entry;
|
|
ConnectionServerEntry entry;
|
|
entry.clientServiceAddress = Address(Address(m.getClientServiceAddress(), 0).getSockAddr4()).getHostAddress();
|
|
entry.clientServicePortPrivate = m.getClientServicePortPrivate();
|
|
entry.clientServicePortPublic = m.getClientServicePortPublic();
|
|
entry.id = m.getId();
|
|
entry.numClients = m.getNumClients();
|
|
entry.pingPort = m.getPingPort();
|
|
|
|
DEBUG_REPORT_LOG(true, ("ConnectionServer Reconnect - address from connection server (%s), address after conversion (%s)\n", m.getClientServiceAddress().c_str(), entry.clientServiceAddress.c_str()));
|
|
|
|
ClusterListEntry *cle = findClusterByConnection(cs);
|
|
if (cle)
|
|
{
|
|
WARNING_STRICT_FATAL(!cle->m_centralServerConnection, ("Got reconnect for connection server with no central! Cluster %s\n", cs->getClusterName().c_str()));
|
|
std::vector<ConnectionServerEntry>::iterator i = std::find(cle->m_connectionServers.begin(), cle->m_connectionServers.end(), entry);
|
|
if (i == cle->m_connectionServers.end())
|
|
{
|
|
cle->m_connectionServers.push_back(entry);
|
|
m_clusterStatusChanged = true;
|
|
}
|
|
else
|
|
{
|
|
*i = entry;
|
|
}
|
|
|
|
std::sort(cle->m_connectionServers.begin(), cle->m_connectionServers.end(), ConnectionServerEntryLessThan());
|
|
}
|
|
else
|
|
WARNING_STRICT_FATAL(true, ("Programmer bug: Got LoginConnectionServerAddress from a cluster that wasn't on the list. Probably indicates we aren't tracking connections properly.\n"));
|
|
break;
|
|
}
|
|
case constcrc("PreloadFinishedMessage") :
|
|
{
|
|
const CentralServerConnection * cs = safe_cast<const CentralServerConnection *>(&source);
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
PreloadFinishedMessage msg(ri);
|
|
|
|
ClusterListEntry *cle = findClusterByConnection(cs);
|
|
if (cle)
|
|
{
|
|
if (msg.getFinished())
|
|
{
|
|
REPORT_LOG(true, ("Cluster %s is ready for players.\n", cle->m_clusterName.c_str()));
|
|
if (!cle->m_readyForPlayers)
|
|
{
|
|
cle->m_readyForPlayers = true;
|
|
m_clusterStatusChanged = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
REPORT_LOG(true, ("Cluster %s is not ready for players.\n", cle->m_clusterName.c_str()));
|
|
if (cle->m_readyForPlayers)
|
|
{
|
|
cle->m_readyForPlayers = false;
|
|
m_clusterStatusChanged = true;
|
|
PurgeManager::onClusterNoLongerReady(cle->m_clusterId);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
WARNING_STRICT_FATAL(true, ("Programmer bug: Got PreloadFinishedMessage from a cluster that wasn't on the list. Probably indicates we aren't tracking connections properly.\n"));
|
|
|
|
break;
|
|
}
|
|
case constcrc("ConnectionServerDown") :
|
|
{
|
|
const CentralServerConnection * centralConnection = safe_cast<const CentralServerConnection*>(&source);
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ConnectionServerDown c(ri); //lint !e1774 !e826
|
|
ClusterListEntry *cle = findClusterByConnection(centralConnection);
|
|
if (cle)
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("Lost a connection server %d for %s.\n", c.getId(), cle->m_clusterName.c_str()));
|
|
|
|
std::vector<ConnectionServerEntry>::iterator iter = cle->m_connectionServers.begin();
|
|
bool found = false;
|
|
for (; iter != cle->m_connectionServers.end(); ++iter)
|
|
{
|
|
if (iter->id == c.getId())
|
|
{
|
|
IGNORE_RETURN(cle->m_connectionServers.erase(iter));
|
|
std::sort(cle->m_connectionServers.begin(), cle->m_connectionServers.end(), ConnectionServerEntryLessThan());
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
DEBUG_REPORT_LOG(!found, ("Tried to remove a connection server that wasn't in our list.\n"));
|
|
m_clusterStatusChanged = true;
|
|
}
|
|
else
|
|
WARNING_STRICT_FATAL(true, ("Programmer bug: Got ConnectionServerDown from a cluster that wasn't on the list. Probably indicates we aren't tracking connections properly.\n"));
|
|
|
|
break;
|
|
}
|
|
case constcrc("ConnectionClosed") :
|
|
{
|
|
const CentralServerConnection *c = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (c)
|
|
{
|
|
ClusterListEntry *cle = findClusterByConnection(c);
|
|
if (cle)
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("Cluster connection %s closed.\n", c->getClusterName().c_str()));
|
|
disconnectCluster(*cle, false, true);
|
|
}
|
|
else
|
|
WARNING_STRICT_FATAL(true, ("Programmer bug: Cluster disconnected but it wasn't on the list. Probably indicates we aren't tracking connections properly.\n"));
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("ValidateAccountMessage") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ValidateAccountMessage msg(ri);
|
|
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection*>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().getAccountValidationData(msg.getStationId(), conn->getClusterId(), msg.getTrack(), msg.getSubscriptionBits());
|
|
else
|
|
WARNING_STRICT_FATAL(true, ("Expect ValidateAccountMessage's to only come from CentralServers.\n"));
|
|
break;
|
|
}
|
|
case constcrc("CntrlSrvDropDupeConns") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<uint32, std::string> > const msg(ri);
|
|
|
|
sendToAllClusters(msg, dynamic_cast<Connection const *>(&source));
|
|
break;
|
|
}
|
|
case constcrc("AdjustAccountFeatureIdRequest") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
AdjustAccountFeatureIdRequest const msg(ri);
|
|
|
|
if (m_sessionApiClient)
|
|
{
|
|
// on a session authenticated cluster, this request should have been serviced by the ConnectionServer
|
|
}
|
|
else
|
|
{
|
|
// for testing purpose when not using session authentication, store
|
|
// the account feature Ids locally in memory, which will get cleared
|
|
// (obviously) when the LoginServer is restarted
|
|
std::map<uint32, std::map<uint32, int> > * nonSessionTestingAccountFeatureIds = nullptr;
|
|
if (msg.getGameCode() == PlatformGameCode::SWG)
|
|
nonSessionTestingAccountFeatureIds = &s_nonSessionTestingAccountSwgFeatureIds;
|
|
else if (msg.getGameCode() == PlatformGameCode::SWGTCG)
|
|
nonSessionTestingAccountFeatureIds = &s_nonSessionTestingAccountSwgTcgFeatureIds;
|
|
|
|
int currentFeatureIdCount = 0;
|
|
int updatedFeatureIdCount = 0;
|
|
if (nonSessionTestingAccountFeatureIds)
|
|
{
|
|
std::map<uint32, int> & accountFeatureIds = (*nonSessionTestingAccountFeatureIds)[msg.getTargetStationId()];
|
|
std::map<uint32, int>::const_iterator accountFeatureId = accountFeatureIds.find(msg.getFeatureId());
|
|
if (accountFeatureId != accountFeatureIds.end())
|
|
currentFeatureIdCount = accountFeatureId->second;
|
|
|
|
updatedFeatureIdCount = std::max(0, currentFeatureIdCount + msg.getAdjustment());
|
|
if (updatedFeatureIdCount > 0)
|
|
{
|
|
accountFeatureIds[msg.getFeatureId()] = updatedFeatureIdCount;
|
|
}
|
|
else
|
|
{
|
|
IGNORE_RETURN(accountFeatureIds.erase(msg.getFeatureId()));
|
|
if (accountFeatureIds.empty())
|
|
IGNORE_RETURN(nonSessionTestingAccountFeatureIds->erase(msg.getTargetStationId()));
|
|
}
|
|
}
|
|
|
|
// CS log SWG TCG account feature grant or SWG account feature grant for reward item trade in
|
|
if (nonSessionTestingAccountFeatureIds && !msg.getTargetPlayerDescription().empty() && msg.getTargetItem().isValid() && !msg.getTargetItemDescription().empty())
|
|
{
|
|
if (msg.getGameCode() == PlatformGameCode::SWGTCG)
|
|
LOG("CustomerService", ("TcgRedemption: %s redeemed %s for SWGTCG account feature Id %lu (%d -> %d)", msg.getTargetPlayerDescription().c_str(), msg.getTargetItemDescription().c_str(), msg.getFeatureId(), currentFeatureIdCount, updatedFeatureIdCount));
|
|
else if (msg.getGameCode() == PlatformGameCode::SWG)
|
|
LOG("CustomerService", ("VeteranRewards: %s traded in %s for SWG account feature Id %lu (%d -> %d)", msg.getTargetPlayerDescription().c_str(), msg.getTargetItemDescription().c_str(), msg.getFeatureId(), currentFeatureIdCount, updatedFeatureIdCount));
|
|
}
|
|
|
|
const CentralServerConnection * conn = dynamic_cast<const CentralServerConnection*>(&source);
|
|
if (conn)
|
|
{
|
|
AdjustAccountFeatureIdResponse const rsp(msg.getRequestingPlayer(), msg.getGameServer(), msg.getTargetPlayer(), msg.getTargetPlayerDescription(), msg.getTargetStationId(), msg.getTargetItem(), msg.getTargetItemDescription(), msg.getGameCode(), msg.getFeatureId(), currentFeatureIdCount, updatedFeatureIdCount, (nonSessionTestingAccountFeatureIds ? RESULT_SUCCESS : RESULT_CANCELLED), false);
|
|
sendToCluster(conn->getClusterId(), rsp);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("AccountFeatureIdRequest") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
AccountFeatureIdRequest const msg(ri);
|
|
|
|
if (m_sessionApiClient)
|
|
{
|
|
// on a session authenticated cluster, this request should have been serviced by the ConnectionServer
|
|
}
|
|
else
|
|
{
|
|
// for testing purpose when not using session authentication, store
|
|
// the account feature Ids locally in memory, which will get cleared
|
|
// (obviously) when the LoginServer is restarted
|
|
std::map<uint32, std::map<uint32, int> > * nonSessionTestingAccountFeatureIds = nullptr;
|
|
if (msg.getGameCode() == PlatformGameCode::SWG)
|
|
nonSessionTestingAccountFeatureIds = &s_nonSessionTestingAccountSwgFeatureIds;
|
|
else if (msg.getGameCode() == PlatformGameCode::SWGTCG)
|
|
nonSessionTestingAccountFeatureIds = &s_nonSessionTestingAccountSwgTcgFeatureIds;
|
|
|
|
static std::map<uint32, int> const empty;
|
|
std::map<uint32, int> const * accountFeatureIds = ∅
|
|
|
|
if (nonSessionTestingAccountFeatureIds)
|
|
{
|
|
std::map<uint32, std::map<uint32, int> >::const_iterator iterFind = nonSessionTestingAccountFeatureIds->find(msg.getTargetStationId());
|
|
if (iterFind != nonSessionTestingAccountFeatureIds->end())
|
|
accountFeatureIds = &(iterFind->second);
|
|
}
|
|
|
|
const CentralServerConnection * conn = dynamic_cast<const CentralServerConnection*>(&source);
|
|
if (conn)
|
|
{
|
|
static std::map<uint32, std::string> const empty;
|
|
AccountFeatureIdResponse const rsp(msg.getRequester(), msg.getGameServer(), msg.getTarget(), msg.getTargetStationId(), msg.getGameCode(), msg.getRequestReason(), RESULT_SUCCESS, false, *accountFeatureIds, empty);
|
|
sendToCluster(conn->getClusterId(), rsp);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("FeatureIdTransactionRequest") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
FeatureIdTransactionRequest const fitr(ri);
|
|
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().featureIdTransactionRequest(conn->getClusterId(), fitr.getStationId(), fitr.getPlayer(), fitr.getGameServer());
|
|
break;
|
|
}
|
|
case constcrc("FeatureIdTransactionSyncUpdate") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
FeatureIdTransactionSyncUpdate const fitsu(ri);
|
|
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().featureIdTransactionSyncUpdate(conn->getClusterId(), fitsu.getStationId(), fitsu.getPlayer(), fitsu.getItemId(), fitsu.getAdjustment());
|
|
break;
|
|
}
|
|
case constcrc("TransferRequestMoveValidation") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
const TransferRequestMoveValidation request(ri);
|
|
|
|
TransferReplyMoveValidation::TransferReplyMoveValidationResult result = TransferReplyMoveValidation::TRMVR_can_create_regular_character;
|
|
|
|
ClusterListEntry * cle = findClusterByName(request.getDestinationGalaxy());
|
|
if (!cle)
|
|
{
|
|
result = TransferReplyMoveValidation::TRMVR_destination_galaxy_invalid;
|
|
}
|
|
else if (!cle->m_centralServerConnection)
|
|
{
|
|
result = TransferReplyMoveValidation::TRMVR_destination_galaxy_not_connected;
|
|
}
|
|
else if (!cle->m_readyForPlayers)
|
|
{
|
|
result = TransferReplyMoveValidation::TRMVR_destination_galaxy_in_loading;
|
|
}
|
|
|
|
if (result == TransferReplyMoveValidation::TRMVR_can_create_regular_character)
|
|
{
|
|
// check with DB to see if account is allowed to create character on the destination galaxy
|
|
LOG("CustomerService", ("CharacterTransfer: Received TransferRequestMoveValidation : %s (character template id %lu) on %s to %s on %s. Forwarding request to Login Database.", request.getSourceCharacter().c_str(), request.getSourceCharacterTemplateId(), request.getSourceGalaxy().c_str(), request.getDestinationCharacter().c_str(), request.getDestinationGalaxy().c_str()));
|
|
DatabaseConnection::getInstance().getAccountValidationData(request, cle->m_centralServerConnection->getClusterId());
|
|
}
|
|
else
|
|
{
|
|
// send failure back to originating server
|
|
TransferReplyMoveValidation reply(request.getTransferRequestSource(), request.getTrack(), request.getSourceStationId(), request.getDestinationStationId(), request.getSourceGalaxy(), request.getDestinationGalaxy(), request.getSourceCharacter(), request.getSourceCharacterId(), request.getSourceCharacterTemplateId(), request.getDestinationCharacter(), request.getCustomerLocalizedLanguage(), result);
|
|
const CentralServerConnection * conn = dynamic_cast<const CentralServerConnection*>(&source);
|
|
if (conn)
|
|
{
|
|
ClusterListEntry * sourceEntry = findClusterById(conn->getClusterId());
|
|
if (sourceEntry && sourceEntry->m_centralServerConnection)
|
|
{
|
|
sourceEntry->m_centralServerConnection->send(reply, true);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("TransferReplyNameValidation") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<std::string, TransferCharacterData> > const replyNameValidation(ri);
|
|
|
|
const CentralServerConnection * conn = dynamic_cast<const CentralServerConnection*>(&source);
|
|
if (conn)
|
|
{
|
|
ClusterListEntry * cle = findClusterByName(replyNameValidation.getValue().second.getSourceGalaxy());
|
|
if (cle && cle->m_centralServerConnection)
|
|
{
|
|
LOG("CustomerService", ("CharacterTransfer: Received TransferReplyNameValidation from destination galaxy CentralServer, forwarding to source galaxy CentralServer : %s", replyNameValidation.getValue().second.toString().c_str()));
|
|
cle->m_centralServerConnection->send(replyNameValidation, true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("TransferLoginCharacterToDestinationServer") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<TransferCharacterData> const login(ri);
|
|
|
|
const CentralServerConnection * conn = dynamic_cast<const CentralServerConnection*>(&source);
|
|
if (conn)
|
|
{
|
|
ClusterListEntry * cle = findClusterByName(login.getValue().getDestinationGalaxy());
|
|
if (cle && cle->m_centralServerConnection)
|
|
{
|
|
LOG("CustomerService", ("CharacterTransfer: Received TransferLoginCharacterToDestinationServer from source galaxy CentralServer, forwarding to destination galaxy CentralServer : %s", login.getValue().toString().c_str()));
|
|
cle->m_centralServerConnection->send(login, true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("UpdateLoginConnectionServerStatus") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
UpdateLoginConnectionServerStatus msg(ri);
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
{
|
|
ClusterListEntry *cle = findClusterByConnection(conn);
|
|
if (cle)
|
|
{
|
|
std::vector<ConnectionServerEntry>::iterator i = cle->m_connectionServers.begin();
|
|
for (; i != cle->m_connectionServers.end(); ++i)
|
|
{
|
|
if (i->id == msg.getId())
|
|
{
|
|
i->clientServicePortPublic = msg.getPublicPort();
|
|
i->clientServicePortPrivate = msg.getPrivatePort();
|
|
i->numClients = msg.getPlayerCount();
|
|
break;
|
|
}
|
|
}
|
|
std::sort(cle->m_connectionServers.begin(), cle->m_connectionServers.end(), ConnectionServerEntryLessThan());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("UpdatePlayerCountMessage") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
UpdatePlayerCountMessage msg(ri);
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
{
|
|
ClusterListEntry *cle = findClusterByConnection(conn);
|
|
if (cle)
|
|
{
|
|
const int tutorialPlayerCount = (msg.getEmptySceneCount() + msg.getTutorialSceneCount() + msg.getFalconSceneCount());
|
|
|
|
// We only want to update the clients if some "threshold" has been crossed
|
|
if ((cle->m_notRecommendedCentral != msg.getLoadedRecently())
|
|
|| hasCrossedThreshold(cle->m_onlineTutorialLimit, cle->m_numTutorialPlayers, tutorialPlayerCount)
|
|
|| hasCrossedThreshold(cle->m_onlineFreeTrialLimit, cle->m_numFreeTrialPlayers, msg.getFreeTrialCount())
|
|
|| hasCrossedThreshold(cle->m_onlinePlayerLimit, cle->m_numPlayers, msg.getCount()))
|
|
{
|
|
m_clusterStatusChanged = true;
|
|
}
|
|
|
|
cle->m_numPlayers = msg.getCount();
|
|
cle->m_numFreeTrialPlayers = msg.getFreeTrialCount();
|
|
cle->m_notRecommendedCentral = msg.getLoadedRecently();
|
|
cle->m_numTutorialPlayers = tutorialPlayerCount;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("RenameCharacterMessage") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
RenameCharacterMessage msg(ri);
|
|
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().renameCharacter(conn->getClusterId(), msg.getCharacterId(), msg.getNewName(), nullptr);
|
|
else
|
|
WARNING_STRICT_FATAL(true, ("Got RenameCharacterMessage from something other than CentralServerConnection.\n"));
|
|
|
|
break;
|
|
}
|
|
case constcrc("LoginCreateCharacterMessage") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
LoginCreateCharacterMessage msg(ri);
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().createCharacter(conn->getClusterId(), msg.getStationId(), msg.getCharacterName(), msg.getCharacterObjectId(), msg.getTemplateId(), msg.getJedi());
|
|
|
|
break;
|
|
}
|
|
case constcrc("LoginRestoreCharacterMessage") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
LoginRestoreCharacterMessage msg(ri);
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().restoreCharacter(conn->getClusterId(), msg.getWhoRequested(), msg.getAccount(), msg.getCharacterName(), msg.getCharacterId(), msg.getTemplateId(), msg.getJedi());
|
|
|
|
break;
|
|
}
|
|
case constcrc("LoginUpgradeAccountMessage") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
LoginUpgradeAccountMessage *msg = new LoginUpgradeAccountMessage(ri);
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().upgradeAccount(msg, conn->getClusterId());
|
|
|
|
break;
|
|
}
|
|
case constcrc("ClaimRewardsMessage") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
ClaimRewardsMessage const * msg = new ClaimRewardsMessage(ri);
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
{
|
|
// current restriction is that once per account event or item cannot require a "consuming" account feature id
|
|
// TODO: looks like this is where vet rewards are consumed...the one per account thing, here - session auth may be enough?
|
|
uint32 const requiredAccountFeatureId = msg->getAccountFeatureId();
|
|
bool const consumeAccountFeatureId = msg->getConsumeAccountFeatureId();
|
|
if ((msg->getConsumeEvent() || msg->getConsumeItem()) && (requiredAccountFeatureId > 0) && consumeAccountFeatureId)
|
|
{
|
|
ClaimRewardsReplyMessage const rsp(msg->getGameServer(), msg->getStationId(), msg->getPlayer(), msg->getRewardEvent(), msg->getRewardItem(), requiredAccountFeatureId, consumeAccountFeatureId, 0, 0, false);
|
|
sendToCluster(conn->getClusterId(), rsp);
|
|
delete msg;
|
|
}
|
|
else if (requiredAccountFeatureId > 0)
|
|
{
|
|
if (m_sessionApiClient)
|
|
{
|
|
/*if (consumeAccountFeatureId)
|
|
{
|
|
// request session/Platform to update the account feature id
|
|
// SessionApiClient will own (and delete) msg
|
|
m_sessionApiClient->handleClaimRewardsMessage(conn->getClusterId(), msg);
|
|
}
|
|
else
|
|
{
|
|
LoginAPI::Feature oldFeature;
|
|
oldFeature.SetID(requiredAccountFeatureId);
|
|
oldFeature.SetData(msg->getAccountFeatureIdOldValue());
|
|
|
|
LoginAPI::Feature newFeature;
|
|
newFeature.SetID(requiredAccountFeatureId);
|
|
newFeature.SetData(msg->getAccountFeatureIdNewValue());
|
|
|
|
DatabaseConnection::getInstance().claimRewards(conn->getClusterId(), msg->getGameServer(), msg->getStationId(), msg->getPlayer(), msg->getRewardEvent(), msg->getConsumeEvent(), msg->getRewardItem(), msg->getConsumeItem(), requiredAccountFeatureId, false, oldFeature.GetConsumeCount(), newFeature.GetConsumeCount());
|
|
delete msg;
|
|
}*/
|
|
}
|
|
else
|
|
{
|
|
// for testing purpose when not using session authentication, store
|
|
// the account feature Ids locally in memory, which will get cleared
|
|
// (obviously) when the LoginServer is restarted
|
|
std::map<uint32, int> & accountFeatureIds = s_nonSessionTestingAccountSwgFeatureIds[msg->getStationId()];
|
|
std::map<uint32, int>::const_iterator accountFeatureId = accountFeatureIds.find(requiredAccountFeatureId);
|
|
int currentFeatureIdCount = 0;
|
|
if (accountFeatureId != accountFeatureIds.end())
|
|
currentFeatureIdCount = accountFeatureId->second;
|
|
|
|
if (currentFeatureIdCount <= 0)
|
|
{
|
|
// fail because account doesn't have required feature id
|
|
IGNORE_RETURN(accountFeatureIds.erase(requiredAccountFeatureId));
|
|
if (accountFeatureIds.empty())
|
|
IGNORE_RETURN(s_nonSessionTestingAccountSwgFeatureIds.erase(msg->getStationId()));
|
|
|
|
ClaimRewardsReplyMessage rsp(msg->getGameServer(), msg->getStationId(), msg->getPlayer(), msg->getRewardEvent(), msg->getRewardItem(), requiredAccountFeatureId, consumeAccountFeatureId, 0, 0, false);
|
|
sendToCluster(conn->getClusterId(), rsp);
|
|
}
|
|
else
|
|
{
|
|
// account has required feature id so claim is success, so update feature id for claim
|
|
int const updatedFeatureIdCount = (consumeAccountFeatureId ? std::max(0, currentFeatureIdCount - 1) : currentFeatureIdCount);
|
|
|
|
if (consumeAccountFeatureId)
|
|
{
|
|
if (updatedFeatureIdCount > 0)
|
|
{
|
|
accountFeatureIds[requiredAccountFeatureId] = updatedFeatureIdCount;
|
|
}
|
|
else
|
|
{
|
|
IGNORE_RETURN(accountFeatureIds.erase(requiredAccountFeatureId));
|
|
if (accountFeatureIds.empty())
|
|
IGNORE_RETURN(s_nonSessionTestingAccountSwgFeatureIds.erase(msg->getStationId()));
|
|
}
|
|
}
|
|
|
|
// if feature id updated successfully, record transaction
|
|
DatabaseConnection::getInstance().claimRewards(conn->getClusterId(), msg->getGameServer(), msg->getStationId(), msg->getPlayer(), msg->getRewardEvent(), msg->getConsumeEvent(), msg->getRewardItem(), msg->getConsumeItem(), requiredAccountFeatureId, consumeAccountFeatureId, currentFeatureIdCount, updatedFeatureIdCount);
|
|
}
|
|
|
|
delete msg;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DatabaseConnection::getInstance().claimRewards(conn->getClusterId(), msg->getGameServer(), msg->getStationId(), msg->getPlayer(), msg->getRewardEvent(), msg->getConsumeEvent(), msg->getRewardItem(), msg->getConsumeItem(), 0, false, 0, 0);
|
|
delete msg;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete msg;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case constcrc("PurgeCompleteMessage") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<StationId> msg(ri);
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
PurgeManager::handlePurgeCompleteOnCluster(msg.getValue(), conn->getClusterId());
|
|
else
|
|
WARNING_STRICT_FATAL(true, ("Programmer bug: got PurgeCompleteMessage from something that couldn't be cast to a CentralServerConnection"));
|
|
|
|
break;
|
|
}
|
|
case constcrc("OccupyUnlockedSlotReq") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<std::pair<uint32, NetworkId>, uint32> > const occupyUnlockedSlotReq(ri);
|
|
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().occupyUnlockedSlot(conn->getClusterId(), static_cast<StationId>(occupyUnlockedSlotReq.getValue().first.first), occupyUnlockedSlotReq.getValue().first.second, occupyUnlockedSlotReq.getValue().second);
|
|
|
|
break;
|
|
}
|
|
case constcrc("VacateUnlockedSlotReq") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<std::pair<uint32, NetworkId>, uint32> > const vacateUnlockedSlotReq(ri);
|
|
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().vacateUnlockedSlot(conn->getClusterId(), static_cast<StationId>(vacateUnlockedSlotReq.getValue().first.first), vacateUnlockedSlotReq.getValue().first.second, vacateUnlockedSlotReq.getValue().second);
|
|
|
|
break;
|
|
}
|
|
case constcrc("SwapUnlockedSlotReq") :
|
|
{
|
|
Archive::ReadIterator ri = static_cast<const GameNetworkMessage &>(message).getByteStream().begin();
|
|
GenericValueTypeMessage<std::pair<std::pair<uint32, NetworkId>, std::pair<uint32, NetworkId> > > const swapUnlockedSlotReq(ri);
|
|
|
|
const CentralServerConnection *conn = dynamic_cast<const CentralServerConnection *>(&source);
|
|
if (conn)
|
|
DatabaseConnection::getInstance().swapUnlockedSlot(conn->getClusterId(), static_cast<StationId>(swapUnlockedSlotReq.getValue().first.first), swapUnlockedSlotReq.getValue().first.second, swapUnlockedSlotReq.getValue().second.second, swapUnlockedSlotReq.getValue().second.first);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Validate whether a particular account can connect to a particular cluster.
|
|
* Also, flag whether character creation is allowed.
|
|
* @see DatabaseConnection::getAccountValidationData
|
|
*/
|
|
void LoginServer::validateAccount(const StationId& stationId, uint32 clusterId, uint32 subscriptionBits, bool canCreateRegular, bool canCreateJedi, bool canSkipTutorial, unsigned int track, std::vector<std::pair<NetworkId, std::string> > const & consumedRewardEvents, std::vector<std::pair<NetworkId, std::string> > const & claimedRewardItems)
|
|
{
|
|
bool canLogin = false;
|
|
|
|
ClusterListEntry *cle = findClusterById(clusterId);
|
|
if (cle && cle->m_centralServerConnection)
|
|
{
|
|
// Check login rights
|
|
// code to restrict logins based on "internal only", geographic rules, etc., goes here
|
|
// allow logins unless the cluster is full
|
|
bool clientIsInternal = false;
|
|
ClientConnection *conn = getValidatedClient(stationId);
|
|
if (conn)
|
|
{
|
|
clientIsInternal = AdminAccountManager::isInternalIp(conn->getRemoteAddress());
|
|
}
|
|
|
|
if (clientIsInternal && ConfigLoginServer::getInternalBypassOnlineLimit())
|
|
{
|
|
canLogin = true;
|
|
}
|
|
else if (cle->m_numPlayers <= cle->m_onlinePlayerLimit)
|
|
{
|
|
canLogin = true;
|
|
|
|
// Check cluster npe user limit
|
|
if (cle->m_numTutorialPlayers > cle->m_onlineTutorialLimit)
|
|
{
|
|
canLogin = false;
|
|
}
|
|
}
|
|
|
|
if (!canLogin)
|
|
{
|
|
canCreateRegular = false;
|
|
canCreateJedi = false;
|
|
canSkipTutorial = false;
|
|
}
|
|
|
|
// check if we want to allow skip tutorial to all
|
|
if (ConfigLoginServer::getAllowSkipTutorialToAll())
|
|
canSkipTutorial = true;
|
|
|
|
ValidateAccountReplyMessage msg(stationId, canLogin, canCreateRegular, canCreateJedi, canSkipTutorial, track, consumedRewardEvents, claimedRewardItems);
|
|
cle->m_centralServerConnection->send(msg, true);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void LoginServer::validateAccountForTransfer(const TransferRequestMoveValidation & request, uint32 clusterId, uint32 sourceCharacterTemplateId, bool canCreateRegular, bool canCreateJedi)
|
|
{
|
|
UNREF(canCreateJedi);
|
|
|
|
// if transfer request was made with console god command, always allow
|
|
// the character to be created on the destination galaxy; we assume
|
|
// the user of the console god command knows that he's doing with regard
|
|
// to making sure that the destination galaxy would not end up with too many
|
|
// characters on the account on the destination galaxy
|
|
if (!canCreateRegular && (request.getTransferRequestSource() == TransferRequestMoveValidation::TRS_console_god_command))
|
|
{
|
|
LOG("CustomerService", ("CharacterTransfer: Account Validation request for %s on %s to %s on %s is complete. Even though canCreateRegular=false, we are setting canCreateRegular=true because this request came from the console god command.", request.getSourceCharacter().c_str(), request.getSourceGalaxy().c_str(), request.getDestinationCharacter().c_str(), request.getDestinationGalaxy().c_str()));
|
|
canCreateRegular = true;
|
|
}
|
|
|
|
LOG("CustomerService", ("CharacterTransfer: Account Validation request for %s on %s to %s on %s is complete. Sending response to CentralServer", request.getSourceCharacter().c_str(), request.getSourceGalaxy().c_str(), request.getDestinationCharacter().c_str(), request.getDestinationGalaxy().c_str()));
|
|
|
|
// if this request came from a game server and the validation fails, send
|
|
// response to the source galaxy so an error message can be displayed to the user
|
|
if ((request.getTransferRequestSource() != TransferRequestMoveValidation::TRS_transfer_server) && !canCreateRegular)
|
|
{
|
|
ClusterListEntry * sourceGalaxyCle = findClusterByName(request.getSourceGalaxy());
|
|
if (sourceGalaxyCle && sourceGalaxyCle->m_centralServerConnection)
|
|
{
|
|
clusterId = sourceGalaxyCle->m_centralServerConnection->getClusterId();
|
|
}
|
|
}
|
|
|
|
ClusterListEntry * cle = findClusterById(clusterId);
|
|
if (cle && cle->m_centralServerConnection)
|
|
{
|
|
TransferReplyMoveValidation response(request.getTransferRequestSource(), request.getTrack(), request.getSourceStationId(), request.getDestinationStationId(), request.getSourceGalaxy(), request.getDestinationGalaxy(), request.getSourceCharacter(), request.getSourceCharacterId(), sourceCharacterTemplateId, request.getDestinationCharacter(), request.getCustomerLocalizedLanguage(), (canCreateRegular ? TransferReplyMoveValidation::TRMVR_can_create_regular_character : TransferReplyMoveValidation::TRMVR_cannot_create_regular_character));
|
|
cle->m_centralServerConnection->send(response, true);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void LoginServer::run(void)
|
|
{
|
|
NetworkHandler::install();
|
|
|
|
#ifdef _DEBUG
|
|
LoginServerRemoteDebugSetup::install();
|
|
#endif
|
|
|
|
DatabaseConnection::getInstance().connect();
|
|
DatabaseConnection::getInstance().requestClusterList();
|
|
SetupSharedLog::install("LoginServer");
|
|
ConsoleManager::install();
|
|
DataTableManager::install();
|
|
AdminAccountManager::install(ConfigLoginServer::getAdminAccountDataTable());
|
|
|
|
unsigned long limit = 20;
|
|
unsigned long startTime;
|
|
unsigned long totalTime = 0;
|
|
|
|
// load authentication data and bind the monitor to the port
|
|
const int port = ConfigLoginServer::getMetricsListenerPort();
|
|
CMonitorAPI *mon = nullptr;
|
|
|
|
if (port) {
|
|
mon = new CMonitorAPI("metricsAuthentication.cfg", ConfigLoginServer::getMetricsListenerPort());
|
|
getInstance().m_soeMonitor = mon;
|
|
const char *masterChannel = "Population";
|
|
mon->add(masterChannel, WORLD_COUNT_CHANNEL);
|
|
std::string host = NetworkHandler::getHostName().c_str();
|
|
size_t dotPos = host.find(".");
|
|
if (dotPos != host.npos) {
|
|
host = host.substr(0, dotPos - 1);
|
|
}
|
|
char tmpBuf[1024];
|
|
IGNORE_RETURN(snprintf(tmpBuf, sizeof(tmpBuf), "LoginServer version %s on %s", ApplicationVersion::getInternalVersion(), host
|
|
.c_str()));
|
|
mon->setDescription(WORLD_COUNT_CHANNEL, tmpBuf);
|
|
|
|
mon->add("Galaxies", CLUSTER_COUNT_CHANNEL);
|
|
}
|
|
|
|
while (!getInstance().done)
|
|
{
|
|
startTime = Clock::timeMs();
|
|
IGNORE_RETURN(Os::update());
|
|
if (getInstance().keyServer->update())
|
|
{
|
|
getInstance().pushKeyToAllServers();
|
|
}
|
|
NetworkHandler::dispatch();
|
|
do
|
|
{
|
|
Os::sleep(1);
|
|
NetworkHandler::update();
|
|
} while (Clock::timeMs() - startTime < limit);
|
|
|
|
DatabaseConnection::getInstance().update();
|
|
PurgeManager::update(static_cast<float>(limit) / 1000.0f);
|
|
if (getInstance().m_clusterStatusChanged)
|
|
{
|
|
getInstance().sendClusterStatusToAll();
|
|
getInstance().m_clusterStatusChanged = false;
|
|
}
|
|
|
|
//if (getInstance().m_sessionApiClient)
|
|
// getInstance().m_sessionApiClient->Process();
|
|
|
|
totalTime += limit; //TODO: make a better way to do this
|
|
if (!ConfigLoginServer::getDevelopmentMode() && (totalTime > 10000))
|
|
{
|
|
getInstance().refreshConnections();
|
|
DatabaseConnection::getInstance().requestClusterList();
|
|
totalTime = 0;
|
|
}
|
|
|
|
NetworkHandler::clearBytesThisFrame();
|
|
|
|
if (port) {
|
|
mon->set(WORLD_COUNT_CHANNEL, static_cast<int>(getInstance().m_clientMap.size()));
|
|
int count = 0;
|
|
ClusterListType::const_iterator i;
|
|
for (i = getInstance().m_clusterList.begin(); i != getInstance().m_clusterList.end(); ++i) {
|
|
if ((*i)->m_connected)
|
|
++count;
|
|
}
|
|
mon->set(CLUSTER_COUNT_CHANNEL, count);
|
|
mon->Update();
|
|
}
|
|
}
|
|
|
|
NetworkHandler::update();
|
|
|
|
ConsoleManager::remove();
|
|
DatabaseConnection::getInstance().disconnect();
|
|
|
|
#ifdef _DEBUG
|
|
LoginServerRemoteDebugSetup::remove();
|
|
#endif
|
|
|
|
SetupSharedLog::remove();
|
|
NetworkHandler::remove();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void LoginServer::sendAvatarList(const StationId& stationId, int stationIdNumberJediSlot, const AvatarList &avatars, TransferCharacterData * const transferData)
|
|
{
|
|
std::vector<EnumerateCharacterId::Chardata> chardata;
|
|
chardata.reserve(avatars.size());
|
|
|
|
EnumerateCharacterId::Chardata temp;
|
|
for (AvatarList::const_iterator i = avatars.begin(); i != avatars.end(); ++i)
|
|
{
|
|
temp.m_name = (*i).m_name;
|
|
temp.m_objectTemplateId = (*i).m_objectTemplateId;
|
|
temp.m_networkId = (*i).m_networkId;
|
|
temp.m_clusterId = (*i).m_clusterId;
|
|
temp.m_characterType = (*i).m_characterType;
|
|
|
|
chardata.push_back(temp);
|
|
}
|
|
|
|
EnumerateCharacterId msg(chardata);
|
|
|
|
if (transferData)
|
|
{
|
|
// send this to the appropriate CentralServer
|
|
if (transferData->getSourceCharacterName().empty())
|
|
{
|
|
// CTS API character list request
|
|
bool result = CentralServerConnection::sendCharacterListResponse(transferData->getSourceStationId(), avatars, *transferData);
|
|
UNREF(result);
|
|
DEBUG_REPORT_LOG(!result, ("Could not send avatar list to StationId %lu because connection has been closed.\n", stationId));
|
|
}
|
|
else
|
|
{
|
|
uint32 characterTemplateId = 0;
|
|
NetworkId characterId = NetworkId::cms_invalid;
|
|
ClusterListEntry const * const cle = findClusterByName(transferData->getSourceGalaxy());
|
|
if (cle)
|
|
{
|
|
// find associated character ID
|
|
for (AvatarList::const_iterator i = avatars.begin(); i != avatars.end(); ++i)
|
|
{
|
|
REPORT_LOG(true, ("checking %s == %s\n", Unicode::wideToNarrow(i->m_name).c_str(), transferData->getSourceCharacterName().c_str()));
|
|
if (Unicode::wideToNarrow(i->m_name) == transferData->getSourceCharacterName()
|
|
&& i->m_clusterId == cle->m_clusterId)
|
|
{
|
|
characterTemplateId = static_cast<uint32>(i->m_objectTemplateId);
|
|
characterId = i->m_networkId;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LOG("CustomerService", ("CharacterTransfer: sendAvatarList() for stationId=%u, characterName=%s, sourceGalaxy=%s, determined characterId=%s, determined character template id=%lu", transferData->getSourceStationId(), transferData->getSourceCharacterName().c_str(), transferData->getSourceGalaxy().c_str(), characterId.getValueString().c_str(), characterTemplateId));
|
|
|
|
transferData->setCharacterId(characterId);
|
|
transferData->setObjectTemplateCrc(characterTemplateId);
|
|
|
|
// part of move request for character transfer system
|
|
const GenericValueTypeMessage<TransferCharacterData> response("TransferReplyCharacterDataFromLoginServer", *transferData);
|
|
CentralServerConnection::sendToCentralServer(transferData->getSourceGalaxy(), response);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ClientConnection *conn = getValidatedClient(stationId);
|
|
if (conn)
|
|
{
|
|
LOG("LoginClientConnection", ("sendAvatarList() for stationId (%lu) at IP (%s), sending avatar list to client", stationId, conn->getRemoteAddress().c_str()));
|
|
|
|
// this message ***MUST*** be sent first, as the client expects to
|
|
// receive this information before receiving the avatar list information
|
|
GenericValueTypeMessage<int> const msgStationIdHasJediSlot("StationIdHasJediSlot", stationIdNumberJediSlot);
|
|
conn->send(msgStationIdHasJediSlot, true);
|
|
|
|
conn->send(msg, true);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("Could not send avatar list to StationId %lu.\n", stationId));
|
|
LOG("LoginClientConnection", ("sendAvatarList() for stationId (%lu), cannot find connection to client for sending avatar list", stationId));
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::performAccountTransfer(const AvatarList &avatars, TransferAccountData * transferAccountData)
|
|
{
|
|
// grab the station ids
|
|
StationId sourceStationId = transferAccountData->getSourceStationId();
|
|
StationId destinationStationId = transferAccountData->getDestinationStationId();
|
|
|
|
// we need to get the avatars' cluster and name for use in the chat transfer process
|
|
// also send a message to modify the game server databases for each of the clusterIds in transferAccountData
|
|
// avatar data is clusterName, avatarName
|
|
std::vector<AvatarData> avatarData;
|
|
for (AvatarList::const_iterator i = avatars.begin(); i != avatars.end(); ++i)
|
|
{
|
|
ClusterListEntry * cluster = findClusterById(i->m_clusterId);
|
|
|
|
if (cluster)
|
|
{
|
|
std::string clusterName = cluster->m_clusterName;
|
|
std::string avatarName = Unicode::wideToNarrow(i->m_name);
|
|
|
|
avatarData.push_back(AvatarData(clusterName, avatarName));
|
|
|
|
LOG("CustomerService", ("CharacterTransfer: Sending request to update game db (via cluster: %s) for account transfer from %lu to %lu (avatar: %s)", clusterName.c_str(), sourceStationId, destinationStationId, avatarName.c_str()));
|
|
const GenericValueTypeMessage<TransferAccountData> message("TransferAccountRequestCentralDatabase", *transferAccountData);
|
|
sendToCluster(i->m_clusterId, message);
|
|
}
|
|
else
|
|
{
|
|
LOG("CustomerService", ("CharacterTransfer: Could not connect to cluster id %lu to update game db for account transfer from %lu to %lu (avatar: %s)", i->m_clusterId, sourceStationId, destinationStationId, Unicode::wideToNarrow(i->m_name).c_str()));
|
|
const GenericValueTypeMessage<TransferAccountData> response("TransferAccountToAccountFailedToUpdateGameDatabase", *transferAccountData);
|
|
CentralServerConnection::sendToCentralServer(transferAccountData->getStartGalaxy(), response);
|
|
return;
|
|
}
|
|
}
|
|
|
|
transferAccountData->setSourceAvatarData(avatarData);
|
|
|
|
// we also need to modify the station id for all characters for the loginServer - this will contact the centralServer when it is complete to finish the transfer process
|
|
DatabaseConnection::getInstance().changeStationId(sourceStationId, destinationStationId, transferAccountData);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::onValidateClient(StationId suid, const std::string & username, ClientConnection *conn, bool isSecure, const char* sessionKey, uint32 gameBits, uint32 subscriptionBits)
|
|
{
|
|
UNREF(username);
|
|
UNREF(gameBits);
|
|
UNREF(subscriptionBits);
|
|
NOT_NULL(conn);
|
|
WARNING_STRICT_FATAL(getValidatedClient(suid), ("Validating an already valid client in onValidateClient(). StationId: %d UserName: %s", suid, username.c_str()));
|
|
|
|
int adminLevel = 0;
|
|
const bool isAdminAccount = AdminAccountManager::isAdminAccount(Unicode::toLower(username), adminLevel);
|
|
|
|
if (conn->getRequestedAdminSuid() != 0)
|
|
{
|
|
//verify internal, secure, is on the god list
|
|
bool loginOK = false;
|
|
if (!isSecure)
|
|
LOG("CustomerService", ("AdminLogin: User %s (account %li) attempted to log into account %li, but was not using a SecureID token", username.c_str(), suid, conn->getRequestedAdminSuid()));
|
|
else
|
|
{
|
|
if (!AdminAccountManager::isInternalIp(conn->getRemoteAddress()))
|
|
LOG("CustomerService", ("AdminLogin: User %s (account %li) attempted to log into account %li, but was not logging in from an internal IP", username.c_str(), suid, conn->getRequestedAdminSuid()));
|
|
else
|
|
{
|
|
if (!isAdminAccount || adminLevel < 10)
|
|
LOG("CustomerService", ("AdminLogin: User %s (account %li) attempted to log into account %li, but did not have sufficient permissions", username.c_str(), suid, conn->getRequestedAdminSuid()));
|
|
else
|
|
{
|
|
suid = conn->getRequestedAdminSuid();
|
|
loginOK = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!loginOK)
|
|
{
|
|
conn->disconnect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// encrypt the clients credentials with the key, return
|
|
// the cipher text to the client for use as a connection
|
|
// token with the central server
|
|
|
|
//Also the sizeof(int) is likewise magic from the session api
|
|
size_t len = sizeof(uint32) + sizeof(bool) + MAX_ACCOUNT_NAME_LENGTH + 1;
|
|
unsigned char * keyBuffer = new unsigned char[len];
|
|
unsigned char * keyBufferPointer = keyBuffer;
|
|
|
|
IGNORE_RETURN(memset(keyBuffer, 0, len));
|
|
|
|
if (ConfigLoginServer::getDoConsumption() || ConfigLoginServer::getDoSessionLogin())
|
|
{
|
|
std::string const strSessionKey(sessionKey);
|
|
size_t sessSize = sizeof(strSessionKey.c_str());
|
|
|
|
// pass the sessionkey
|
|
len = sessSize + sizeof(StationId);
|
|
memcpy(keyBufferPointer, sessionKey, sessSize);
|
|
keyBufferPointer += len;
|
|
memcpy(keyBufferPointer, &suid, sizeof(StationId));
|
|
|
|
// if LoginServer did session login, send the session key back to the client;
|
|
// the client normally gets the session key from the LaunchPad, but in this mode
|
|
// where the LoginServer does the session login, it will get it from the LoginServer
|
|
if (ConfigLoginServer::getDoSessionLogin())
|
|
{
|
|
GenericValueTypeMessage<std::string> const msg("SetSessionKey", strSessionKey);
|
|
conn->send(msg, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we aren't validating sessions, pack username, security status, and username to connectionserver
|
|
memcpy(keyBufferPointer, &suid, sizeof(uint32)); //lint !e64 !e119 !e534 (lint isn't resolving memcpy properly)
|
|
keyBufferPointer += sizeof(uint32);
|
|
memcpy(keyBufferPointer, &isSecure, sizeof(bool)); //lint !e64 !e119 !e534
|
|
keyBufferPointer += sizeof(bool);
|
|
memcpy(keyBufferPointer, username.c_str(), MAX_ACCOUNT_NAME_LENGTH); //lint !e64 !e119 !e534
|
|
}
|
|
|
|
KeyShare::Token token = LoginServer::getInstance().makeToken(keyBuffer, len);
|
|
Archive::ByteStream a;
|
|
token.pack(a);
|
|
|
|
const LoginClientToken k(a.getBuffer(), static_cast<unsigned char>(a.getSize()), static_cast<uint32>(suid), username);
|
|
conn->send(k, true);
|
|
delete[] keyBuffer;
|
|
|
|
// send cluster enum
|
|
bool clientInternal = AdminAccountManager::isInternalIp(conn->getRemoteAddress());
|
|
|
|
std::vector<LoginEnumCluster::ClusterData> data;
|
|
|
|
for (ClusterListType::const_iterator j = m_clusterList.begin(); j != m_clusterList.end(); ++j)
|
|
{
|
|
ClusterListEntry *cle = *j;
|
|
if (cle && cle->m_clusterId != 0 && cle->m_clusterName.size() != 0 && (clientInternal || !cle->m_secret))
|
|
{
|
|
LoginEnumCluster::ClusterData item;
|
|
item.m_clusterId = cle->m_clusterId;
|
|
item.m_clusterName = cle->m_clusterName;
|
|
item.m_timeZone = cle->m_timeZone;
|
|
data.push_back(item);
|
|
}
|
|
}
|
|
|
|
LoginEnumCluster e(data, ConfigLoginServer::getMaxCharactersPerAccount());
|
|
|
|
conn->send(e, true);
|
|
|
|
//Send off list of cluster that has character
|
|
//creation disabled, even if the list is empty
|
|
//***MUST*** be done after sending LoginEnumCluster
|
|
GenericValueTypeMessage<std::set<std::string> > const msgCharacterCreationDisabledClusterList("CharacterCreationDisabled", ConfigLoginServer::getCharacterCreationDisabledClusterList());
|
|
conn->send(msgCharacterCreationDisabledClusterList, true);
|
|
|
|
//Send off request for the avatar list from the database.
|
|
DatabaseConnection::getInstance().requestAvatarListForAccount(suid, 0);
|
|
LOG("LoginClientConnection", ("onValidateClient() for stationId (%lu) at IP (%s), id (%s), requesting avatar list for account", suid, conn->getRemoteAddress().c_str(), username.c_str()));
|
|
|
|
//Set up the connection as being validated with this suid.
|
|
conn->setIsValidated(true);
|
|
conn->setStationId(suid);
|
|
conn->setIsSecure(isSecure);
|
|
conn->setAdminLevel(isAdminAccount ? adminLevel : -1);
|
|
IGNORE_RETURN(m_validatedClientMap.insert(std::pair<StationId, ClientConnection*>(suid, conn)));
|
|
|
|
//Must be done after setting various information in the connection object above
|
|
sendClusterStatus(*conn);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
LoginServer::ClusterListEntry *LoginServer::findClusterByName(const std::string &clusterName)
|
|
{
|
|
ClusterListType::iterator i;
|
|
for (i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
NOT_NULL(*i); // not legal to push nullptr pointers on the vector
|
|
if ((*i)->m_clusterName == clusterName)
|
|
return *i;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
uint32 LoginServer::getClusterIDByName(const std::string &sName)
|
|
{
|
|
ClusterListEntry const * const p_cluster = findClusterByName(sName);
|
|
return p_cluster ? p_cluster->m_clusterId : 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
const std::string & LoginServer::getClusterNameById(uint32 clusterId)
|
|
{
|
|
static std::string const empty;
|
|
|
|
ClusterListEntry const * const cle = findClusterById(clusterId);
|
|
if (cle)
|
|
return cle->m_clusterName;
|
|
|
|
return empty;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
LoginServer::ClusterListEntry *LoginServer::addCluster(const std::string &clusterName)
|
|
{
|
|
DEBUG_FATAL(!ConfigLoginServer::getDevelopmentMode(), ("Programmer bug: addCluster(std::string&) can only be used in development mode.\n"));
|
|
|
|
ClusterListEntry *e = new ClusterListEntry;
|
|
e->m_clusterName = clusterName;
|
|
m_clusterList.push_back(e);
|
|
return e;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::sendExtendedClusterInfo(ClientConnection &client) const
|
|
{
|
|
std::vector<LoginClusterStatusEx::ClusterData> data;
|
|
|
|
for (ClusterListType::const_iterator j = m_clusterList.begin(); j != m_clusterList.end(); ++j)
|
|
{
|
|
ClusterListEntry *cle = *j;
|
|
|
|
if (cle)
|
|
{
|
|
LoginClusterStatusEx::ClusterData item;
|
|
item.m_clusterId = cle->m_clusterId;
|
|
item.m_branch = cle->m_branch;
|
|
item.m_version = cle->m_changelist;
|
|
item.m_networkVersion = cle->m_networkVersion;
|
|
data.push_back(item);
|
|
}
|
|
}
|
|
|
|
client.send(LoginClusterStatusEx(data), true);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Send the list of active clusters to a client.
|
|
* Also picks a connection server for the client to use.
|
|
* @todo We'd like to resend this to all clients whenever the status
|
|
* of any servers changes
|
|
*/
|
|
void LoginServer::sendClusterStatus(ClientConnection &conn) const
|
|
{
|
|
const bool clientIsPrivate = AdminAccountManager::isInternalIp(conn.getRemoteAddress());
|
|
const unsigned int subscriptionBits = conn.getSubscriptionBits();
|
|
const bool isFreeTrialAccount = (((subscriptionBits & ClientSubscriptionFeature::FreeTrial) != 0)
|
|
&& ((subscriptionBits & ClientSubscriptionFeature::Base) == 0));
|
|
|
|
std::vector<LoginClusterStatus::ClusterData> data;
|
|
std::vector<LoginClusterStatusEx::ClusterData> dataEx;
|
|
|
|
for (ClusterListType::const_iterator j = m_clusterList.begin(); j != m_clusterList.end(); ++j)
|
|
{
|
|
ClusterListEntry *cle = *j;
|
|
// Cluster is OK for login if
|
|
// 1) We're connected to Central
|
|
// 2) We know the cluster Id
|
|
// 3) We have at least one Connection Server
|
|
// 4) Client is internal or the cluster is not secret
|
|
// 5) Cluster has told us its ready for players
|
|
if (cle && cle->m_clusterId != 0 && cle->m_connected && !cle->m_connectionServers.empty() && (clientIsPrivate || !cle->m_secret))
|
|
{
|
|
DEBUG_FATAL(!cle->m_centralServerConnection, ("Programmer bug: m_connected was true but m_centralServerConnection was nullptr\n"));
|
|
LoginClusterStatus::ClusterData item;
|
|
item.m_clusterId = cle->m_clusterId;
|
|
item.m_timeZone = cle->m_timeZone;
|
|
|
|
// size_t connectionServerChoice = Random::random(cle->m_connectionServers.size() - 1); //lint !e713 !e732 // loss of precision (arg no 1)
|
|
ConnectionServerEntry &connServer = cle->m_connectionServers[0];
|
|
|
|
item.m_connectionServerAddress = connServer.clientServiceAddress;
|
|
if (clientIsPrivate)
|
|
{
|
|
item.m_connectionServerPort = connServer.clientServicePortPrivate;
|
|
}
|
|
else
|
|
{
|
|
item.m_connectionServerPort = connServer.clientServicePortPublic;
|
|
}
|
|
if (item.m_connectionServerPort)
|
|
{
|
|
item.m_connectionServerPingPort = connServer.pingPort;
|
|
|
|
// for security/confidential information issue, only report
|
|
// population count to secured internal connections with
|
|
// admin privilege >= 10
|
|
if (clientIsPrivate && conn.getIsSecure() && (conn.getAdminLevel() >= 10))
|
|
item.m_populationOnline = cle->m_numPlayers;
|
|
else
|
|
item.m_populationOnline = -1;
|
|
|
|
const int percentFull = cle->m_numPlayers * 100 / cle->m_onlinePlayerLimit;
|
|
if (percentFull >= 100)
|
|
item.m_populationOnlineStatus = LoginClusterStatus::ClusterData::PS_full;
|
|
else if (percentFull >= ConfigLoginServer::getPopulationExtremelyHeavyThresholdPercent())
|
|
item.m_populationOnlineStatus = LoginClusterStatus::ClusterData::PS_extremely_heavy;
|
|
else if (percentFull >= ConfigLoginServer::getPopulationVeryHeavyThresholdPercent())
|
|
item.m_populationOnlineStatus = LoginClusterStatus::ClusterData::PS_very_heavy;
|
|
else if (percentFull >= ConfigLoginServer::getPopulationHeavyThresholdPercent())
|
|
item.m_populationOnlineStatus = LoginClusterStatus::ClusterData::PS_heavy;
|
|
else if (percentFull >= ConfigLoginServer::getPopulationMediumThresholdPercent())
|
|
item.m_populationOnlineStatus = LoginClusterStatus::ClusterData::PS_medium;
|
|
else if (percentFull >= ConfigLoginServer::getPopulationLightThresholdPercent())
|
|
item.m_populationOnlineStatus = LoginClusterStatus::ClusterData::PS_light;
|
|
else
|
|
item.m_populationOnlineStatus = LoginClusterStatus::ClusterData::PS_very_light;
|
|
|
|
item.m_maxCharactersPerAccount = cle->m_maxCharactersPerAccount;
|
|
if (cle->m_readyForPlayers)
|
|
{
|
|
item.m_status = LoginClusterStatus::ClusterData::S_up;
|
|
if (cle->m_numTutorialPlayers >= cle->m_onlineTutorialLimit)
|
|
item.m_status = LoginClusterStatus::ClusterData::S_restricted;
|
|
if (isFreeTrialAccount && !cle->m_freeTrialCanCreateChar)
|
|
{
|
|
item.m_status = LoginClusterStatus::ClusterData::S_restricted;
|
|
}
|
|
if ((cle->m_numPlayers >= cle->m_onlinePlayerLimit)
|
|
|| (isFreeTrialAccount && (cle->m_numFreeTrialPlayers >= cle->m_onlineFreeTrialLimit)))
|
|
{
|
|
item.m_status = LoginClusterStatus::ClusterData::S_full;
|
|
}
|
|
}
|
|
else
|
|
item.m_status = LoginClusterStatus::ClusterData::S_loading;
|
|
if (cle->m_locked && !clientIsPrivate)
|
|
item.m_status = LoginClusterStatus::ClusterData::S_locked; // locked takes precedence over up or loading
|
|
|
|
item.m_dontRecommend = (cle->m_notRecommendedDatabase || cle->m_notRecommendedCentral);
|
|
item.m_onlinePlayerLimit = cle->m_onlinePlayerLimit;
|
|
item.m_onlineFreeTrialLimit = cle->m_onlineFreeTrialLimit;
|
|
|
|
data.push_back(item);
|
|
}
|
|
|
|
++connServer.numClients;
|
|
std::sort(cle->m_connectionServers.begin(), cle->m_connectionServers.end(), ConnectionServerEntryLessThan());
|
|
}
|
|
|
|
if (cle)
|
|
{
|
|
LoginClusterStatusEx::ClusterData itemEx;
|
|
itemEx.m_clusterId = cle->m_clusterId;
|
|
itemEx.m_branch = cle->m_branch;
|
|
itemEx.m_version = cle->m_changelist;
|
|
dataEx.push_back(itemEx);
|
|
}
|
|
}
|
|
|
|
LoginClusterStatus msg(data);
|
|
conn.send(msg, true);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Send the list of active clusters to all connected clients.
|
|
*/
|
|
void LoginServer::sendClusterStatusToAll() const
|
|
{
|
|
for (std::map<StationId, ClientConnection*>::const_iterator i = m_validatedClientMap.begin(); i != m_validatedClientMap.end(); ++i)
|
|
{
|
|
if (i->second)
|
|
sendClusterStatus(*(i->second));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
LoginServer::ClusterListEntry::ClusterListEntry() :
|
|
m_clusterId(0),
|
|
m_clusterName(),
|
|
m_centralServerConnection(0),
|
|
m_connectionServers(),
|
|
m_numPlayers(0),
|
|
m_numFreeTrialPlayers(0),
|
|
m_numTutorialPlayers(0),
|
|
m_maxCharacters(ConfigLoginServer::getMaxCharactersPerCluster()),
|
|
m_maxCharactersPerAccount(0),
|
|
m_onlinePlayerLimit(0),
|
|
m_onlineFreeTrialLimit(0),
|
|
m_onlineTutorialLimit(0),
|
|
m_freeTrialCanCreateChar(false),
|
|
m_timeZone(0),
|
|
m_connected(false),
|
|
m_address(""),
|
|
m_port(0),
|
|
m_allowReconnect(true),
|
|
m_secret(false),
|
|
m_readyForPlayers(false),
|
|
m_locked(false),
|
|
m_notRecommendedDatabase(false),
|
|
m_notRecommendedCentral(false),
|
|
m_changelist(0)
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Called as we retrieve clusters from the database. If any of the data
|
|
* has changed, disconnect and reconnect.
|
|
* If we don't know about the cluster already, add it to the list.
|
|
*/
|
|
void LoginServer::updateClusterData(uint32 clusterId, const std::string &clusterName, const std::string &address, const uint16 port, bool secret, bool locked, bool notRecommended, int maxCharactersPerAccount, int onlinePlayerLimit, int onlineFreeTrialLimit, bool freeTrialCanCreateChar, int onlineTutorialLimit)
|
|
{
|
|
ClusterListEntry *cle = findClusterById(clusterId);
|
|
|
|
if (ConfigLoginServer::getDevelopmentMode())
|
|
{
|
|
if (!cle)
|
|
cle = findClusterByName(clusterName);
|
|
}
|
|
else
|
|
{
|
|
if (cle)
|
|
{
|
|
// refreshing data on a cluster we already know about
|
|
if ((cle->m_clusterName != clusterName) || (cle->m_address != address) || (cle->m_port != port))
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("Disconnecting from cluster %lu because its data has changed in the database.\n", clusterId));
|
|
disconnectCluster(*cle, true, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!cle)
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("Cluster %lu: %s\n", clusterId, clusterName.c_str()));
|
|
|
|
cle = new ClusterListEntry;
|
|
m_clusterList.push_back(cle);
|
|
}
|
|
|
|
if ((cle->m_secret != secret)
|
|
|| (cle->m_locked != locked)
|
|
|| (cle->m_onlinePlayerLimit != onlinePlayerLimit)
|
|
|| (cle->m_onlineFreeTrialLimit != onlineFreeTrialLimit)
|
|
|| (cle->m_freeTrialCanCreateChar != freeTrialCanCreateChar)
|
|
|| (cle->m_onlineTutorialLimit != onlineTutorialLimit))
|
|
{
|
|
// The cluster may now be locked/unlocked to some players, so let them know
|
|
m_clusterStatusChanged = true;
|
|
}
|
|
|
|
// tell the cluster if its locked or secret state changes
|
|
if (((cle->m_locked != locked) || (cle->m_secret != secret)) && cle->m_connected && cle->m_centralServerConnection)
|
|
{
|
|
GenericValueTypeMessage<std::pair<bool, bool> > const msgState("UpdateClusterLockedAndSecretState", std::make_pair(locked, secret));
|
|
cle->m_centralServerConnection->send(msgState, true);
|
|
}
|
|
|
|
bool const clusterIdSet = ((cle->m_clusterId == 0) && (clusterId > 0));
|
|
cle->m_clusterId = clusterId;
|
|
cle->m_clusterName = clusterName;
|
|
cle->m_address = address;
|
|
cle->m_port = port;
|
|
cle->m_secret = secret;
|
|
cle->m_locked = locked;
|
|
cle->m_notRecommendedDatabase = notRecommended;
|
|
cle->m_maxCharactersPerAccount = maxCharactersPerAccount;
|
|
cle->m_onlinePlayerLimit = onlinePlayerLimit;
|
|
cle->m_onlineFreeTrialLimit = onlineFreeTrialLimit;
|
|
cle->m_freeTrialCanCreateChar = freeTrialCanCreateChar;
|
|
cle->m_onlineTutorialLimit = onlineTutorialLimit;
|
|
if (cle->m_centralServerConnection)
|
|
{
|
|
cle->m_centralServerConnection->setClusterId(clusterId);
|
|
|
|
// tell the cluster its cluster id
|
|
if (clusterIdSet && ConfigLoginServer::getDevelopmentMode())
|
|
{
|
|
GenericValueTypeMessage<uint32> const msgClusterId("ClusterId", cle->m_clusterId);
|
|
cle->m_centralServerConnection->send(msgClusterId, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Remove a connection to a cluster.
|
|
* @param cle The ClusterListEntry representing the cluster.
|
|
* @param forceDisconnect If true, drops the network connection. If false, assumes
|
|
* the network connection has already been closed
|
|
* @param reconnect If true, will attempt to reestablish connection to the cluster
|
|
* (does not apply in development mode)
|
|
*/
|
|
void LoginServer::disconnectCluster(ClusterListEntry &cle, bool const forceDisconnect, bool const reconnect)
|
|
{
|
|
if (cle.m_centralServerConnection && forceDisconnect)
|
|
cle.m_centralServerConnection->disconnect();
|
|
|
|
cle.m_centralServerConnection = 0; //TODO: revisit after connection deletion issues are fixed
|
|
cle.m_allowReconnect = reconnect;
|
|
cle.m_connectionServers.clear();
|
|
|
|
bool statusChange = false;
|
|
|
|
if (cle.m_connected || cle.m_readyForPlayers)
|
|
statusChange = true;
|
|
|
|
cle.m_connected = false;
|
|
cle.m_readyForPlayers = false;
|
|
|
|
m_clusterStatusChanged = statusChange;
|
|
|
|
// for "carousel" cluster, where multiple clusters point to the same address,
|
|
// when disconnecting a "carousel" cluster, need to flag the other "carousel"
|
|
// clusters to allow reconnect so that another "carousel" cluster can be
|
|
// started, and the LoginServer will connect to it
|
|
if (!ConfigLoginServer::getDevelopmentMode() && reconnect)
|
|
{
|
|
for (ClusterListType::iterator i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
NOT_NULL(*i);
|
|
if (!(*i)->m_centralServerConnection && !(*i)->m_allowReconnect && ((*i)->m_address == cle.m_address))
|
|
{
|
|
(*i)->m_allowReconnect = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Called after we've added a new cluster to the database.
|
|
*/
|
|
void LoginServer::onClusterRegistered(uint32 clusterId, const std::string &clusterName)
|
|
{
|
|
DEBUG_FATAL(!ConfigLoginServer::getDevelopmentMode(), ("Programmer bug: should not be registering new clusters automatically unless running in development mode.\n"));
|
|
static std::string noAddress("");
|
|
updateClusterData(clusterId, clusterName, noAddress, 0, false, false, false, 8, 100, 100, true, 350);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Find a cluster by ID.
|
|
*/
|
|
|
|
LoginServer::ClusterListEntry * LoginServer::findClusterById(uint32 clusterId)
|
|
{
|
|
ClusterListType::const_iterator i;
|
|
for (i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
NOT_NULL(*i); // not legal to push nullptr pointers on the vector
|
|
if ((*i)->m_clusterId == clusterId)
|
|
return *i;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Find a cluster list entry using the connection to Central
|
|
*/
|
|
|
|
LoginServer::ClusterListEntry * LoginServer::findClusterByConnection(const CentralServerConnection *connection)
|
|
{
|
|
ClusterListType::const_iterator i;
|
|
for (i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
NOT_NULL(*i); // not legal to push nullptr pointers on the vector
|
|
if ((*i)->m_centralServerConnection == connection)
|
|
return *i;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::refreshConnections()
|
|
{
|
|
if (ConfigLoginServer::getDevelopmentMode())
|
|
return; // in development mode, we don't establish any connections
|
|
ClusterListType::iterator i;
|
|
for (i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
NOT_NULL(*i);
|
|
if (!(*i)->m_centralServerConnection && (*i)->m_allowReconnect)
|
|
{
|
|
(*i)->m_centralServerConnection = new CentralServerConnection(
|
|
(*i)->m_address,
|
|
(*i)->m_port,
|
|
(*i)->m_clusterName,
|
|
(*i)->m_clusterId);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::sendToCluster(uint32 clusterId, const GameNetworkMessage &message)
|
|
{
|
|
ClusterListEntry *cle = findClusterById(clusterId);
|
|
if (cle && cle->m_connected && cle->m_centralServerConnection)
|
|
cle->m_centralServerConnection->send(message, true);
|
|
else
|
|
DEBUG_REPORT_LOG(true, ("Could not send message to cluster %lu because it was not connected.\n", clusterId));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void LoginServer::setDone(const bool isDone)
|
|
{
|
|
done = isDone;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::sendToAllClusters(GameNetworkMessage const & message, Connection const * excludeCentralConnection /*= nullptr*/, uint32 excludeClusterId /*= 0*/, char const * excludeClusterName /*= nullptr*/)
|
|
{
|
|
ClusterListType::const_iterator i;
|
|
for (i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
if (NON_NULL(*i)->m_connected && (*i)->m_centralServerConnection && ((*i)->m_centralServerConnection != excludeCentralConnection) && ((*i)->m_clusterId != excludeClusterId) && ((excludeClusterName == nullptr) || _stricmp(excludeClusterName, (*i)->m_centralServerConnection->getClusterName().c_str())))
|
|
(*i)->m_centralServerConnection->send(message, true);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool LoginServer::areAllClustersUp() const
|
|
{
|
|
if (m_clusterList.empty())
|
|
return false;
|
|
|
|
ClusterListType::const_iterator i;
|
|
for (i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
if (!(NON_NULL(*i)->m_readyForPlayers))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::getAllClusterNamesAndIDs(std::map< std::string, uint32 > &results) const
|
|
{
|
|
results.clear();
|
|
for (ClusterListType::const_iterator i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
{
|
|
if (!*i)
|
|
continue;
|
|
results[(*i)->m_clusterName] = (*i)->m_clusterId;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::getClusterIds(std::vector<uint32> result)
|
|
{
|
|
result.reserve(m_clusterList.size());
|
|
for (ClusterListType::const_iterator i = m_clusterList.begin(); i != m_clusterList.end(); ++i)
|
|
result.push_back(NON_NULL(*i)->m_clusterId);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void LoginServer::setClusterInfoByName(const std::string &name, const std::string &branch, int changelist, const std::string &networkVersion)
|
|
{
|
|
ClusterListEntry *entry = findClusterByName(name);
|
|
|
|
if (entry)
|
|
{
|
|
entry->m_branch = branch;
|
|
entry->m_changelist = changelist;
|
|
entry->m_networkVersion = networkVersion;
|
|
}
|
|
}
|
|
|
|
// ======================================================================
|