// PseudoClientConnection.cpp // copyright 2001 Verant Interactive //----------------------------------------------------------------------- #include "FirstConnectionServer.h" #include "PseudoClientConnection.h" #include "CentralConnection.h" #include "ConnectionServer.h" #include "UnicodeUtils.h" #include "serverNetworkMessages/CentralConnectionServerMessages.h" #include "serverNetworkMessages/GameConnectionServerMessages.h" #include "serverNetworkMessages/GameServerForLoginMessage.h" #include "serverNetworkMessages/NewClient.h" #include "serverNetworkMessages/RequestGameServerForLoginMessage.h" #include "serverNetworkMessages/TransferCharacterDataArchive.h" #include "sharedLog/Log.h" #include "sharedMath/Vector.h" #include "sharedNetworkMessages/GenericValueTypeMessage.h" #include "sharedUtility/StartingLocationData.h" #include "sharedUtility/StartingLocationManager.h" //----------------------------------------------------------------------- namespace PseudoClientConnectionNamespace { std::map s_pseudoClientConnectionMap; std::map s_pseudoClientConnectionMapByCharacterId; } using namespace PseudoClientConnectionNamespace; //----------------------------------------------------------------------- /* Constructed by way of CentralServerConnection to create a psueudo-client. The CentralServer sends TransferCharacterData when it receives a TransferLoginRequest from the TransferServer */ PseudoClientConnection::PseudoClientConnection(const TransferCharacterData & transferDataFromCentralServer, unsigned int stationId) : m_transferCharacterData(transferDataFromCentralServer), m_gameConnection(0), m_trackStationId(stationId) { REPORT_LOG(true, ("ConnectionServer: creating a PseudoClientConnection from transferCharacterData: %s\n", transferDataFromCentralServer.toString().c_str())); // tell CentralServer about this PseudoClientConnection GenericValueTypeMessage > info("NewPseudoClientConnection", std::make_pair(m_trackStationId, static_cast(m_transferCharacterData.getTransferRequestSource()))); CentralConnection * centralServerConnection = ConnectionServer::getCentralConnection(); if(centralServerConnection) { centralServerConnection->send(info, true); } s_pseudoClientConnectionMap[m_trackStationId] = this; s_pseudoClientConnectionMapByCharacterId[m_transferCharacterData.getCharacterId()] = this; } //----------------------------------------------------------------------- PseudoClientConnection::~PseudoClientConnection() { // tell CentralServer that this PseudoClientConnection is no longer relevant GenericValueTypeMessage info("DestroyPseudoClientConnection", m_trackStationId); CentralConnection * centralServerConnection = ConnectionServer::getCentralConnection(); if(centralServerConnection) { centralServerConnection->send(info, true); } std::map::iterator f = s_pseudoClientConnectionMap.find(m_trackStationId); if(f != s_pseudoClientConnectionMap.end()) { s_pseudoClientConnectionMap.erase(f); } std::map::iterator cf = s_pseudoClientConnectionMapByCharacterId.find(m_transferCharacterData.getCharacterId()); if(cf !=s_pseudoClientConnectionMapByCharacterId.end()) { s_pseudoClientConnectionMapByCharacterId.erase(cf); } cf = s_pseudoClientConnectionMapByCharacterId.find(m_transferCharacterData.getDestinationCharacterId()); if(cf !=s_pseudoClientConnectionMapByCharacterId.end()) { s_pseudoClientConnectionMapByCharacterId.erase(cf); } m_gameConnection = 0; } //----------------------------------------------------------------------- const TransferCharacterData & PseudoClientConnection::getTransferCharacterData() const { return m_transferCharacterData; } //----------------------------------------------------------------------- void PseudoClientConnection::controlAssumed() { if(m_transferCharacterData.getCSToolId() > 0) { LOG("CustomerService", ("CharacterTransfer: Received ControlAssumedMessage from GameServer for CS Tool login character request (or from \"remote object loginCharacter\" console command). The request has been completed and the character is now logged in.")); // disconnect them, we logged them in for the CS Tool. DropClient dropMsg(m_transferCharacterData.getCharacterId()); if(m_gameConnection) { dropMsg.setImmediate(true); m_gameConnection->send(dropMsg, true); } delete this; return; } LOG("CustomerService", ("CharacterTransfer: Received ControlAssumedMessage from GameServer, ready to request bank to be loaded (if source character) or to apply transfer data (if target character)!")); if(m_gameConnection) { if(m_transferCharacterData.getScriptDictionaryData().size()) { LOG("CustomerService", ("CharacterTransfer: controlAssumed, already have transfer data, sending ApplyTransferData")); // already have the transfer data, this character is headed to the destination galaxy GenericValueTypeMessage applyTransferData("ApplyTransferData", m_transferCharacterData); m_gameConnection->send(applyTransferData, true); } else { LOG("CustomerService", ("CharacterTransfer: controlAssumed, sending RequestLoadCTSBank message")); GenericValueTypeMessage requestTransferData("RequestLoadCTSBank", m_transferCharacterData.getCharacterId()); m_gameConnection->send(requestTransferData, true); } } } //----------------------------------------------------------------------- void PseudoClientConnection::onBankLoaded() { LOG("CustomerService", ("CharacterTransfer: Received BankLoaded from GameServer, ready to request transfer data!")); if(m_gameConnection) { if(!m_transferCharacterData.getScriptDictionaryData().size()) { GenericValueTypeMessage requestTransferData("RequestLoadPackedHouses", m_transferCharacterData.getCharacterId()); m_gameConnection->send(requestTransferData, true); } else LOG("CustomerService", ("CharacterTransfer: OnBankLoaded, RequestTransferData already has data")); } } //----------------------------------------------------------------------- void PseudoClientConnection::onPackedHousesLoaded() { LOG("CustomerService", ("CharacterTransfer: Received PackedHousesLoadedMessage from GameServer, ready to request transfer data!")); if(m_gameConnection) { if(!m_transferCharacterData.getScriptDictionaryData().size()) { GenericValueTypeMessage requestTransferData("RequestTransferData", m_transferCharacterData); m_gameConnection->send(requestTransferData, true); } else LOG("CustomerService", ("CharacterTransfer: onPackedHousesLoaded, RequestTransferData already has data")); } } //----------------------------------------------------------------------- PseudoClientConnection * PseudoClientConnection::getPseudoClientConnection(const NetworkId & characterId) { PseudoClientConnection * result = 0; std::map::iterator f = s_pseudoClientConnectionMapByCharacterId.find(characterId); if(f != s_pseudoClientConnectionMapByCharacterId.end()) { result = f->second; } return result; } //----------------------------------------------------------------------- PseudoClientConnection * PseudoClientConnection::getPseudoClientConnection (unsigned int stationId) { PseudoClientConnection * result = 0; std::map::iterator f = s_pseudoClientConnectionMap.find(stationId); if(f != s_pseudoClientConnectionMap.end()) { result = f->second; } return result; } //----------------------------------------------------------------------- /** Find a game server to connect the pseudo client using the source station id indicated by m_transferCharacterData.m_sourceStationId */ void PseudoClientConnection::requestGameServerForLogin() const { NetworkId characterId = m_transferCharacterData.getCharacterId(); unsigned int stationId = m_trackStationId; NetworkId containerId = m_transferCharacterData.getContainerId(); if(m_transferCharacterData.getScriptDictionaryData().size()) { characterId = m_transferCharacterData.getDestinationCharacterId(); containerId = NetworkId::cms_invalid; } RequestGameServerForLoginMessage requestmsg(stationId, characterId, containerId, m_transferCharacterData.getScene(), m_transferCharacterData.getStartingCoordinates(), (m_transferCharacterData.getScriptDictionaryData().empty() && (m_transferCharacterData.getCSToolId() == 0))); if(ConnectionServer::getCentralConnection()) { ConnectionServer::getCentralConnection()->send(requestmsg, true); LOG("CustomerService", ("CharacterTransfer: ***ConnectionServer: sending RequestGameServerForLoginMessage(%d, %s, %s, %s)\n", m_transferCharacterData.getSourceStationId(), m_transferCharacterData.getCharacterId().getValueString().c_str(), m_transferCharacterData.getContainerId().getValueString().c_str(), m_transferCharacterData.getScene().c_str())); } } //----------------------------------------------------------------------- void PseudoClientConnection::receiveMessage(const Archive::ByteStream & message) { Archive::ReadIterator ri = message.begin(); const GameNetworkMessage msg(ri); ri = message.begin(); if(msg.isType("GameServerForLoginMessage")) { const GameServerForLoginMessage gameServerForLogin(ri); LOG("CustomerService", ("CharacterTransfer: *** ConnectionServer: Received GameServerForLoginMessage for %d, server=%d\n", gameServerForLogin.getStationId(), gameServerForLogin.getServer())); m_gameConnection = ConnectionServer::getGameConnection(gameServerForLogin.getServer()); if(m_gameConnection) { NetworkId characterId = m_transferCharacterData.getCharacterId(); unsigned int stationId = m_trackStationId; if(m_transferCharacterData.getScriptDictionaryData().size()) { LOG("CustomerService", ("CharacterTransfer: Setting up login for destination server")); characterId = m_transferCharacterData.getDestinationCharacterId(); } std::vector > static const emptyStringVector; NewClient m(characterId, "TransferServer", NetworkHandler::getHostName(), true, false, stationId, nullptr, 0, 0, 0, 0, 0, 0, 0, emptyStringVector, emptyStringVector, m_transferCharacterData.getCSToolId() != 0, true); m_gameConnection->send(m, true); LOG("CustomerService", ("CharacterTransfer: Sent NewClient(%s, \"TransferServer\", \"%s\", true, false, %d, nullptr, 0, 0)\n", characterId.getValueString().c_str(), NetworkHandler::getHostName().c_str(), stationId)); } } else if(msg.isType("TransferLoginCharacterToSourceServer")) { // this is a message that will create a new pseudoclient connection LOG("CustomerService", ("CharacterTransfer: requestGameServerForLogin()")); GenericValueTypeMessage loginRequest(ri); const NetworkId & requestCharacterId = loginRequest.getValue().getCharacterId(); if(requestCharacterId == NetworkId::cms_invalid || requestCharacterId != m_transferCharacterData.getCharacterId()) { LOG("CustomerService", ("CharacterTransfer: *** ERROR *** Received a request to login to source server for %s, but a transfer for %s is already in progress. Sending failure.", loginRequest.getValue().toString().c_str(), m_transferCharacterData.toString().c_str())); GenericValueTypeMessage fail("ReplyTransferDataFail", m_transferCharacterData); ConnectionServer::sendToCentralProcess(fail); } else { requestGameServerForLogin(); } } else if(msg.isType("TransferLoginCharacterToDestinationServer")) { GenericValueTypeMessage loginRequest(ri); const NetworkId & requestCharacterId = loginRequest.getValue().getCharacterId(); if(requestCharacterId == NetworkId::cms_invalid || requestCharacterId != m_transferCharacterData.getCharacterId()) { LOG("CustomerService", ("CharacterTransfer: *** ERROR *** Received a request to login to destination server for %s, but a transfer for %s is already in progress. Sending failure.", loginRequest.getValue().toString().c_str(), m_transferCharacterData.toString().c_str())); GenericValueTypeMessage fail("ReplyTransferDataFail", m_transferCharacterData); ConnectionServer::sendToCentralProcess(fail); } else { // find a valid starting location std::vector startingLocations; StartingLocationData bestLocation; startingLocations = StartingLocationManager::getLocations(); if(! startingLocations.empty()) { bestLocation = *(startingLocations.begin()); } m_transferCharacterData.setScene(bestLocation.planet); m_transferCharacterData.setStartingCoordinates(Vector(bestLocation.x, bestLocation.y, bestLocation.z)); // start character creation process ConnectionCreateCharacter connectionCreate( m_transferCharacterData.getDestinationStationId(), Unicode::narrowToWide(m_transferCharacterData.getDestinationCharacterName()), m_transferCharacterData.getObjectTemplateName(), m_transferCharacterData.getScaleFactor(), bestLocation.name, m_transferCharacterData.getCustomizationData(), m_transferCharacterData.getHairTemplateName(), m_transferCharacterData.getHairAppearanceData(), m_transferCharacterData.getProfession(), false, m_transferCharacterData.getBiography(), false, m_transferCharacterData.getSkillTemplate(), m_transferCharacterData.getWorkingSkill(), false, true, 0xFFFFFFFF); // assume all feature bits set, so that character creation will not be blocked by account features LOG("CustomerService", ("CharacterTransfer: Sending ConnectionCreateCharacter to CentralServer : %s", m_transferCharacterData.toString().c_str())); ConnectionServer::sendToCentralProcess(connectionCreate); } } else if(msg.isType("CtsSrcCharWrongPlanet")) { LOG("CustomerService", ("CharacterTransfer: *** ERROR *** Source character is not one of the 10 original ground planets. Sending failure for %s.", m_transferCharacterData.toString().c_str())); GenericValueTypeMessage fail("ReplyTransferDataFail", m_transferCharacterData); ConnectionServer::sendToCentralProcess(fail); } else if(msg.isType("ReplyTransferData")) { // the game server has responded with valid transfer information. // upload the data to the transfer server (by way of the CentralServer) // and disconnect the client since there's nothing more needed to be // done here GenericValueTypeMessage reply(ri); m_transferCharacterData = reply.getValue(); LOG("CustomerService", ("CharacterTransfer: Received ReplyTransferData %s", m_transferCharacterData.toString().c_str())); CentralConnection * centralServerConnection = ConnectionServer::getCentralConnection(); if(centralServerConnection) { const GenericValueTypeMessage transferReply("TransferReceiveDataFromGameServer", m_transferCharacterData); centralServerConnection->send(transferReply, true); } DropClient dropMsg(m_transferCharacterData.getCharacterId()); if(m_gameConnection) { m_gameConnection->send(dropMsg, true); } delete this; } else if(msg.isType("ConnectionCreateCharacterSuccess")) { const ConnectionCreateCharacterSuccess success(ri); // woohoo! character has been created on the server! // Log the character back in, run the transfer scripts to // apply data in the script dictionary associated with the character LOG("CustomerService", ("CharacterTransfer: Received ConnectionCreateCharacterSuccess for %s", m_transferCharacterData.toString().c_str())); m_transferCharacterData.setDestinationCharacterId(success.getNetworkId()); requestGameServerForLogin(); s_pseudoClientConnectionMapByCharacterId[m_transferCharacterData.getDestinationCharacterId()] = this; } else if(msg.isType("ConnectionCreateCharacterFailed")) { const ConnectionCreateCharacterFailed failed(ri); LOG("CustomerService", ("CharacterTransfer: Received ConnectionCreateCharacterFailed [%s, %s] for %s", failed.getErrorMessage().getDebugString().c_str(), failed.getOptionalDetailedErrorMessage().c_str(), m_transferCharacterData.toString().c_str())); s_pseudoClientConnectionMapByCharacterId[m_transferCharacterData.getDestinationCharacterId()] = this; GenericValueTypeMessage reply("TransferCreateCharacterFailed", m_transferCharacterData); CentralConnection * centralServerConnection = ConnectionServer::getCentralConnection(); if(centralServerConnection) { centralServerConnection->send(reply, true); } } else if(msg.isType("ApplyTransferDataSuccess")) { GenericValueTypeMessage success(ri); LOG("CustomerService", ("CharacterTransfer: Received ApplyTransferDataSuccess from GameServer! %s", success.getValue().toString().c_str())); CentralConnection * centralServerConnection = ConnectionServer::getCentralConnection(); if(centralServerConnection) { centralServerConnection->send(success, true); } } else if(msg.isType("ApplyTransferDataFail")) { GenericValueTypeMessage fail(ri); LOG("CustomerService", ("CharacterTransfer: Received ApplyTransferDataFail from GameServer! %s", fail.getValue().toString().c_str())); CentralConnection * centralServerConnection = ConnectionServer::getCentralConnection(); if(centralServerConnection) { centralServerConnection->send(fail, true); } } else if(msg.isType("ReplyBankCTSLoaded")) { LOG("CustomerService", ("CharacterTransfer: Received ReplyBankCTSLoaded from GameServer!")); onBankLoaded(); } else if(msg.isType("PackedHousesLoaded")) { onPackedHousesLoaded(); } } //----------------------------------------------------------------------- void PseudoClientConnection::destroyAllPseudoClientConnectionInstances() { std::map instances = s_pseudoClientConnectionMap; std::map::iterator i = instances.begin(); for(; i != instances.end(); ++i) { PseudoClientConnection * c = i->second; delete c; } } //----------------------------------------------------------------------- bool PseudoClientConnection::tryToDeliverMessageTo(const NetworkId & characterId, const Archive::ByteStream & msg) { bool result = false; std::map::iterator f = s_pseudoClientConnectionMapByCharacterId.find(characterId); if(f != s_pseudoClientConnectionMapByCharacterId.end()) { result = true; f->second->receiveMessage(msg); } return result; } //----------------------------------------------------------------------- bool PseudoClientConnection::tryToDeliverMessageTo(unsigned int stationId, const Archive::ByteStream & message) { Archive::ReadIterator ri = message.begin(); const GameNetworkMessage msg(ri); ri = message.begin(); bool result = false; if(msg.isType("TransferLoginCharacterToSourceServer") || msg.isType("TransferLoginCharacterToDestinationServer")) { // this is a message that will create a new pseudoclient connection const GenericValueTypeMessage login(ri); unsigned int trackStationId = login.getValue().getSourceStationId(); if(msg.isType("TransferLoginCharacterToSourceServer")) { LOG("CustomerService", ("CharacterTransfer: Creating a PseudoClientConnection on source server for %s", login.getValue().toString().c_str())); } else { LOG("CustomerService", ("CharacterTransfer: Creating a PseudoClientConnection on destination server for %s", login.getValue().toString().c_str())); trackStationId = login.getValue().getDestinationStationId(); } // remove old sessions if they exist PseudoClientConnection * oldConnection = PseudoClientConnection::getPseudoClientConnection(trackStationId); delete oldConnection; // setup new pseudoclient connection PseudoClientConnection * newConnection = new PseudoClientConnection(login.getValue(), trackStationId); newConnection->receiveMessage(message); }//lint !e429 // leak // suppressed because this is tracked in the map and it is the responsibility of the sender to send a destroy message or the receiving connection to be destroyed, in this case CentralConnection else { std::map::const_iterator const f = s_pseudoClientConnectionMap.find(stationId); if(f != s_pseudoClientConnectionMap.end()) { result = true; f->second->receiveMessage(message); } else { LOG("CustomerService", ("CharacterTransfer: *** FAILED TO DELIVER MESSAGE TO PseudoClientConnection(%d)!! ***", stationId)); } } return result; } //----------------------------------------------------------------------- void PseudoClientConnection::gameConnectionClosed(const GameConnection * gameConnection) { std::map::iterator i; for(i = s_pseudoClientConnectionMap.begin(); i != s_pseudoClientConnectionMap.end(); ++i) { if(i->second->m_gameConnection == gameConnection) { i->second->gameConnectionClosed(); } } } //----------------------------------------------------------------------- void PseudoClientConnection::gameConnectionClosed() { CentralConnection * centralConnection = ConnectionServer::getCentralConnection(); if(centralConnection) { GenericValueTypeMessage gameServerDown("TransferFailGameServerClosedConnectionWithConnectionServer", m_transferCharacterData); centralConnection->send(gameServerDown, true); } } // ======================================================================