mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-01-17 00:04:52 -05:00
1462 lines
73 KiB
C++
Executable File
1462 lines
73 KiB
C++
Executable File
// ClientConnection.cpp
|
|
// copyright 2001 Verant Interactive
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
#include "FirstConnectionServer.h"
|
|
#include "ClientConnection.h"
|
|
|
|
#include "Archive/ByteStream.h"
|
|
#include "ChatServerConnection.h"
|
|
#include "CustomerServiceConnection.h"
|
|
#include "ConfigConnectionServer.h"
|
|
#include "ConnectionServer.h"
|
|
#include "GameConnection.h"
|
|
#include "SessionApiClient.h"
|
|
#include "UnicodeUtils.h"
|
|
#include "serverKeyShare/KeyShare.h"
|
|
#include "serverNetworkMessages/CentralConnectionServerMessages.h"
|
|
#include "serverNetworkMessages/ChatDisconnectAvatar.h"
|
|
#include "serverNetworkMessages/GameConnectionServerMessages.h"
|
|
#include "serverNetworkMessages/RandomName.h"
|
|
#include "serverNetworkMessages/RequestGameServerForLoginMessage.h"
|
|
#include "serverNetworkMessages/ValidateAccountMessage.h"
|
|
#include "serverNetworkMessages/ValidateCharacterForLoginMessage.h"
|
|
#include "serverNetworkMessages/VerifyAndLockName.h"
|
|
#include "serverUtility/AdminAccountManager.h"
|
|
#include "sharedFoundation/CalendarTime.h"
|
|
#include "sharedFoundation/Clock.h"
|
|
#include "sharedFoundation/Crc.h"
|
|
#include "sharedGame/PlatformFeatureBits.h"
|
|
#include "sharedLog/Log.h"
|
|
#include "sharedNetwork/Connection.h"
|
|
#include "sharedNetworkMessages/ClientCentralMessages.h"
|
|
#include "sharedNetworkMessages/ClientPermissionsMessage.h"
|
|
#include "sharedNetworkMessages/AppendCommentMessage.h"
|
|
#include "sharedNetworkMessages/CancelTicketMessage.h"
|
|
#include "sharedNetworkMessages/ChatEnterRoom.h"
|
|
#include "sharedNetworkMessages/ChatEnterRoomById.h"
|
|
#include "sharedNetworkMessages/ChatEnum.h"
|
|
#include "sharedNetworkMessages/ChatOnEnteredRoom.h"
|
|
#include "sharedNetworkMessages/ChatPersistentMessageToServer.h"
|
|
#include "sharedNetworkMessages/ChatQueryRoom.h"
|
|
#include "sharedNetworkMessages/ConnectPlayerMessage.h"
|
|
#include "sharedNetworkMessages/CreateTicketMessage.h"
|
|
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
|
|
#include "sharedNetworkMessages/GetTicketsMessage.h"
|
|
#include "sharedNetworkMessages/NewTicketActivityMessage.h"
|
|
#include "sharedNetworkMessages/ErrorMessage.h"
|
|
#include "sharedNetworkMessages/HeartBeat.h"
|
|
#include "Session/LoginAPI/Client.h"
|
|
#include "UdpLibrary.h"
|
|
|
|
#include "sharedFoundation/CrcConstexpr.hpp"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "webAPI.h"
|
|
|
|
using namespace StellaBellum;
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
namespace ClientConnectionNamespace {
|
|
unsigned long gs_receiveDelayMaxMs = 16384;
|
|
}
|
|
|
|
using namespace ClientConnectionNamespace;
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
|
std::map <std::string, uint32> ClientConnection::sm_outgoingBytesMap_Working; // working stats that will rotate after 1 minute
|
|
std::map <std::string, uint32> ClientConnection::sm_outgoingBytesMap_Stats; // computed stats from the last minute
|
|
uint32 ClientConnection::sm_outgoingBytesMap_Worktime = 0; // time we started filling in the working map
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
ClientConnection::ClientConnection(UdpConnectionMT *u, TcpClient *t)
|
|
: ServerConnection(u, t), m_accountName(""), m_canCreateRegularCharacter(false),
|
|
m_canCreateJediCharacter(false), m_hasRequestedCharacterCreate(false), m_hasCreatedCharacter(false),
|
|
m_pendingCharacterCreate(nullptr), m_canSkipTutorial(false), m_characterId(NetworkId::cms_invalid),
|
|
m_characterName(), m_startPlayTime(0), m_lastActiveTime(0), m_activePlayTimeDuration(0), m_client(0),
|
|
m_containerId(NetworkId::cms_invalid), m_featureBitsGame(0), m_featureBitsSubscription(0),
|
|
m_hasBeenSentToGameServer(false), m_hasBeenValidated(false), m_hasSelectedCharacter(false), m_isSecure(false),
|
|
m_isAdminAccount(false), m_hasCSLoggedAccountFeatureIds(false), m_suid(0), m_requestedSuid(0),
|
|
m_usingAdminLogin(false), m_targetCoordinates(), m_targetScene(""), m_validatingCharacter(false),
|
|
m_receiveHistoryBytes(0), m_receiveHistoryPackets(0), m_receiveHistoryMs(0), m_receiveLastTimeMs(0),
|
|
m_sendLastTimeMs(0), m_sessionId(""), m_sessionValidated(false), m_connectionServerLag(0), m_gameServerLag(0),
|
|
m_countSpamLimitResetTime(0), m_entitlementTotalTime(0), m_entitlementEntitledTime(0),
|
|
m_entitlementTotalTimeSinceLastLogin(0), m_entitlementEntitledTimeSinceLastLogin(0), m_buddyPoints(0),
|
|
m_sendToStarport(false), m_pendingChatEnterRoomRequests(), m_pendingChatQueryRoomRequests() {
|
|
static const std::string loginTrace("TRACE_LOGIN");
|
|
LOG(loginTrace, ("new ClientConnection"));
|
|
|
|
setNoDataTimeout(600000);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
ClientConnection::~ClientConnection() {
|
|
bool hasBeenKicked = false;
|
|
|
|
if (ConnectionServer::getClientConnection(m_suid) == this) {
|
|
ConnectionServer::removeConnectedCharacter(m_suid);
|
|
}
|
|
if (m_client) {
|
|
hasBeenKicked = m_client->hasBeenKicked();
|
|
delete m_client;
|
|
m_client = nullptr;
|
|
}
|
|
|
|
// tell Session to stop recording play time for the character
|
|
if (m_hasBeenValidated && m_sessionValidated && ConnectionServer::getSessionApiClient() && (m_lastActiveTime > 0) &&
|
|
ConfigConnectionServer::getSessionRecordPlayTime()) {
|
|
LOG("CustomerService", ("Login:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount(this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getCurrentActivePlayTimeDuration().c_str()));
|
|
|
|
// log total active play time for the session to the balance log
|
|
LOG("GameBalance", ("balancelog:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount(this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getActivePlayTimeDuration().c_str()));
|
|
|
|
ConnectionServer::getSessionApiClient()->stopPlay(*this);
|
|
}
|
|
|
|
if (ConnectionServer::getSessionApiClient()) {
|
|
ConnectionServer::getSessionApiClient()->dropClient(this, hasBeenKicked);
|
|
}
|
|
|
|
std::map < unsigned
|
|
long, GameClientMessage * > ::const_iterator
|
|
iter;
|
|
for (iter = m_pendingChatEnterRoomRequests.begin(); iter != m_pendingChatEnterRoomRequests.end(); ++iter) {
|
|
delete iter->second;
|
|
}
|
|
m_pendingChatEnterRoomRequests.clear();
|
|
|
|
for (iter = m_pendingChatQueryRoomRequests.begin(); iter != m_pendingChatQueryRoomRequests.end(); ++iter) {
|
|
delete iter->second;
|
|
}
|
|
m_pendingChatQueryRoomRequests.clear();
|
|
|
|
delete m_pendingCharacterCreate;
|
|
m_pendingCharacterCreate = nullptr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
const NetworkId &ClientConnection::getCharacterId() const {
|
|
return m_characterId;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
const std::string &ClientConnection::getCharacterName() const {
|
|
return m_characterName;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
std::string ClientConnection::getPlayTimeDuration() const {
|
|
int playTimeDuration = 0;
|
|
|
|
if (m_startPlayTime > 0) {
|
|
playTimeDuration = static_cast<int>(::time(nullptr) - m_startPlayTime);
|
|
}
|
|
|
|
return CalendarTime::convertSecondsToHMS(static_cast<unsigned int>(playTimeDuration));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
std::string ClientConnection::getActivePlayTimeDuration() const {
|
|
int activePlayTimeDuration = static_cast<int>(m_activePlayTimeDuration);
|
|
|
|
if (m_lastActiveTime > 0) {
|
|
activePlayTimeDuration += static_cast<int>(::time(nullptr) - m_lastActiveTime);
|
|
}
|
|
|
|
return CalendarTime::convertSecondsToHMS(static_cast<unsigned int>(activePlayTimeDuration));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
std::string ClientConnection::getCurrentActivePlayTimeDuration() const {
|
|
int activePlayTimeDuration = 0;
|
|
|
|
if (m_lastActiveTime > 0) {
|
|
activePlayTimeDuration = static_cast<int>(::time(nullptr) - m_lastActiveTime);
|
|
}
|
|
|
|
return CalendarTime::convertSecondsToHMS(static_cast<unsigned int>(activePlayTimeDuration));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::sendPlayTimeInfoToGameServer() const {
|
|
if (m_client && m_client->getGameConnection()) {
|
|
// update the game server with play time info
|
|
GenericValueTypeMessage < std::pair < int32, std::pair < int32, unsigned
|
|
long > > >
|
|
const msgPlayTimeInfo(
|
|
"UpdateSessionPlayTimeInfo", std::make_pair(static_cast<int32>(m_startPlayTime), std::make_pair(static_cast<int32>(m_lastActiveTime), m_activePlayTimeDuration)));
|
|
|
|
std::vector <NetworkId> v;
|
|
v.push_back(m_client->getNetworkId());
|
|
GameClientMessage const gcm(v, true, msgPlayTimeInfo);
|
|
m_client->getGameConnection()->send(gcm, true);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void ClientConnection::handleSelectCharacterMessage(const SelectCharacter &msg) {
|
|
if (!m_sessionValidated) {
|
|
|
|
}
|
|
|
|
//Only accept this message from clients who have been validated and
|
|
//haven't already selected.
|
|
if (m_hasSelectedCharacter || m_validatingCharacter || !m_hasBeenValidated || !m_sessionValidated) {
|
|
if (m_hasSelectedCharacter) {
|
|
LOG("TraceCharacterSelection", ("%d cannot select a character because the client has already selected a character", getSUID()));
|
|
}
|
|
if (m_validatingCharacter) {
|
|
LOG("TraceCharacterSelection", ("%d cannot select a character because the client has not yet received validation", getSUID()));
|
|
}
|
|
if (!m_hasBeenValidated) {
|
|
LOG("TraceCharacterSelection", ("%d cannot select a character because the client has not been validated", getSUID()));
|
|
}
|
|
if (!m_sessionValidated) {
|
|
LOG("TraceCharacterSelection", ("%d cannot select a character because the client has not been session validated", getSUID()));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
m_validatingCharacter = true;
|
|
|
|
// The client is picking a character from the list the Login Server gave him.
|
|
// But we don't trust him not to cheat, so we double-check that he really
|
|
// owns the character he selected.
|
|
|
|
ValidateCharacterForLoginMessage vclm(getSUID(), msg.getId());
|
|
ConnectionServer::sendToCentralProcess(vclm);
|
|
LOG("TraceCharacterSelection", ("%d selected %s for login. Sending a validation request to CentralServer to verify this client can use this character", getSUID(), msg.getId().getValueString().c_str()));
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* We got a message telling us what game server to use for the player (who
|
|
* is in the process of logging in).
|
|
* Connect the player with the game server.
|
|
*/
|
|
void ClientConnection::handleGameServerForLoginMessage(uint32 serverId) {
|
|
DEBUG_WARNING(serverId == 0, ("Got handleGameServerForLoginMessage with serverId=0.\n"));
|
|
IGNORE_RETURN(sendToGameServer(serverId));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Examine the ID the client sends us. If it looks OK, send it to Central
|
|
* to be validated. Central will reply with a list of permissions.
|
|
* @see onIdValidated
|
|
*/
|
|
void ClientConnection::handleClientIdMessage(const ClientIdMsg &msg) {
|
|
//Only check clients that have not been validated.
|
|
if (m_hasBeenValidated) {
|
|
return;
|
|
}
|
|
|
|
DEBUG_FATAL(m_hasSelectedCharacter, ("Trying to validate a client who already has a character selected.\n"));
|
|
bool result = false;
|
|
m_gameBitsToClear = msg.getGameBitsToClear();
|
|
|
|
if (msg.getTokenSize() > 0) {
|
|
Archive::ByteStream t(msg.getToken(), msg.getTokenSize());
|
|
Archive::ReadIterator ri(t);
|
|
KeyShare::Token token(ri);
|
|
char sessionId[apiSessionIdWidth];
|
|
|
|
if (!ConfigConnectionServer::getValidateStationKey()) {
|
|
// get SUID from token
|
|
result = ConnectionServer::decryptToken(token, m_suid, m_isSecure, m_accountName);
|
|
} else {
|
|
result = ConnectionServer::decryptToken(token, sessionId, m_requestedSuid);
|
|
}
|
|
|
|
static const std::string sessURL(ConfigConnectionServer::getSessionURL());
|
|
|
|
if (result || strlen(sessionId) != 0) {
|
|
if (ConfigConnectionServer::getValidateStationKey()) {
|
|
bool cont = false;
|
|
StationId apiSuid = 0;
|
|
|
|
FATAL(sessURL.empty(), ("Session URL is empty in connection server."));
|
|
|
|
const std::string clientIP(getRemoteAddress());
|
|
const std::string sess(sessionId);
|
|
|
|
FATAL(clientIP.empty(), ("Remote IP is empty"));
|
|
|
|
DEBUG_WARNING(true, ("ConnectionServer::handleClientIdMessage - For ip %s suid is %lu requestedSUID is %lu and session is %s", clientIP.c_str(), m_suid, m_requestedSuid, sess.c_str()));
|
|
|
|
webAPI api(sessURL);
|
|
|
|
// add our data
|
|
api.addJsonData<std::string>("session_key", sess);
|
|
|
|
if (api.submit()) {
|
|
bool status = api.getNullableValue<bool>("status");
|
|
|
|
if (status) {
|
|
std::string apiUser = api.getString("user_name");
|
|
std::string apiIP = api.getString("ip");
|
|
int expired = api.getNullableValue<int>("expired");
|
|
|
|
if (!ConfigConnectionServer::getUseOldSuidGenerator()) {
|
|
apiSuid = api.getNullableValue<int>("user_id");
|
|
} else {
|
|
if (apiUser.length() > MAX_ACCOUNT_NAME_LENGTH) {
|
|
apiUser.resize(MAX_ACCOUNT_NAME_LENGTH);
|
|
}
|
|
apiSuid = std::hash < std::string > {}(apiUser.c_str());
|
|
}
|
|
|
|
if (apiIP == clientIP && expired == 0) {
|
|
m_suid = apiSuid;
|
|
cont = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!cont) {
|
|
LOG("ClientDisconnect", ("SUID %d (%d) passed a bad token to the connections erver. Disconnecting.", m_suid, apiSuid));
|
|
disconnect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
static const std::string loginTrace("TRACE_LOGIN");
|
|
LOG(loginTrace, ("ClientConnection SUID = %d", m_suid));
|
|
|
|
//check for duplicate login
|
|
ClientConnection *oldConnection = ConnectionServer::getClientConnection(m_suid);
|
|
if (oldConnection) {
|
|
//There is already someone connected to this cluster with this suid.
|
|
LOG("Network", ("SUID %d already logged in, disconnecting client.\n", m_suid));
|
|
|
|
ConnectionServer::dropClient(oldConnection, "Already Connected");
|
|
|
|
disconnect();
|
|
return;
|
|
}
|
|
|
|
// verify version
|
|
if (ConfigConnectionServer::getValidateClientVersion() &&
|
|
msg.getVersion() != GameNetworkMessage::NetworkVersionId) {
|
|
std::string strSessionId(sessionId, apiSessionIdWidth);
|
|
strSessionId += '\0';
|
|
|
|
const int bufferSize = 255 + apiSessionIdWidth;
|
|
char *buffer = new char[bufferSize];
|
|
snprintf(buffer, bufferSize -
|
|
1, "network version mismatch: got (ip=[%s], sessionId=[%s], version=[%s]), required (version=[%s])", getRemoteAddress().c_str(), strSessionId.c_str(), msg.getVersion().c_str(), GameNetworkMessage::NetworkVersionId.c_str());
|
|
buffer[bufferSize - 1] = '\0';
|
|
|
|
ConnectionServer::dropClient(this, std::string(buffer));
|
|
disconnect();
|
|
|
|
delete[] buffer;
|
|
|
|
return;
|
|
}
|
|
|
|
// test mode only
|
|
if (!m_suid && !ConfigConnectionServer::getValidateStationKey()) {
|
|
WARNING(true, ("Generating suid from username. This is not safe or secure."));
|
|
|
|
m_suid = atoi(m_accountName.c_str());
|
|
|
|
if (m_suid == 0) {
|
|
m_suid = std::hash < std::string > {}(m_accountName.c_str());
|
|
}
|
|
}
|
|
|
|
onValidateClient(m_suid, m_accountName, m_isSecure, nullptr, ConfigConnectionServer::getDefaultGameFeatures(), ConfigConnectionServer::getDefaultSubscriptionFeatures(), 0, 0, 0, 0, ConfigConnectionServer::getFakeBuddyPoints());
|
|
}
|
|
} else {
|
|
WARNING(true, ("SUID %d passed a bad token to the connections server (cache issue or hacker?). Disconnecting.", m_suid));
|
|
|
|
// They sent us a token that was no good -- either a hack attempt, or
|
|
// possibly it was just too old.
|
|
LOG("ClientDisconnect", ("SUID %d passed a bad token to the connections server (cache issue or hacker?). Disconnecting.", m_suid));
|
|
disconnect();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void
|
|
ClientConnection::onIdValidated(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial, std::vector <std::pair<NetworkId, std::string>> const &consumedRewardEvents, std::vector <std::pair<NetworkId, std::string>> const &claimedRewardItems) {
|
|
//@todo start session with station.
|
|
//@todo add more permissions to this message as needed.
|
|
|
|
// resume character creation
|
|
if (m_pendingCharacterCreate) {
|
|
if (!m_pendingCharacterCreate->getUseNewbieTutorial() && !canSkipTutorial) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. The client is not allowed to skip the tutorial", getSUID()));
|
|
// This is probably a hack attempt, because the Client was already told they couldn't skip the tutorial
|
|
LOG("ClientDisconnect", ("Disconnecting %u because they tried to skip the tutorial without permission.\n", getSUID()));
|
|
disconnect();
|
|
} else if (m_pendingCharacterCreate->getJedi() && !canCreateJediCharacter) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. The request character type was Jedi, but this client cannot create a Jedi character", getSUID()));
|
|
// This is probably a hack attempt, because the Client was already told they couldn't create a character
|
|
LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a Jedi character without permission.\n", getSUID()));
|
|
disconnect();
|
|
} else if (!m_pendingCharacterCreate->getJedi() && !canCreateRegularCharacter) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. The client is not allowed to create any characters", getSUID()));
|
|
// This is probably a hack attempt, because the Client was already told they couldn't create a character
|
|
LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a regular character without permission.\n", getSUID()));
|
|
disconnect();
|
|
} else {
|
|
ConnectionServer::sendToCentralProcess(*m_pendingCharacterCreate);
|
|
LOG("TraceCharacterCreation", ("%d character creation request sent to CentralServer", getSUID()));
|
|
|
|
m_hasRequestedCharacterCreate = true;
|
|
}
|
|
|
|
delete m_pendingCharacterCreate;
|
|
m_pendingCharacterCreate = nullptr;
|
|
|
|
return;
|
|
}
|
|
|
|
// Save lists of claimed rewards, which won't be used again until later in the login sequence
|
|
m_consumedRewardEvents = consumedRewardEvents;
|
|
m_claimedRewardItems = claimedRewardItems;
|
|
|
|
|
|
int level = 0;
|
|
if (AdminAccountManager::isAdminAccount(Unicode::toLower(getAccountName()), level) && (level !=
|
|
0)) // Note: not checking IP, so that owners of god accounts can create characters to play from home without having to erase the characters they use for work
|
|
{
|
|
canLogin = true;
|
|
canCreateRegularCharacter = true;
|
|
canSkipTutorial = true;
|
|
m_isAdminAccount = true;
|
|
}
|
|
|
|
ClientPermissionsMessage c(canLogin, canCreateRegularCharacter, canCreateJediCharacter, canSkipTutorial);
|
|
send(c, true);
|
|
|
|
DEBUG_REPORT_LOG(true, ("Permissions for %lu:\n", getSUID()));
|
|
DEBUG_REPORT_LOG(canLogin, ("\tcanLogin\n"));
|
|
DEBUG_REPORT_LOG(canCreateRegularCharacter, ("\tcanCreateRegularCharacter\n"));
|
|
DEBUG_REPORT_LOG(canCreateJediCharacter, ("\tcanCreateJediCharacter\n"));
|
|
DEBUG_REPORT_LOG(canSkipTutorial, ("\tcanSkipTutorial\n"));
|
|
DEBUG_REPORT_LOG(!(canLogin || canCreateRegularCharacter || canCreateJediCharacter ||
|
|
canSkipTutorial), ("\tnone\n"));
|
|
|
|
if (canLogin) {
|
|
m_hasBeenValidated = true;
|
|
m_canCreateRegularCharacter = canCreateRegularCharacter;
|
|
m_canCreateJediCharacter = canCreateJediCharacter;
|
|
m_canSkipTutorial = canSkipTutorial;
|
|
} else {
|
|
LOG("TRACE_LOGIN", ("%d does not have permissions to log in", getSUID()));
|
|
LOG("ClientDisconnect", ("Client (SUID %u) does not have permissions to log in. Disconnecting.", getSUID()));
|
|
disconnect();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::onConnectionClosed() {
|
|
ServerConnection::onConnectionClosed();
|
|
static MessageConnectionCallback m("ClientConnectionClosed");
|
|
emitMessage(m);
|
|
|
|
LOG("TRACE_LOGIN", ("%d closed connection", getSUID()));
|
|
if (m_client) {
|
|
if (!m_client->hasBeenKicked()) {
|
|
LOG("CustomerService", ("Login:%s Dropped Reason: Client Dropped Connection. Character: %s (%s). Play time: %s. Active play time: %s", describeAccount(this).c_str(), getCharacterName().c_str(), getCharacterId().getValueString().c_str(), getPlayTimeDuration().c_str(), getActivePlayTimeDuration().c_str()));
|
|
}
|
|
ChatServerConnection *chatConnection = m_client->getChatConnection();
|
|
if (chatConnection) {
|
|
ChatDisconnectAvatar m(m_characterId);
|
|
chatConnection->send(m, true);
|
|
}
|
|
// We cannot do this here, as this connection will be deleted on
|
|
// return from this function already.
|
|
//m_client->kick();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::onConnectionOpened() {
|
|
ServerConnection::onConnectionOpened();
|
|
static MessageConnectionCallback m("ClientConnectionOpened");
|
|
emitMessage(m);
|
|
setOverflowLimit(ConfigConnectionServer::getClientOverflowLimit());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::onConnectionOverflowing(const unsigned int bytesPending) {
|
|
char errbuf[1024];
|
|
snprintf(errbuf, sizeof(errbuf), "Connection overflow from server to client, %d bytes. Disconnected.", bytesPending);
|
|
LOG("Network", ("Disconnect: Client connection overflowing. %d bytes pending", bytesPending));
|
|
|
|
std::vector < std::pair < std::string, int > > ::const_iterator
|
|
i;
|
|
for (i = m_pendingPackets.begin(); i != m_pendingPackets.end(); ++i) {
|
|
LOG("Network", ("Overflow packets this frame: [%s] %d bytes", i->first.c_str(), i->second));
|
|
}
|
|
|
|
// ErrorMessage err(name, desc, false);
|
|
// send(err, true);
|
|
WARNING(true, (errbuf));
|
|
LOG("ClientDisconnect", ("About to drop client (character) %s because the connection is overflowing\n", m_characterName.c_str()));
|
|
|
|
snprintf(errbuf, sizeof(errbuf) - 1, "Connection Overflow (bytes pending=%u)", bytesPending);
|
|
errbuf[sizeof(errbuf) - 1] = '\0';
|
|
ConnectionServer::dropClient(this, std::string(errbuf));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool ClientConnection::checkSpamLimit(unsigned int messageSize) {
|
|
if (!ConfigConnectionServer::getSpamLimitEnabled()) {
|
|
return true;
|
|
}
|
|
|
|
unsigned long curTimeMs = Clock::timeMs();
|
|
if (m_receiveLastTimeMs) {
|
|
++m_receiveHistoryPackets;
|
|
m_receiveHistoryBytes += messageSize;
|
|
m_receiveHistoryMs += curTimeMs - m_receiveLastTimeMs;
|
|
|
|
// rescale the history information if we've exceeded the reset time; this
|
|
// must be done before the spam check below or else we may run into overflow
|
|
// issues because m_receiveHistoryMs could be pretty large if we haven't
|
|
// received anything from the client for a while
|
|
while (m_receiveHistoryMs > ConfigConnectionServer::getSpamLimitResetTimeMs()) {
|
|
++m_countSpamLimitResetTime;
|
|
|
|
unsigned int resetScale = ConfigConnectionServer::getSpamLimitResetScaleFactor();
|
|
m_receiveHistoryMs /= resetScale;
|
|
m_receiveHistoryBytes /= resetScale;
|
|
m_receiveHistoryPackets /= resetScale;
|
|
}
|
|
|
|
// check for exceeding limits, but wait for at least
|
|
// one reset cycle so that there has been enough
|
|
// elapsed time, so we won't get a false positive
|
|
if (m_countSpamLimitResetTime) {
|
|
if (m_receiveHistoryBytes >=
|
|
m_receiveHistoryMs * ConfigConnectionServer::getSpamLimitBytesPerSec() / 1000) {
|
|
LOG("Network", ("Client %s disconnected for exceeding bytes/sec limit (bytes=%u, time=%lums)\n", getCharacterId().getValueString().c_str(), m_receiveHistoryBytes, m_receiveHistoryMs));
|
|
return false;
|
|
}
|
|
if (m_receiveHistoryPackets >=
|
|
m_receiveHistoryMs * ConfigConnectionServer::getSpamLimitPacketsPerSec() / 1000) {
|
|
LOG("Network", ("Client %s disconnected for exceeding packets/sec limit (packets=%u, time=%lums)\n", getCharacterId().getValueString().c_str(), m_receiveHistoryPackets, m_receiveHistoryMs));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
m_receiveLastTimeMs = curTimeMs;
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::onReceive(const Archive::ByteStream &message) {
|
|
try {
|
|
if (!checkSpamLimit(message.getSize())) {
|
|
ConnectionServer::dropClient(this, "Spam Detected");
|
|
return;
|
|
}
|
|
|
|
unsigned long curTimeMs = Clock::timeMs();
|
|
if (m_sendLastTimeMs +
|
|
std::min(gs_receiveDelayMaxMs, static_cast<unsigned long>(Clock::frameTime() * 1000.0f)) < curTimeMs) {
|
|
static HeartBeat h;
|
|
send(h, false);
|
|
}
|
|
|
|
Archive::ReadIterator ri = message.begin();
|
|
GameNetworkMessage m(ri);
|
|
ri = message.begin();
|
|
|
|
const uint32 messageType = m.getType();
|
|
|
|
//Clients with a selected character get routed to a game server.
|
|
//@todo check for filtering out bad messages.
|
|
if (m_hasSelectedCharacter) {
|
|
// if it is a chat message, send it directly to the chat server
|
|
switch (messageType) {
|
|
case constcrc("ChatAddFriend") :
|
|
case constcrc("ChatAddModeratorToRoom") :
|
|
case constcrc("ChatBanAvatarFromRoom") :
|
|
case constcrc("ChatCreateRoom") :
|
|
case constcrc("ChatDeletePersistentMessage") :
|
|
case constcrc("ChatDeleteAllPersistentMessages") :
|
|
case constcrc("ChatDestroyRoom") :
|
|
case constcrc("ChatInstantMessageToCharacter") :
|
|
case constcrc("ChatInviteAvatarToRoom") :
|
|
case constcrc("ChatKickAvatarFromRoom") :
|
|
case constcrc("ChatRemoveAvatarFromRoom") :
|
|
case constcrc("ChatRemoveFriend") :
|
|
case constcrc("ChatRemoveModeratorFromRoom") :
|
|
case constcrc("ChatRequestPersistentMessage") :
|
|
case constcrc("ChatRequestRoomList") :
|
|
case constcrc("ChatSendToRoom") :
|
|
case constcrc("ChatUninviteFromRoom") :
|
|
case constcrc("ChatUnbanAvatarFromRoom") :
|
|
case constcrc("VerifyPlayerNameMessage") : {
|
|
DEBUG_REPORT_LOG(true, ("ConnServ: ClientConnection::onReceive()\n"));
|
|
|
|
NOT_NULL(m_client);
|
|
if (m_client) {
|
|
static std::vector <NetworkId> v;
|
|
v.clear();
|
|
v.push_back(m_client->getNetworkId());
|
|
GameClientMessage gcm(v, true, ri);
|
|
if (m_client->getChatConnection()) {
|
|
m_client->getChatConnection()->send(gcm, true);
|
|
} else {
|
|
// defer chat messages until a server is back online
|
|
Archive::ByteStream bs;
|
|
m.pack(bs);
|
|
m_client->deferChatMessage(bs);
|
|
}
|
|
} else {
|
|
ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!");
|
|
disconnect();
|
|
}
|
|
break;
|
|
}
|
|
// ChatEnterRoom and ChatEnterRoomById needs to go to the game server to determine
|
|
// if the character is not allowed to enter the room because of game rule restrictions;
|
|
// only if that test pass do we forward the message on to the chat server to request
|
|
// to enter the room
|
|
case constcrc("ChatEnterRoom") :
|
|
case constcrc("ChatEnterRoomById") : {
|
|
NOT_NULL(m_client);
|
|
|
|
unsigned int sequence;
|
|
std::string roomName;
|
|
|
|
Archive::ReadIterator cri = message.begin();
|
|
|
|
if (messageType == constcrc("ChatEnterRoom")) {
|
|
ChatEnterRoom const cer(cri);
|
|
sequence = cer.getSequence();
|
|
roomName = cer.getRoomName();
|
|
} else {
|
|
ChatEnterRoomById const cerbi(cri);
|
|
sequence = cerbi.getSequence();
|
|
roomName = cerbi.getRoomName();
|
|
}
|
|
|
|
if (m_client && m_client->getGameConnection()) {
|
|
if (m_pendingChatEnterRoomRequests.count(sequence) == 0) {
|
|
GenericValueTypeMessage < std::pair < std::pair < NetworkId, std::string >, unsigned
|
|
int > >
|
|
const cervr(
|
|
"ChatEnterRoomValidationRequest", std::make_pair(std::make_pair(m_client->getNetworkId(), roomName), sequence));
|
|
|
|
m_client->getGameConnection()->send(cervr, true);
|
|
|
|
// queue up request until game server responds
|
|
static std::vector <NetworkId> v;
|
|
v.clear();
|
|
v.push_back(m_client->getNetworkId());
|
|
m_pendingChatEnterRoomRequests[sequence] = new GameClientMessage(v, true, ri);
|
|
}
|
|
} else {
|
|
// send back response to client saying game server not available
|
|
|
|
// the client only cares about sequence and result when it's a failure
|
|
ChatOnEnteredRoom fail(sequence, SWG_CHAT_ERR_NO_GAME_SERVER, 0, ChatAvatarId());
|
|
send(fail, true);
|
|
}
|
|
|
|
break;
|
|
}
|
|
// ChatQueryRoom needs to go to the game server to determine if the character is
|
|
// not allowed to query the room because of game rule restrictions; only if that
|
|
// test pass do we forward the message on to the chat server for completion
|
|
case constcrc("ChatQueryRoom") : {
|
|
NOT_NULL(m_client);
|
|
|
|
Archive::ReadIterator cri = message.begin();
|
|
ChatQueryRoom cqr(cri);
|
|
|
|
if (m_client && m_client->getGameConnection()) {
|
|
if (m_pendingChatQueryRoomRequests.count(cqr.getSequence()) == 0) {
|
|
GenericValueTypeMessage < std::pair < std::pair < NetworkId, std::string >, unsigned
|
|
int > >
|
|
const cqrvr(
|
|
"ChatQueryRoomValidationRequest", std::make_pair(std::make_pair(m_client->getNetworkId(), cqr.getRoomName()), cqr.getSequence()));
|
|
|
|
m_client->getGameConnection()->send(cqrvr, true);
|
|
|
|
// queue up request until game server responds
|
|
static std::vector <NetworkId> v;
|
|
v.clear();
|
|
v.push_back(m_client->getNetworkId());
|
|
m_pendingChatQueryRoomRequests[cqr.getSequence()] = new GameClientMessage(v, true, ri);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// ChatInviteGroupToRoom needs to go to the game server to get group information
|
|
case constcrc("ChatInviteGroupToRoom") : {
|
|
NOT_NULL(m_client);
|
|
if (m_client) {
|
|
if (m_client->getGameConnection()) {
|
|
static std::vector <NetworkId> v;
|
|
v.clear();
|
|
v.push_back(m_client->getNetworkId());
|
|
GameClientMessage gcm(v, true, ri);
|
|
m_client->getGameConnection()->send(gcm, true);
|
|
} else {
|
|
// defer chat messages until a server is back online
|
|
Archive::ByteStream bs;
|
|
m.pack(bs);
|
|
m_client->deferChatMessage(bs);
|
|
}
|
|
} else {
|
|
ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!");
|
|
disconnect();
|
|
}
|
|
break;
|
|
}
|
|
// ChatPersistentMessageToServer may need to be passed off to the game server for guild or citizens messages
|
|
case constcrc("ChatPersistentMessageToServer") : {
|
|
NOT_NULL(m_client);
|
|
if (m_client) {
|
|
static std::vector <NetworkId> v;
|
|
v.clear();
|
|
v.push_back(m_client->getNetworkId());
|
|
|
|
Archive::ReadIterator cri = message.begin();
|
|
ChatPersistentMessageToServer chat(cri);
|
|
std::string const &toName = chat.getToCharacterName().name;
|
|
if (!_stricmp(toName.c_str(), "guild") || !_strnicmp(toName.c_str(), "guild ", 6) ||
|
|
!_stricmp(toName.c_str(), "citizens")) {
|
|
if (m_client->getGameConnection()) {
|
|
GameClientMessage gcm(v, true, ri);
|
|
m_client->getGameConnection()->send(gcm, true);
|
|
}
|
|
} else {
|
|
if (m_client->getChatConnection()) {
|
|
GameClientMessage gcm(v, true, ri);
|
|
m_client->getChatConnection()->send(gcm, true);
|
|
} else {
|
|
// defer chat messages until a server is back online
|
|
Archive::ByteStream bs;
|
|
m.pack(bs);
|
|
m_client->deferChatMessage(bs);
|
|
}
|
|
}
|
|
} else {
|
|
ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!");
|
|
disconnect();
|
|
}
|
|
break;
|
|
}
|
|
// if it is a cs message, send it directly to the cs server
|
|
case constcrc("ConnectPlayerMessage") :
|
|
case constcrc("DisconnectPlayerMessage") :
|
|
case constcrc("CreateTicketMessage") :
|
|
case constcrc("AppendCommentMessage") :
|
|
case constcrc("CancelTicketMessage") :
|
|
case constcrc("GetTicketsMessage") :
|
|
case constcrc("GetCommentsMessage") :
|
|
case constcrc("SearchKnowledgeBaseMessage") :
|
|
case constcrc("GetArticleMessage") :
|
|
case constcrc("RequestCategoriesMessage") :
|
|
case constcrc("NewTicketActivityMessage") : {
|
|
NOT_NULL(m_client);
|
|
if (m_client) {
|
|
CustomerServiceConnection *customerServiceConnection = m_client->getCustomerServiceConnection();
|
|
|
|
//DEBUG_REPORT_LOG(true, ("CONSRV::CS - suid: %i\n", getSUID()));
|
|
|
|
if (customerServiceConnection != nullptr) {
|
|
static std::vector <NetworkId> v;
|
|
v.clear();
|
|
v.push_back(m_client->getNetworkId());
|
|
|
|
// TODO: this shit could be made into a template
|
|
switch (messageType) {
|
|
case constcrc("ConnectPlayerMessage") : {
|
|
ConnectPlayerMessage message(ri);
|
|
message.setStationId(getSUID());
|
|
|
|
GameClientMessage gcm(v, true, message);
|
|
customerServiceConnection->send(gcm, true);
|
|
break;
|
|
}
|
|
case constcrc("CreateTicketMessage") : {
|
|
CreateTicketMessage message(ri);
|
|
message.setStationId(getSUID());
|
|
|
|
GameClientMessage gcm(v, true, message);
|
|
customerServiceConnection->send(gcm, true);
|
|
break;
|
|
}
|
|
case constcrc("AppendCommentMessage") : {
|
|
AppendCommentMessage message(ri);
|
|
message.setStationId(getSUID());
|
|
|
|
GameClientMessage gcm(v, true, message);
|
|
customerServiceConnection->send(gcm, true);
|
|
break;
|
|
}
|
|
case constcrc("CancelTicketMessage") : {
|
|
CancelTicketMessage message(ri);
|
|
message.setStationId(getSUID());
|
|
|
|
GameClientMessage gcm(v, true, message);
|
|
customerServiceConnection->send(gcm, true);
|
|
break;
|
|
}
|
|
case constcrc("GetTicketsMessage") : {
|
|
GetTicketsMessage message(ri);
|
|
message.setStationId(getSUID());
|
|
|
|
GameClientMessage gcm(v, true, message);
|
|
customerServiceConnection->send(gcm, true);
|
|
break;
|
|
}
|
|
case constcrc("NewTicketActivityMessage") : {
|
|
NewTicketActivityMessage message(ri);
|
|
message.setStationId(getSUID());
|
|
|
|
GameClientMessage gcm(v, true, message);
|
|
customerServiceConnection->send(gcm, true);
|
|
break;
|
|
}
|
|
default : {
|
|
GameClientMessage gcm(v, true, ri);
|
|
customerServiceConnection->send(gcm, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ConnectionServer::dropClient(this, "m_client is nullptr while receiving a message!");
|
|
disconnect();
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("28afefcc187a11dc888b001") : // obfuscation for ClientInactivityMessage message
|
|
{
|
|
GenericValueTypeMessage<bool> msg(ri);
|
|
|
|
if (m_hasBeenValidated && m_sessionValidated) {
|
|
// client went inactive
|
|
if (msg.getValue()) {
|
|
if (m_lastActiveTime > 0) {
|
|
// record the amount of active time
|
|
m_activePlayTimeDuration += static_cast<unsigned long>(::time(nullptr) -
|
|
m_lastActiveTime);
|
|
|
|
// tell Session to stop recording play time for the character
|
|
if (ConnectionServer::getSessionApiClient() &&
|
|
ConfigConnectionServer::getSessionRecordPlayTime()) {
|
|
LOG("CustomerService", ("Login:%s calling SessionStopPlay() for %s/%s/%s (%s). Active play time: %s", ClientConnection::describeAccount(this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str(), this->getCurrentActivePlayTimeDuration().c_str()));
|
|
ConnectionServer::getSessionApiClient()->stopPlay(*this);
|
|
}
|
|
|
|
// client is no longer active; this needs to be set after the LOG() statement
|
|
// above because getCurrentActivePlayTimeDuration() uses m_lastActiveTime
|
|
m_lastActiveTime = 0;
|
|
|
|
// update the play time info on the game server
|
|
sendPlayTimeInfoToGameServer();
|
|
|
|
// drop inactive character
|
|
if (ConfigConnectionServer::getDisconnectOnInactive()) {
|
|
LOG("ClientDisconnect", ("Disconnecting %u because the player was inactive for too long.", getSUID()));
|
|
ConnectionServer::dropClient(this, "Client inactivity");
|
|
disconnect();
|
|
} else if (ConfigConnectionServer::getDisconnectFreeTrialOnInactive() &&
|
|
((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0)) {
|
|
LOG("ClientDisconnect", ("Disconnecting (free trial) %u because the player was inactive for too long.", getSUID()));
|
|
ConnectionServer::dropClient(this, "Client inactivity (free trial)");
|
|
disconnect();
|
|
}
|
|
}
|
|
}
|
|
// client went active
|
|
else {
|
|
if (m_lastActiveTime == 0) {
|
|
// record the time client went active
|
|
m_lastActiveTime = ::time(nullptr);
|
|
|
|
// tell Session to start recording play time for the character
|
|
if (ConnectionServer::getSessionApiClient() &&
|
|
ConfigConnectionServer::getSessionRecordPlayTime()) {
|
|
LOG("CustomerService", ("Login:%s calling SessionStartPlay() for %s/%s/%s (%s)", ClientConnection::describeAccount(this).c_str(), this->getSessionId().c_str(), ConfigConnectionServer::getClusterName(), this->getCharacterName().c_str(), this->getCharacterId().getValueString().c_str()));
|
|
ConnectionServer::getSessionApiClient()->startPlay(*this);
|
|
}
|
|
|
|
// update the play time info on the game server
|
|
sendPlayTimeInfoToGameServer();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default : {
|
|
//Forward on to Game Server
|
|
DEBUG_REPORT_LOG((!m_client ||
|
|
!m_client->getGameConnection()), ("Warn, received game message with no game connection. This may happen for a short time after a GameServer crashes. If it continues to happen, it indicates a bug.\n"));
|
|
|
|
if (m_client && m_client->getGameConnection()) {
|
|
static std::vector <NetworkId> v;
|
|
v.clear();
|
|
v.push_back(m_client->getNetworkId());
|
|
GameClientMessage gcm(v, true, ri);
|
|
m_client->getGameConnection()->send(gcm, true);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
switch (messageType) {
|
|
case constcrc("ClientIdMsg") : {
|
|
DEBUG_REPORT_LOG(true, ("Recieved ClientIdMsg\n"));
|
|
ClientIdMsg k(ri);
|
|
|
|
handleClientIdMessage(k);
|
|
break;
|
|
}
|
|
case constcrc("SelectCharacter") : {
|
|
SelectCharacter s(ri);
|
|
DEBUG_REPORT_LOG(true, ("Recvd SelectCharacter message for %s.\n", s.getId().getValueString().c_str()));
|
|
|
|
handleSelectCharacterMessage(s);
|
|
break;
|
|
}
|
|
case constcrc("ClientCreateCharacter") : {
|
|
if (m_hasBeenValidated && !m_hasSelectedCharacter) //lint !e774 no this doesn't always eval to true
|
|
{
|
|
ClientCreateCharacter clientCreate(ri);
|
|
DEBUG_REPORT_LOG(true, ("Got ClientCreateCharacter message for %lu with name %s\n", m_suid, Unicode::wideToNarrow(clientCreate.getCharacterName()).c_str()));
|
|
LOG("TraceCharacterCreation", ("%d sent ClientCreateCharacter(charaterName=%s, templateName=%s, scaleFactor=%f, startingLocation=%s, hairTemplateName=%s, profession=%s, jedi=%d, useNewbieTutorial=%d, skillTemplate=%s, workingSkill=%s)", getSUID(), Unicode::wideToNarrow(clientCreate.getCharacterName()).c_str(), clientCreate.getTemplateName().c_str(), clientCreate.getScaleFactor(), clientCreate.getStartingLocation().c_str(), clientCreate.getHairTemplateName().c_str(), clientCreate.getProfession().c_str(), static_cast<int>(clientCreate.getJedi()), static_cast<int>(clientCreate.getUseNewbieTutorial()), clientCreate.getSkillTemplate().c_str(), clientCreate.getWorkingSkill().c_str()));
|
|
|
|
if (!clientCreate.getUseNewbieTutorial() && !m_canSkipTutorial) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. The client is not allowed to skip the tutorial", getSUID()));
|
|
// This is probably a hack attempt, because the Client was already told they couldn't skip the tutorial
|
|
LOG("ClientDisconnect", ("Disconnecting %u because they tried to skip the tutorial without permission.\n", getSUID()));
|
|
disconnect();
|
|
} else if (clientCreate.getJedi() && !m_canCreateJediCharacter) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. The request character type was Jedi, but this client cannot create a Jedi character", getSUID()));
|
|
// This is probably a hack attempt, because the Client was already told they couldn't create a character
|
|
LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a Jedi character without permission.\n", getSUID()));
|
|
disconnect();
|
|
} else if (!clientCreate.getJedi() && !m_canCreateRegularCharacter) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. The client is not allowed to create any characters", getSUID()));
|
|
// This is probably a hack attempt, because the Client was already told they couldn't create a character
|
|
LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a regular character without permission.\n", getSUID()));
|
|
disconnect();
|
|
} else if (m_hasRequestedCharacterCreate) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. The client has already requested character creation on this connection", getSUID()));
|
|
LOG("ClientDisconnect", ("Disconnecting %u because the client has already requested character creation on this connection.\n", getSUID()));
|
|
disconnect();
|
|
} else if (m_hasCreatedCharacter) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. A character has been created on this or another galaxy for this account while this connection was up", getSUID()));
|
|
LOG("ClientDisconnect", ("Disconnecting %u because a character has been created on this or another galaxy for this account while this connection was up.\n", getSUID()));
|
|
disconnect();
|
|
} else if (clientCreate.getCharacterName().length() == 0) {
|
|
LOG("TraceCharacterCreation", ("%d failed character creation. The character's name is empty", getSUID()));
|
|
LOG("ClientDisconnect", ("Disconnecting %u because they tried to create a character with no name.\n", getSUID()));
|
|
disconnect();
|
|
} else {
|
|
Unicode::String biography(clientCreate.getBiography());
|
|
if (biography.length() > 1024) {
|
|
IGNORE_RETURN(biography.erase(1024));
|
|
DEBUG_REPORT_LOG(true, ("Biography shortened to 1024 characters.\n"));
|
|
}
|
|
|
|
if (m_isAdminAccount) {
|
|
ConnectionCreateCharacter connectionCreate(m_suid, clientCreate.getCharacterName(), clientCreate.getTemplateName(), clientCreate.getScaleFactor(), clientCreate.getStartingLocation(), clientCreate.getAppearanceData(), clientCreate.getHairTemplateName(), clientCreate.getHairAppearanceData(), clientCreate.getProfession(), clientCreate.getJedi(), biography, clientCreate.getUseNewbieTutorial(), clientCreate.getSkillTemplate(), clientCreate.getWorkingSkill(), m_isAdminAccount, false, m_featureBitsGame);
|
|
|
|
ConnectionServer::sendToCentralProcess(connectionCreate);
|
|
LOG("TraceCharacterCreation", ("%d character creation request sent to CentralServer", getSUID()));
|
|
} else {
|
|
// for regular players, do one final check with the LoginServer
|
|
// to make sure the character can be created (i.e. that character
|
|
// limits have not been exceeded)
|
|
delete m_pendingCharacterCreate;
|
|
m_pendingCharacterCreate = new ConnectionCreateCharacter(m_suid, clientCreate.getCharacterName(), clientCreate.getTemplateName(), clientCreate.getScaleFactor(), clientCreate.getStartingLocation(), clientCreate.getAppearanceData(), clientCreate.getHairTemplateName(), clientCreate.getHairAppearanceData(), clientCreate.getProfession(), clientCreate.getJedi(), biography, clientCreate.getUseNewbieTutorial(), clientCreate.getSkillTemplate(), clientCreate.getWorkingSkill(), m_isAdminAccount, false, m_featureBitsGame);
|
|
|
|
LOG("TraceCharacterCreation", ("%d character creation request awaiting final verification from LoginServer", getSUID()));
|
|
|
|
ValidateAccountMessage vcm(m_suid, 0, m_featureBitsSubscription);
|
|
ConnectionServer::sendToCentralProcess(vcm);
|
|
}
|
|
|
|
m_hasRequestedCharacterCreate = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case constcrc("ClientRandomNameRequest") : {
|
|
ClientRandomNameRequest clientRandomName(ri);
|
|
|
|
RandomNameRequest randomNameRequest(m_suid, clientRandomName.getCreatureTemplate());
|
|
ConnectionServer::sendToCentralProcess(randomNameRequest);
|
|
LOG("TraceCharacterCreation", ("%d requested a random name. Request sent to CentralServer", getSUID()));
|
|
break;
|
|
}
|
|
case constcrc("ClientVerifyAndLockNameRequest") : {
|
|
ClientVerifyAndLockNameRequest clientVerifyAndLockNameRequest(ri);
|
|
|
|
VerifyAndLockNameRequest verifyAndLockNameRequest(m_suid, NetworkId::cms_invalid, clientVerifyAndLockNameRequest.getTemplateName(), clientVerifyAndLockNameRequest.getCharacterName(), m_featureBitsGame);
|
|
ConnectionServer::sendToCentralProcess(verifyAndLockNameRequest);
|
|
LOG("TraceCharacterCreation", ("%d requested a verify and lock of name: %s. Request sent to CentralServer", getSUID(), Unicode::wideToNarrow(verifyAndLockNameRequest.getCharacterName()).c_str()));
|
|
break;
|
|
}
|
|
case constcrc("LagRequest") : {
|
|
// TODO: why is this commented out?
|
|
// handleLagRequest();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} catch (const Archive::ReadException &readException) {
|
|
WARNING(true, ("Archive read error (%s) from client. Disconnecting client", readException.what()));
|
|
LOG("ClientDisconnect", ("Archive read error (%s) from client. Disconnecting client", readException.what()));
|
|
disconnect();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::handleLagRequest() {
|
|
// client is requesting a lag ping (reliable trace)
|
|
GameNetworkMessage response("ConnectionServerLagResponse");
|
|
send(response, true);
|
|
|
|
if (m_hasSelectedCharacter && m_client && m_client->getGameConnection()) {
|
|
// send to game server
|
|
GameNetworkMessage request("LagRequest");
|
|
std::vector <NetworkId> v;
|
|
v.push_back(m_characterId);
|
|
GameClientMessage gcm(v, true, request);
|
|
m_client->getGameConnection()->send(gcm, true);
|
|
} else {
|
|
// send game response immediately
|
|
GameNetworkMessage gameResponse("GameServerLagResponse");
|
|
send(gameResponse, true);
|
|
}
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/** character has selected a character and calls this function
|
|
* to associate the connection with the new client they created with that
|
|
* character. We no longer need the character map.
|
|
*/
|
|
void ClientConnection::setClient(Client *newClient) {
|
|
//This fatal is here to try to catch the reconnect bug.
|
|
WARNING_STRICT_FATAL(m_client, ("Attempting to set the client on a connection that already has one. Client %s\n", getCharacterName().c_str()));
|
|
// jrandall - I've removed the fatal because it is blocking some people from getting some work
|
|
// done. I'm on a high priority fix at the moment. If this warning starts appearing,
|
|
// set a break point or something.
|
|
//DEBUG_FATAL(client, ("Attempting to set the client on a connection that already has one.\n"));
|
|
m_client = newClient;
|
|
|
|
// todo put this in: characterMap.clear();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::send(const GameNetworkMessage &message, const bool reliable) {
|
|
m_sendLastTimeMs = Clock::timeMs();
|
|
|
|
|
|
if (sm_outgoingBytesMap_Worktime == 0) {
|
|
sm_outgoingBytesMap_Worktime = m_sendLastTimeMs;
|
|
} else if ((m_sendLastTimeMs - sm_outgoingBytesMap_Worktime) > 60000) // 60 seconds
|
|
{
|
|
sm_outgoingBytesMap_Stats = sm_outgoingBytesMap_Working;
|
|
std::map<std::string, uint32>::iterator iter;
|
|
for (iter = sm_outgoingBytesMap_Working.begin(); iter != sm_outgoingBytesMap_Working.end(); ++iter) {
|
|
iter->second = 0;
|
|
}
|
|
sm_outgoingBytesMap_Worktime = m_sendLastTimeMs;
|
|
}
|
|
sm_outgoingBytesMap_Working[message.getCmdName()] += message.getByteStream().getSize();
|
|
|
|
|
|
ServerConnection::send(message, reliable);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
std::map <std::string, uint32> &ClientConnection::getPacketBytesPerMinStats() {
|
|
uint32 now = Clock::timeMs();
|
|
if (sm_outgoingBytesMap_Worktime == 0) {
|
|
sm_outgoingBytesMap_Worktime = now;
|
|
} else if ((now - sm_outgoingBytesMap_Worktime) > 60000) // 60 seconds
|
|
{
|
|
sm_outgoingBytesMap_Stats = sm_outgoingBytesMap_Working;
|
|
std::map<std::string, uint32>::iterator iter;
|
|
for (iter = sm_outgoingBytesMap_Working.begin(); iter != sm_outgoingBytesMap_Working.end(); ++iter) {
|
|
iter->second = 0;
|
|
}
|
|
sm_outgoingBytesMap_Worktime = now;
|
|
}
|
|
|
|
return sm_outgoingBytesMap_Stats;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::handleChatEnterRoomValidationResponse(unsigned int sequence, unsigned int result) {
|
|
std::map < unsigned
|
|
long, GameClientMessage * > ::iterator
|
|
iterFind = m_pendingChatEnterRoomRequests.find(sequence);
|
|
if (iterFind != m_pendingChatEnterRoomRequests.end()) {
|
|
if (result == CHATRESULT_SUCCESS) {
|
|
if (m_client && m_client->getChatConnection()) {
|
|
// game server says it's ok to enter the chat room,
|
|
// so forward the request on to the chat server
|
|
m_client->getChatConnection()->send(*(iterFind->second), true);
|
|
} else {
|
|
// send back response to client saying chat server not available
|
|
|
|
// the client only cares about sequence and result when it's a failure
|
|
ChatOnEnteredRoom fail(sequence, SWG_CHAT_ERR_CHAT_SERVER_UNAVAILABLE, 0, ChatAvatarId());
|
|
send(fail, true);
|
|
}
|
|
} else {
|
|
// send back response to client saying game server denied enter room request
|
|
|
|
// the client only cares about sequence and result when it's a failure
|
|
ChatOnEnteredRoom fail(sequence, result, 0, ChatAvatarId());
|
|
send(fail, true);
|
|
}
|
|
|
|
delete iterFind->second;
|
|
m_pendingChatEnterRoomRequests.erase(iterFind);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::handleChatQueryRoomValidationResponse(unsigned int sequence, bool success) {
|
|
std::map < unsigned
|
|
long, GameClientMessage * > ::iterator
|
|
iterFind = m_pendingChatQueryRoomRequests.find(sequence);
|
|
if (iterFind != m_pendingChatQueryRoomRequests.end()) {
|
|
if (success) {
|
|
if (m_client && m_client->getChatConnection()) {
|
|
// game server says it's ok to query the chat room,
|
|
// so forward the request on to the chat server
|
|
m_client->getChatConnection()->send(*(iterFind->second), true);
|
|
}
|
|
}
|
|
|
|
delete iterFind->second;
|
|
m_pendingChatQueryRoomRequests.erase(iterFind);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void ClientConnection::sendByteStream(const Archive::ByteStream &bs, bool reliable) {
|
|
Connection::send(bs, reliable);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Send the client to an arbitratry game server based on the current scene
|
|
*/
|
|
const bool ClientConnection::sendToGameServer() {
|
|
GameConnection *c = const_cast<GameConnection *>(ConnectionServer::getGameConnection(m_targetScene));
|
|
return sendToGameServer(c);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Send the client to a particular game server, sepcified by process id.
|
|
*/
|
|
|
|
const bool ClientConnection::sendToGameServer(uint32 gameServerId) {
|
|
GameConnection *c = const_cast<GameConnection *>(ConnectionServer::getGameConnection(gameServerId));
|
|
return sendToGameServer(c);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool ClientConnection::sendToGameServer(GameConnection *c) {
|
|
bool result = false;
|
|
|
|
if (c && m_hasSelectedCharacter && m_hasBeenValidated) {
|
|
//create a new client
|
|
ConnectionServer::addNewClient(this, m_characterId, c, m_targetScene, m_sendToStarport);
|
|
|
|
LoggedInMessage m(m_suid);
|
|
ConnectionServer::sendToCentralProcess(m);
|
|
result = true;
|
|
m_hasBeenSentToGameServer = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Called when DBProcess responds to our ValidateCharacterForLoginMessage.
|
|
* Now we know whether the character is valid and where in the world it
|
|
* is located.
|
|
*/
|
|
void
|
|
ClientConnection::onCharacterValidated(bool isValid, const NetworkId &character, const std::string &characterName, const NetworkId &container, const std::string &scene, const Vector &coordinates) {
|
|
if (!m_validatingCharacter) {
|
|
LOG("TraceCharacterSelection", ("%d received a validation response, but is not in the process of validation", getSUID()));
|
|
DEBUG_REPORT_LOG(true, ("Got unexpected onCharacterValidated() for account %lu.\n", getSUID()));
|
|
return;
|
|
}
|
|
m_validatingCharacter = false;
|
|
|
|
if (isValid) {
|
|
LOG("TraceCharacterSelection", ("%d received a validation response. The character (%s: %s) at (%s,%f,%f,%f,%s) selected is valid", getSUID(), character.getValueString().c_str(), characterName.c_str(), scene.c_str(), coordinates.x, coordinates.y, coordinates.z, container.getValueString().c_str()));
|
|
LOG("CustomerService", ("Login:%s received a validation response. The character (%s: %s) at (%s,%f,%f,%f,%s) selected is valid", describeAccount(this).c_str(), character.getValueString().c_str(), characterName.c_str(), scene.c_str(), coordinates.x, coordinates.y, coordinates.z, container.getValueString().c_str()));
|
|
m_targetScene = scene;
|
|
m_targetCoordinates = coordinates;
|
|
m_characterId = character;
|
|
m_containerId = container;
|
|
m_characterName = characterName;
|
|
|
|
m_hasSelectedCharacter = true;
|
|
|
|
// ask CentralServer to suggest a game server for this character
|
|
// (Central will forward the request to a Planet Server)
|
|
|
|
RequestGameServerForLoginMessage requestmsg(getSUID(), m_characterId, m_containerId, m_targetScene, m_targetCoordinates, false);
|
|
if (ConnectionServer::getCentralConnection()) {
|
|
ConnectionServer::getCentralConnection()->send(requestmsg, true);
|
|
} else {
|
|
LOG("ClientDisconnect", ("Can't handle login of character %s because there is no connection to Central.\n", m_characterId.getValueString().c_str()));
|
|
ErrorMessage err("Validation Failed", "The connection to the central server is down. Please try again later.");
|
|
send(err, true);
|
|
|
|
disconnect();
|
|
}
|
|
} else {
|
|
LOG("TraceCharacterSelection", ("%d validation failed, disconnecting client", getSUID()));
|
|
ErrorMessage err("Validation Failed", "Your character was denied login by the database.");
|
|
send(err, true);
|
|
|
|
LOG("ClientDisconnect", ("Denying login for account %u.\n", getSUID()));
|
|
disconnect();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------
|
|
|
|
void
|
|
ClientConnection::onValidateClient(StationId suid, const std::string &username, bool secure, const char *id, const uint32 gameFeatures, const uint32 subscriptionFeatures, unsigned int entitlementTotalTime, unsigned int entitlementEntitledTime, unsigned int entitlementTotalTimeSinceLastLogin, unsigned int entitlementEntitledTimeSinceLastLogin, int buddyPoints) {
|
|
UNREF(id);
|
|
m_sessionValidated = true;
|
|
m_suid = suid;
|
|
m_accountName = username;
|
|
m_featureBitsGame = gameFeatures;
|
|
m_featureBitsSubscription = subscriptionFeatures;
|
|
m_isSecure = secure;
|
|
m_entitlementTotalTime = entitlementTotalTime;
|
|
m_entitlementEntitledTime = entitlementEntitledTime;
|
|
m_entitlementTotalTimeSinceLastLogin = entitlementTotalTimeSinceLastLogin;
|
|
m_entitlementEntitledTimeSinceLastLogin = entitlementEntitledTimeSinceLastLogin;
|
|
m_buddyPoints = buddyPoints;
|
|
|
|
if (id) {
|
|
m_sessionId = id;
|
|
}
|
|
|
|
if (m_requestedSuid != 0 && suid != m_requestedSuid) {
|
|
//verify internal, secure, is on the god list
|
|
bool loginOK = false;
|
|
if (!secure) {
|
|
LOG("CustomerService", ("AdminLogin: User %s (account %li) attempted to log into account %li, but was not using a SecureID token", username.c_str(), suid, m_requestedSuid));
|
|
} else {
|
|
if (!AdminAccountManager::isInternalIp(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, m_requestedSuid));
|
|
} else {
|
|
int adminLevel = 0;
|
|
if (!AdminAccountManager::isAdminAccount(Unicode::toLower(username), adminLevel) || 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, m_requestedSuid));
|
|
} else {
|
|
LOG("CustomerService", ("AdminLogin: User %s (account %li) logged into account %li", username.c_str(), m_suid, m_requestedSuid));
|
|
DEBUG_REPORT_LOG(true, ("AdminLogin: User %s (account %li) logged into account %li\n", username.c_str(), m_suid, m_requestedSuid));
|
|
m_suid = m_requestedSuid;
|
|
m_usingAdminLogin = true;
|
|
loginOK = true;
|
|
}
|
|
}
|
|
}
|
|
if (!loginOK) {
|
|
disconnect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_featureBitsGame &= ~ConfigConnectionServer::getDisabledFeatureBits();
|
|
|
|
//Configoption to enable JTL features for beta players so that our code can pretend everything uses the JTL Retail bit
|
|
if (ConfigConnectionServer::getSetJtlRetailIfBetaIsSet()) {
|
|
if (ClientGameFeature::SpaceExpansionBeta & m_featureBitsGame) {
|
|
m_featureBitsGame |= ClientGameFeature::SpaceExpansionRetail;
|
|
}
|
|
}
|
|
|
|
//Configoption to enable Obiwan features for beta players so that our code can pretend everything uses the Obiwan Retail bit
|
|
if (ConfigConnectionServer::getSetTrialsOfObiwanRetailIfBetaIsSet()) {
|
|
if (ClientGameFeature::TrialsOfObiwanBeta & m_featureBitsGame) {
|
|
//-- add retail bit only if player does not have the preorder bit
|
|
if ((m_featureBitsGame & ClientGameFeature::TrialsOfObiwanPreorder) == 0) {
|
|
m_featureBitsGame |= ClientGameFeature::TrialsOfObiwanRetail;
|
|
}
|
|
} else {
|
|
// Clear bits from players who might have them for real, but aren't in the beta
|
|
m_featureBitsGame &= ~ClientGameFeature::TrialsOfObiwanRetail;
|
|
m_featureBitsGame &= ~ClientGameFeature::TrialsOfObiwanPreorder;
|
|
}
|
|
}
|
|
|
|
//-- Obiwan Preorders get the Retail bit as well... All rewards etc...
|
|
if (ClientGameFeature::TrialsOfObiwanPreorder & m_featureBitsGame) {
|
|
m_featureBitsGame |= ClientGameFeature::TrialsOfObiwanRetail;
|
|
}
|
|
|
|
// Restrictions for "new free trial" account
|
|
if (((m_featureBitsSubscription & ClientSubscriptionFeature::FreeTrial2) != 0) &&
|
|
((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0)) {
|
|
// "new free trial" account don't have access to RoW until they convert
|
|
m_featureBitsGame &= ~ClientGameFeature::Episode3ExpansionRetail;
|
|
m_featureBitsGame &= ~ClientGameFeature::Episode3PreorderDownload;
|
|
|
|
// ClientGameFeature::FreeTrial2 indicates this is a converted "new free trial"
|
|
// account, and since this account hasn't converted yet, we remove this bit
|
|
m_featureBitsGame &= ~ClientGameFeature::FreeTrial2;
|
|
}
|
|
|
|
// Clear feature bits that only apply if the account is paying (i.e. the sub base bit is set)
|
|
if ((m_featureBitsSubscription & ClientSubscriptionFeature::Base) == 0) {
|
|
m_featureBitsGame &= ~ClientGameFeature::HousePackupReward;
|
|
m_featureBitsGame &= ~ClientGameFeature::BuddyProgramReward;
|
|
}
|
|
|
|
ValidateAccountMessage vcm(m_suid, 0, m_featureBitsSubscription);
|
|
ConnectionServer::sendToCentralProcess(vcm);
|
|
ConnectionServer::addConnectedClient(m_suid, this);
|
|
|
|
uint32 const requiredSubscriptionBits = ConfigConnectionServer::getRequiredSubscriptionBits();
|
|
if (requiredSubscriptionBits != 0) {
|
|
if ((subscriptionFeatures & requiredSubscriptionBits) != requiredSubscriptionBits) {
|
|
LOG("ClientDisconnect", ("Suid %d (%s) by session denial reason 'Invalid Subscription Bits'.", suid, username.c_str()));
|
|
LOG("CustomerService", ("Login: %s by session denial reason 'Invalid Subscription Bits'.", describeAccount(this).c_str()));
|
|
disconnect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
uint32 const requiredGameBits = ConfigConnectionServer::getRequiredGameBits();
|
|
if (requiredGameBits != 0) {
|
|
if ((gameFeatures & requiredGameBits) != requiredGameBits) {
|
|
LOG("ClientDisconnect", ("Suid %d (%s) by session denial reason 'Invalid Game Bits'.", suid, username.c_str()));
|
|
LOG("CustomerService", ("Login: %s by session denial reason 'Invalid Game Bits'.", describeAccount(this).c_str()));
|
|
disconnect();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// tell client the server-side game and subscription feature bits and which ConnectionServer we are and the current server Epoch time
|
|
GenericValueTypeMessage < std::pair < std::pair < unsigned
|
|
long, unsigned
|
|
long > , std::pair < int, int32 > > >
|
|
const msgFeatureBits(
|
|
"AccountFeatureBits", std::make_pair(std::make_pair(gameFeatures, subscriptionFeatures), std::make_pair(ConfigConnectionServer::getConnectionServerNumber(), static_cast<int32>(::time(nullptr)))));
|
|
send(msgFeatureBits, true);
|
|
|
|
std::string const gameFeaturesDescription = ClientGameFeature::getDescription(gameFeatures);
|
|
std::string const subscriptionFeaturesDescription = ClientSubscriptionFeature::getDescription(subscriptionFeatures);
|
|
|
|
LOG("CustomerService", ("Login:%s at IP: %s:%hu has connected with game code 0x%x (%s) and sub code 0x%x (%s) (entitlement total: %u/%u, since last login: %u/%u)", describeAccount(this).c_str(), getRemoteAddress().c_str(), getRemotePort(), gameFeatures, gameFeaturesDescription.c_str(), subscriptionFeatures, subscriptionFeaturesDescription.c_str(), m_entitlementEntitledTime, m_entitlementTotalTime, m_entitlementEntitledTimeSinceLastLogin, m_entitlementTotalTimeSinceLastLogin));
|
|
|
|
// ask CentralServer to tell all other ConnectionServers on this galaxy to drop duplicate connections for this account
|
|
// and ask CentralServer (via LoginServer) to tell all ConnectionServers on other galaxies to drop duplicate connections for this account
|
|
if (!m_usingAdminLogin && !m_isSecure) {
|
|
GenericValueTypeMessage <std::pair<uint32, std::string>> const dropDuplicateConnections("ConnSrvDropDupeConns", std::make_pair(m_suid, m_sessionId));
|
|
ConnectionServer::sendToCentralProcess(dropDuplicateConnections);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
std::string ClientConnection::describeAccount(const ClientConnection *c) {
|
|
std::string result = "";
|
|
if (c) {
|
|
char idbuf[512] = {"\0"};
|
|
const std::string &sessionId = c->getSessionId();
|
|
if (sessionId.empty()) {
|
|
snprintf(idbuf, sizeof(idbuf), " (%lu)", c->m_suid);
|
|
} else {
|
|
snprintf(idbuf, sizeof(idbuf), " (%lu, %s)", c->m_suid, sessionId.c_str());
|
|
}
|
|
|
|
result = c->m_accountName;
|
|
result += idbuf;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
std::vector <std::pair<NetworkId, std::string>> const &ClientConnection::getConsumedRewardEvents() const {
|
|
return m_consumedRewardEvents;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
std::vector <std::pair<NetworkId, std::string>> const &ClientConnection::getClaimedRewardItems() const {
|
|
return m_claimedRewardItems;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool ClientConnection::isUsingAdminLogin() const {
|
|
return m_usingAdminLogin;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int ClientConnection::getBuddyPoints() const {
|
|
return m_buddyPoints;
|
|
}
|
|
|
|
// ======================================================================
|