From 26b88b453323fcfcf8259991d820d3749436c5d6 Mon Sep 17 00:00:00 2001 From: Anonymous Date: Fri, 17 Jan 2014 01:42:55 -0700 Subject: [PATCH] Added ConnectionServer project --- engine/server/application/CMakeLists.txt | 1 + .../ConnectionServer/CMakeLists.txt | 6 + .../ConnectionServer/src/CMakeLists.txt | 115 ++ .../ConnectionServer/src/linux/main.cpp | 58 + .../src/shared/CentralConnection.cpp | 134 ++ .../src/shared/CentralConnection.h | 32 + .../src/shared/ChatServerConnection.cpp | 228 +++ .../src/shared/ChatServerConnection.h | 36 + .../ConnectionServer/src/shared/Client.cpp | 313 +++ .../ConnectionServer/src/shared/Client.h | 146 ++ .../src/shared/ClientConnection.cpp | 1688 +++++++++++++++++ .../src/shared/ClientConnection.h | 354 ++++ .../src/shared/ConfigConnectionServer.cpp | 166 ++ .../src/shared/ConfigConnectionServer.h | 465 +++++ .../src/shared/ConnectionServer.cpp | 1586 ++++++++++++++++ .../src/shared/ConnectionServer.h | 138 ++ .../shared/ConnectionServerMetricsData.cpp | 62 + .../src/shared/ConnectionServerMetricsData.h | 39 + .../src/shared/CustomerServiceConnection.cpp | 104 + .../src/shared/CustomerServiceConnection.h | 34 + .../src/shared/FirstConnectionServer.h | 23 + .../src/shared/GameConnection.cpp | 348 ++++ .../src/shared/GameConnection.h | 76 + .../src/shared/PseudoClientConnection.cpp | 502 +++++ .../src/shared/PseudoClientConnection.h | 57 + .../src/shared/SessionApiClient.cpp | 1001 ++++++++++ .../src/shared/SessionApiClient.h | 146 ++ .../src/win32/FirstConnectionServer.cpp | 1 + .../ConnectionServer/src/win32/WinMain.cpp | 58 + 29 files changed, 7917 insertions(+) create mode 100644 engine/server/application/ConnectionServer/CMakeLists.txt create mode 100644 engine/server/application/ConnectionServer/src/CMakeLists.txt create mode 100644 engine/server/application/ConnectionServer/src/linux/main.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/CentralConnection.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/CentralConnection.h create mode 100644 engine/server/application/ConnectionServer/src/shared/ChatServerConnection.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/ChatServerConnection.h create mode 100644 engine/server/application/ConnectionServer/src/shared/Client.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/Client.h create mode 100644 engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/ClientConnection.h create mode 100644 engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h create mode 100644 engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/ConnectionServer.h create mode 100644 engine/server/application/ConnectionServer/src/shared/ConnectionServerMetricsData.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/ConnectionServerMetricsData.h create mode 100644 engine/server/application/ConnectionServer/src/shared/CustomerServiceConnection.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/CustomerServiceConnection.h create mode 100644 engine/server/application/ConnectionServer/src/shared/FirstConnectionServer.h create mode 100644 engine/server/application/ConnectionServer/src/shared/GameConnection.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/GameConnection.h create mode 100644 engine/server/application/ConnectionServer/src/shared/PseudoClientConnection.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/PseudoClientConnection.h create mode 100644 engine/server/application/ConnectionServer/src/shared/SessionApiClient.cpp create mode 100644 engine/server/application/ConnectionServer/src/shared/SessionApiClient.h create mode 100644 engine/server/application/ConnectionServer/src/win32/FirstConnectionServer.cpp create mode 100644 engine/server/application/ConnectionServer/src/win32/WinMain.cpp diff --git a/engine/server/application/CMakeLists.txt b/engine/server/application/CMakeLists.txt index 3e04c974..1a543a2d 100644 --- a/engine/server/application/CMakeLists.txt +++ b/engine/server/application/CMakeLists.txt @@ -1,4 +1,5 @@ +add_subdirectory(ConnectionServer) add_subdirectory(LoginServer) add_subdirectory(LogServer) add_subdirectory(TaskManager) diff --git a/engine/server/application/ConnectionServer/CMakeLists.txt b/engine/server/application/ConnectionServer/CMakeLists.txt new file mode 100644 index 00000000..aeec66ad --- /dev/null +++ b/engine/server/application/ConnectionServer/CMakeLists.txt @@ -0,0 +1,6 @@ + +cmake_minimum_required(VERSION 2.8) + +project(ConnectionServer) + +add_subdirectory(src) diff --git a/engine/server/application/ConnectionServer/src/CMakeLists.txt b/engine/server/application/ConnectionServer/src/CMakeLists.txt new file mode 100644 index 00000000..abe40785 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/CMakeLists.txt @@ -0,0 +1,115 @@ + +set(SHARED_SOURCES + shared/CentralConnection.cpp + shared/CentralConnection.h + shared/ChatServerConnection.cpp + shared/ChatServerConnection.h + shared/ClientConnection.cpp + shared/ClientConnection.h + shared/Client.cpp + shared/Client.h + shared/ConfigConnectionServer.cpp + shared/ConfigConnectionServer.h + shared/ConnectionServer.cpp + shared/ConnectionServer.h + shared/ConnectionServerMetricsData.cpp + shared/ConnectionServerMetricsData.h + shared/CustomerServiceConnection.cpp + shared/CustomerServiceConnection.h + shared/FirstConnectionServer.h + shared/GameConnection.cpp + shared/GameConnection.h + shared/SessionApiClient.cpp + shared/SessionApiClient.h + shared/PseudoClientConnection.cpp + shared/PseudoClientConnection.h +) + +if(WIN32) + set(PLATFORM_SOURCES + win32/FirstConnectionServer.cpp + win32/WinMain.cpp + ) +else() + set(PLATFORM_SOURCES + linux/main.cpp + ) +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/shared + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedCompression/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedDebug/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFile/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundation/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedFoundationTypes/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedGame/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedLog/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMath/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMathArchive/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMemoryManager/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedMessageDispatch/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetwork/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedNetworkMessages/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedRandom/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedThread/include/public + ${SWG_ENGINE_SOURCE_DIR}/shared/library/sharedUtility/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverKeyShare/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverMetrics/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverNetworkMessages/include/public + ${SWG_ENGINE_SOURCE_DIR}/server/library/serverUtility/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/archive/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/localization/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/localizationArchive/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/singleton/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicode/include + ${SWG_EXTERNALS_SOURCE_DIR}/ours/library/unicodeArchive/include/public + ${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/platform/projects + ${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/platform/utils + ${SWG_EXTERNALS_SOURCE_DIR}/3rd/library/udplibrary +) + +link_directories(${STLPORT_LIBDIR}) + +add_executable(ConnectionServer + ${SHARED_SOURCES} + ${PLATFORM_SOURCES} +) + +target_link_libraries(ConnectionServer + sharedCompression + sharedDebug + sharedFile + sharedFoundation + sharedGame + sharedLog + sharedMath + sharedMemoryManager + sharedMessageDispatch + sharedNetwork + sharedNetworkMessages + sharedRandom + sharedSynchronization + sharedThread + sharedUtility + serverKeyShare + serverMetrics + serverNetworkMessages + serverUtility + archive + crypto + fileInterface + localization + localizationArchive + unicode + unicodeArchive + Base + CommonAPI + LoginAPI + udplibrary + ${ZLIB_LIBRARY} +) + +if(WIN32) + target_link_libraries(ConnectionServer mswsock ws2_32) +endif() diff --git a/engine/server/application/ConnectionServer/src/linux/main.cpp b/engine/server/application/ConnectionServer/src/linux/main.cpp new file mode 100644 index 00000000..24c76421 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/linux/main.cpp @@ -0,0 +1,58 @@ +#include "FirstConnectionServer.h" +#include "ConfigConnectionServer.h" +#include "ConnectionServer.h" + +#include "sharedCompression/SetupSharedCompression.h" +#include "sharedDebug/SetupSharedDebug.h" +#include "sharedFile/SetupSharedFile.h" +#include "sharedFoundation/Os.h" +#include "sharedFoundation/SetupSharedFoundation.h" +#include "sharedNetwork/NetworkHandler.h" +#include "sharedNetworkMessages/SetupSharedNetworkMessages.h" +#include "sharedRandom/SetupSharedRandom.h" +#include "sharedThread/SetupSharedThread.h" + +// ====================================================================== + +void dumpPid(const char * argv) +{ + pid_t p = getpid(); + char fileName[1024]; + sprintf(fileName, "%s.%d", argv, p); + FILE * f = fopen(fileName, "w+"); + fclose(f); +} + +int main(int argc, char ** argv) +{ + SetupSharedThread::install(); + SetupSharedDebug::install(1024); + + //-- setup foundation + SetupSharedFoundation::Data setupFoundationData(SetupSharedFoundation::Data::D_game); + setupFoundationData.lpCmdLine = ConvertCommandLine(argc,argv); + SetupSharedFoundation::install (setupFoundationData); + + SetupSharedCompression::install(); + + SetupSharedFile::install(false); + SetupSharedNetworkMessages::install(); + SetupSharedRandom::install(time(NULL)); + + //setup the server + NetworkHandler::install(); + Os::setProgramName("ConnectionServer"); + ConfigConnectionServer::install(); + + //-- run game + ConnectionServer::install(); + SetupSharedFoundation::callbackWithExceptionHandling(ConnectionServer::run); + ConnectionServer::remove(); + + ConfigConnectionServer::remove(); + NetworkHandler::remove(); + SetupSharedFoundation::remove(); + SetupSharedThread::remove(); + + return 0; +} diff --git a/engine/server/application/ConnectionServer/src/shared/CentralConnection.cpp b/engine/server/application/ConnectionServer/src/shared/CentralConnection.cpp new file mode 100644 index 00000000..07c3b71f --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/CentralConnection.cpp @@ -0,0 +1,134 @@ +// CentralConnection.cpp +// copyright 2001 Verant Interactive + +//----------------------------------------------------------------------- + +#include "FirstConnectionServer.h" +#include "CentralConnection.h" + +#include "Archive/ByteStream.h" +#include "ConnectionServer.h" +#include "PseudoClientConnection.h" +#include "serverKeyShare/KeyShare.h" +#include "serverNetworkMessages/CentralConnectionServerMessages.h" +#include "serverNetworkMessages/TransferCharacterData.h" +#include "serverNetworkMessages/TransferCharacterDataArchive.h" +#include "sharedLog/Log.h" +#include "sharedNetwork/NetworkSetupData.h" +#include "sharedNetwork/Service.h" +#include "sharedNetworkMessages/ErrorMessage.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" + +//----------------------------------------------------------------------- + +CentralConnection::CentralConnection(const std::string & address, const unsigned short port) : +ServerConnection(address, port, NetworkSetupData()) +{ +} + + +//----------------------------------------------------------------------- +CentralConnection::CentralConnection(UdpConnectionMT * u, TcpClient * t) : +ServerConnection(u, t) +{ + PseudoClientConnection::destroyAllPseudoClientConnectionInstances(); +} + +//----------------------------------------------------------------------- + +CentralConnection::~CentralConnection() +{ + PseudoClientConnection::destroyAllPseudoClientConnectionInstances(); +} + +//----------------------------------------------------------------------- + + +void CentralConnection::onConnectionClosed() +{ + ServerConnection::onConnectionClosed(); + static MessageConnectionCallback m("CentralConnectionClosed"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void CentralConnection::onConnectionOpened() +{ + ServerConnection::onConnectionOpened(); + static const MessageConnectionCallback m("CentralConnectionOpened"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void CentralConnection::onReceive(const Archive::ByteStream & message) +{ + Archive::ReadIterator ri = message.begin(); + const GameNetworkMessage msg(ri); + ri = message.begin(); + + if(msg.isType("TransferLoginCharacterToSourceServer")) + { + GenericValueTypeMessage login(ri); + // received a request to create a pseudoclient and connect it + // to a game server. + LOG("CustomerService", ("CharacterTransfer: ***ConnectionServer: Received TransferLoginCharacterToSourceServer request from CentralServer for %s\n", login.getValue().toString().c_str())); + IGNORE_RETURN(PseudoClientConnection::tryToDeliverMessageTo(login.getValue().getSourceStationId(), message)); + } + else if(msg.isType("TransferLoginCharacterToDestinationServer")) + { + GenericValueTypeMessage login(ri); + LOG("CustomerService", ("CharacterTransfer: ***ConnectionServer: Received TransferLoginCharacterToDestinationServer request from CentralServer for %s", login.getValue().toString().c_str())); + IGNORE_RETURN(PseudoClientConnection::tryToDeliverMessageTo(login.getValue().getDestinationStationId(), message)); + } + else if(msg.isType("CtsSrcCharWrongPlanet")) + { + GenericValueTypeMessage > const failureMsg(ri); + LOG("CustomerService", ("CharacterTransfer: ***ConnectionServer: Received CtsSrcCharWrongPlanet error from CentralServer for character (%s) stationId (%u) because character is not one of the 10 original ground planets", failureMsg.getValue().first.getValueString().c_str(), failureMsg.getValue().second)); + IGNORE_RETURN(PseudoClientConnection::tryToDeliverMessageTo(failureMsg.getValue().second, message)); + } + else if(msg.isType("TransferKickConnectedClients")) + { + GenericValueTypeMessage kick(ri); + ClientConnection * clientConnection = ConnectionServer::getClientConnection(kick.getValue()); + if(clientConnection) + { + ConnectionServer::dropClient(clientConnection, "TransferServer requests client drop"); + } + } + else if(msg.isType("TransferClosePseudoClientConnection")) + { + GenericValueTypeMessage closeRequest(ri); + PseudoClientConnection * pseudoClient = PseudoClientConnection::getPseudoClientConnection(closeRequest.getValue()); + delete pseudoClient; + } + else if(msg.isType("LoginDeniedRecentCTS")) + { + GenericValueTypeMessage > loginDeniedRecentCTS(ri); + ClientConnection * clientConnection = ConnectionServer::getClientConnection(loginDeniedRecentCTS.getValue().second); + if (clientConnection) + { + LOG("CustomerService", ("Login:%s, character %s (%s) is a recent CTS that has not been persisted yet.", ClientConnection::describeAccount(clientConnection).c_str(), clientConnection->getCharacterName().c_str(), clientConnection->getCharacterId().getValueString().c_str())); + ErrorMessage err("Login Failed", "The selected character has just been recently transferred and has not been completely initialized. In most cases, it takes about 15 minutes (but in some cases can take up to 2 hours) to complete initialization. Please try again later."); + clientConnection->send(err, true); + } + } + else if(msg.isType("LoginDeniedPendingPlayerRenameRequest")) + { + GenericValueTypeMessage > loginDeniedPendingPlayerRenameRequest(ri); + ClientConnection * clientConnection = ConnectionServer::getClientConnection(loginDeniedPendingPlayerRenameRequest.getValue().second); + if (clientConnection) + { + LOG("CustomerService", ("Login:%s, character %s (%s) has a pending player requested character rename request.", ClientConnection::describeAccount(clientConnection).c_str(), clientConnection->getCharacterName().c_str(), clientConnection->getCharacterId().getValueString().c_str())); + ErrorMessage err("Login Failed", "The selected character currently has a pending character rename request. It can take up to 30 minutes for the rename request to complete."); + clientConnection->send(err, true); + } + } + else + { + ServerConnection::onReceive(message); + } +} + +//----------------------------------------------------------------------- diff --git a/engine/server/application/ConnectionServer/src/shared/CentralConnection.h b/engine/server/application/ConnectionServer/src/shared/CentralConnection.h new file mode 100644 index 00000000..103520f8 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/CentralConnection.h @@ -0,0 +1,32 @@ +// CentralConnection.h +// copyright 2000 Verant Interactive +// Author: Justin Randall + +#ifndef _CentralConnection_H +#define _CentralConnection_H + +//----------------------------------------------------------------------- + +#include "serverUtility/ServerConnection.h" + +//----------------------------------------------------------------------- + +class CentralConnection : public ServerConnection +{ +public: + CentralConnection (const std::string & address, const unsigned short port); + CentralConnection(UdpConnectionMT *, TcpClient *); + ~CentralConnection(); + void onConnectionClosed(); + void onConnectionOpened(); + void onReceive(const Archive::ByteStream & message); + +private: + CentralConnection(); + CentralConnection(const CentralConnection&); + CentralConnection& operator=(const CentralConnection&); +}; + +//----------------------------------------------------------------------- + +#endif // _CentralConnection_H diff --git a/engine/server/application/ConnectionServer/src/shared/ChatServerConnection.cpp b/engine/server/application/ConnectionServer/src/shared/ChatServerConnection.cpp new file mode 100644 index 00000000..76cdc21c --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ChatServerConnection.cpp @@ -0,0 +1,228 @@ +// ChatServerConnection.cpp +// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +//----------------------------------------------------------------------- + +#include "FirstConnectionServer.h" +#include "ChatServerConnection.h" +#include "ClientConnection.h" +#include "serverNetworkMessages/GameConnectionServerMessages.h" +#include "sharedNetwork/Service.h" +#include "sharedNetworkMessages/ChatOnChangeFriendStatus.h" +#include "sharedNetworkMessages/ChatOnChangeIgnoreStatus.h" +#include "sharedNetworkMessages/ChatOnEnteredRoom.h" +#include "sharedNetworkMessages/ChatOnGetFriendsList.h" +#include "sharedNetworkMessages/ChatOnGetIgnoreList.h" +#include "sharedNetworkMessages/ChatOnLeaveRoom.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" + +//----------------------------------------------------------------------- +/* +void putClientsInRoom(const unsigned int roomId, const std::vector & clients) +{ + std::vector::const_iterator i; + for(i = clients.begin(); i != clients.end(); ++i) + { + + Client* client = ConnectionServer::getClient((*i)); + DEBUG_REPORT_LOG(!client, ("Error, could not map %s to a client\n", (*i).getValueString().c_str())); + if (client) + client->enterRoom(roomId); + } +} +*/ + +//----------------------------------------------------------------------- +/* +void removeClientsFromRoom(const unsigned int roomId, const std::vector & clients) +{ + std::vector::const_iterator i; + for(i = clients.begin(); i != clients.end(); ++i) + { + + Client* client = ConnectionServer::getClient((*i)); + DEBUG_REPORT_LOG(!client, ("Error, could not map %s to a client\n", (*i).getValueString().c_str())); + if (client) + client->leaveRoom(roomId); + } +} +*/ +//----------------------------------------------------------------------- + +ChatServerConnection::ChatServerConnection(UdpConnectionMT * u, TcpClient * t) : + ServerConnection(u, t), + clients() +{ +} + +//----------------------------------------------------------------------- + +ChatServerConnection::~ChatServerConnection() +{ +} + +//----------------------------------------------------------------------- + +void ChatServerConnection::addClient(Client * newClient) +{ + if (clients.find(newClient) == clients.end()) + IGNORE_RETURN( clients.insert(newClient) ); + else + DEBUG_WARNING(true, ("called ChatServerConnection::addClient with a client that already exists in the map.")); +} + +//----------------------------------------------------------------------- + +void ChatServerConnection::onConnectionClosed() +{ + ServerConnection::onConnectionClosed(); + static MessageConnectionCallback m("ChatServerConnectionClosed"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +const std::set & ChatServerConnection::getClients() const +{ + return clients; +} + +//----------------------------------------------------------------------- + +void ChatServerConnection::onConnectionOpened() +{ + ServerConnection::onConnectionOpened(); + static MessageConnectionCallback m("ChatServerConnectionOpened"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void ChatServerConnection::onReceive(const Archive::ByteStream & message) +{ + Archive::ReadIterator ri = message.begin(); + GameNetworkMessage m(ri); + ri = message.begin(); + + if (m.isType("GameClientMessage")) + { + //we're receiving a message to forward to the client. + //it is prefixed with NetworkId and reliable. + const GameClientMessage msg(ri); + Archive::ReadIterator mri(msg.getByteStream()); + GameNetworkMessage gnm(mri); + mri = msg.getByteStream().begin(); + + // The connection server wants to trap room + // enter/leave messages to cache clients + // in particular rooms for autorecovery when + // a chat server process is stopped then later + // restarted + // + // ************ THIS CODE DOES NOT WORK *********** + // The following code has been commented out since + // everyone in the group is sent these messages but + // the logic assumes that only the person entering + // or leaving a chat room is sent the message. + /* + if(gnm.isType("ChatOnEnteredRoom")) + { + Archive::ReadIterator cri(msg.getByteStream()); + ChatOnEnteredRoom chat(cri); + putClientsInRoom(chat.getRoomId(), msg.getDistributionList()); + } + else if(gnm.isType("ChatOnLeaveRoom")) + { + Archive::ReadIterator cri(msg.getByteStream()); + ChatOnLeaveRoom chat(cri); + removeClientsFromRoom(chat.getRoomId(), msg.getDistributionList()); + } + */ + + const std::vector & d = msg.getDistributionList(); + std::vector::const_iterator i; + for(i = d.begin(); i != d.end(); ++i) + { + if ((*i) == NetworkId::cms_invalid) + { + //broadcast to everyone + Service *service = ConnectionServer::getClientServicePrivate(); + LogicalPacket const * p = service->createPacket(msg.getByteStream().getBuffer(), msg.getByteStream().getSize()); + const ConnectionServer::ClientMap clientMap(ConnectionServer::getClientMap()); + ConnectionServer::ClientMap::const_iterator i; + for(i = clientMap.begin(); i != clientMap.end(); ++i) + { + Client* client = (*i).second; + if (client) + { + client->getClientConnection()->sendSharedPacket(p, msg.getReliable()); + } + } + service->releasePacket(p); + break; + } + + Client* client = ConnectionServer::getClient((*i)); + DEBUG_REPORT_LOG(!client, ("Error, could not map %s to a client\n", (*i).getValueString().c_str())); + if (client) + { + if (!gnm.isType("ChatStatisticsCS")) + client->getClientConnection()->sendByteStream(msg.getByteStream(), msg.getReliable()); + + GameConnection *gc = client->getGameConnection(); + if (gc) + { + if (gnm.isType("ChatOnGetFriendsList")) + { + Archive::ReadIterator cri(msg.getByteStream()); + ChatOnGetFriendsList c(cri); + gc->send(c, true); + //DEBUG_REPORT_LOG(true, ("Sending a ChatOnGetFriendsList msg to game server\n")); + } + else if (gnm.isType("ChatOnChangeIgnoreStatus")) + { + Archive::ReadIterator cri(msg.getByteStream()); + ChatOnChangeIgnoreStatus c(cri); + gc->send(c, true); + //DEBUG_REPORT_LOG(true, ("Sending a ChatOnChangeIgnoreStatus msg to game server\n")); + } + else if (gnm.isType("ChatOnChangeFriendStatus")) + { + Archive::ReadIterator cri(msg.getByteStream()); + ChatOnChangeFriendStatus c(cri); + gc->send(c, true); + //DEBUG_REPORT_LOG(true, ("Sending a ChatOnChangeFriendStatus msg to game server\n")); + } + else if (gnm.isType("ChatOnGetIgnoreList")) + { + Archive::ReadIterator cri(msg.getByteStream()); + ChatOnGetIgnoreList c(cri); + gc->send(c, true); + //DEBUG_REPORT_LOG(true, ("Sending a ChatOnGetIgnoreList msg to game server\n")); + } + else if (gnm.isType("ChatStatisticsCS")) + { + Archive::ReadIterator cri(msg.getByteStream()); + GenericValueTypeMessage, std::pair > > chatStatistics(cri); + gc->send(chatStatistics, true); + //DEBUG_REPORT_LOG(true, ("Sending a ChatStatisticsCS msg to game server\n")); + } + } + } + } + } +} + +//----------------------------------------------------------------------- + +void ChatServerConnection::removeClient(Client * oldClient) +{ + std::set::iterator f = clients.find(oldClient); + if(f != clients.end()) + clients.erase(f); +} + +//----------------------------------------------------------------------- + + diff --git a/engine/server/application/ConnectionServer/src/shared/ChatServerConnection.h b/engine/server/application/ConnectionServer/src/shared/ChatServerConnection.h new file mode 100644 index 00000000..db9a44d8 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ChatServerConnection.h @@ -0,0 +1,36 @@ +// ChatServerConnection.h +// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +#ifndef _INCLUDED_ChatServerConnection_H +#define _INCLUDED_ChatServerConnection_H + +//----------------------------------------------------------------------- + +#include "serverUtility/ServerConnection.h" + +class Client; + +//----------------------------------------------------------------------- + +class ChatServerConnection : public ServerConnection +{ +public: + explicit ChatServerConnection(UdpConnectionMT *, TcpClient *); + virtual ~ChatServerConnection(); + void addClient(Client *); + void onConnectionClosed (); + void onConnectionOpened (); + void onReceive (const Archive::ByteStream &); + void removeClient(Client *); + const std::set & getClients() const; +private: + ChatServerConnection(); + ChatServerConnection & operator = (const ChatServerConnection & rhs); + ChatServerConnection(const ChatServerConnection & source); + std::set clients; +}; + +//----------------------------------------------------------------------- + +#endif // _INCLUDED_ChatServerConnection_H diff --git a/engine/server/application/ConnectionServer/src/shared/Client.cpp b/engine/server/application/ConnectionServer/src/shared/Client.cpp new file mode 100644 index 00000000..5809bfde --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/Client.cpp @@ -0,0 +1,313 @@ +// ----------------------------------------- +// Client.cpp +// copyright 2001 Sony Online Entertainment +// ----------------------------------------- + +#include "FirstConnectionServer.h" + +#include "ChatServerConnection.h" +#include "CustomerServiceConnection.h" +#include "Client.h" +#include "ClientConnection.h" +#include "serverNetworkMessages/ChatConnectAvatar.h" +#include "serverNetworkMessages/ChatDisconnectAvatar.h" +#include "serverNetworkMessages/GameConnectionServerMessages.h" +#include "sharedGame/PlatformFeatureBits.h" +#include "sharedLog/Log.h" +#include "sharedMessageDispatch/Transceiver.h" +#include "sharedNetworkMessages/ChatEnterRoomById.h" +#include "sharedNetworkMessages/ChatServerStatus.h" +#include "sharedNetworkMessages/DisconnectPlayerMessage.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" + +//---------------------------------------------------------------------- + +Client::Client(ClientConnection * cconn, const NetworkId& oid) : +Receiver(), +m_chatConnection(0), +m_customerServiceConnection(0), +m_clientConnection(cconn), +m_deferredChatMessages(), +m_hasBeenKicked(false), +m_oid(oid), +m_gameConnection(0), +//m_roomCache(), +m_sceneName(), +m_skipLoadScreen(false), +m_callback(new MessageDispatch::Callback) +{ + static const std::string loginTrace("TRACE_LOGIN"); + LOG(loginTrace, ("new Client(%s)", m_oid.getValueString().c_str())); + + // until/unless a chat server connection is set, the client will + // listen for a new chat server coming online. + // This handles the case where a client connects and there + // are no chat servers currently running (e.g. a crash happens + // at the point in time a client is connecting) + connectToMessage("ChatServerConnectionOpened"); + connectToMessage("CustomerServiceConnectionOpened"); + m_callback->connect(*this, &Client::onChatConnectionClosed); +} + +//---------------------------------------------------------------------- + +Client::~Client() +{ + ConnectionServer::dropClient(m_clientConnection, "Destroying Client Object"); + if(m_chatConnection) + { + m_chatConnection->removeClient(this); + m_chatConnection = 0; + } + + if(m_customerServiceConnection) + { + std::vector v; + v.clear(); + v.push_back(getNetworkId()); + DisconnectPlayerMessage message; + GameClientMessage gcm(v, true, message); + m_customerServiceConnection->send(gcm, true); + + m_customerServiceConnection->removeClient(this); + m_customerServiceConnection = 0; + } + + if (m_oid != NetworkId::cms_invalid && ConnectionServer::getClient(m_oid)) + { + WARNING_STRICT_FATAL(true, ("Attempting to delete client %d without removing him from map\n", m_oid.getValueString().c_str())); + } + m_clientConnection = 0; + + setGameConnection(0); + m_gameConnection = 0; + delete m_callback; +} + +//----------------------------------------------------------------------- + +void Client::onChatConnectionClosed(Connection * closedConnection) +{ + if(m_chatConnection == closedConnection) + { + m_chatConnection->removeClient(this); + m_chatConnection = 0; + } +} + +//----------------------------------------------------------------------- + +void Client::deferChatMessage(const Archive::ByteStream & message) +{ + m_deferredChatMessages.push_back(message); +} + +//----------------------------------------------------------------------- +/* +void Client::enterRoom(const unsigned int roomId) +{ + IGNORE_RETURN ( m_roomCache.insert(roomId) ); +} +*/ + +//----------------------------------------------------------------------- + +void Client::flushChatMessages() +{ + if(m_chatConnection) + { + std::vector::const_iterator i; + for(i = m_deferredChatMessages.begin(); i != m_deferredChatMessages.end(); ++i) + { + m_chatConnection->Connection::send((*i), true); + } + m_deferredChatMessages.clear(); + } +} + +//------------------------------------------------------------ + +void Client::handleTransfer(const std::string & sceneName, GameConnection* conn) +{ + NOT_NULL(conn); + //Set the new scene name + setSceneName(sceneName); + + //If we are already connected to a game server drop them + if (getGameConnection() && getGameConnection() != conn) + { + DropClient msg(getNetworkId()); + getGameConnection()->send(msg, true); + } + //Set the new game server + setGameConnection(conn); + m_skipLoadScreen = false; +} + +//----------------------------------------------------------------------- +/* +void Client::leaveRoom(const unsigned int roomId) +{ + std::set::iterator f = m_roomCache.find(roomId); + if(f != m_roomCache.end()) + m_roomCache.erase(f); +} +*/ +//------------------------------------------------------------ + +void Client::receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message) +{ + if(message.isType("GameConnectionClosed")) + { + // Game server has crashed. With luck, we'll get transferred to a new server shortly + // So, clear our connection and put us on a queue. If we don't get transferred in a + // reasonable about of time, we'll be dropped. + + m_gameConnection = 0; + ConnectionServer::addRecoveringClient(getNetworkId()); + } + else if(message.isType("ChatServerConnectionOpened")) + { + const ChatServerConnection & chatConnection = static_cast(source); + setChatConnection(const_cast(&chatConnection)); + } + else if(message.isType("CustomerServiceConnectionOpened")) + { + const CustomerServiceConnection & customerServiceConnection = static_cast(source); + setCustomerServiceConnection(const_cast(&customerServiceConnection)); + } +} + +//----------------------------------------------------------------------- + +void Client::setChatConnection(ChatServerConnection * c) +{ + if(m_chatConnection) + { + disconnectFromEmitter(*m_chatConnection, "ChatServerConnectionClosed"); + // advise the old chat server that this client is disconnecting + // from it + NOT_NULL(m_clientConnection); + ChatDisconnectAvatar m(m_clientConnection->getCharacterId()); + m_chatConnection->send(m, true); + } + else + { + disconnectFromMessage("ChatServerConnectionOpened"); + } + + m_chatConnection = c; + + if(c && m_clientConnection) + { + // advise the new chat server that the client is connecting + connectToEmitter(*m_chatConnection, "ChatServerConnectionClosed"); + ChatConnectAvatar connectAvatar(m_clientConnection->getCharacterName(), m_clientConnection->getCharacterId(), m_clientConnection->getSUID(), m_clientConnection->getIsSecure(), ((m_clientConnection->getSubscriptionFeatures() & ClientSubscriptionFeature::Base) != 0)); + m_chatConnection->send(connectAvatar, true); + m_chatConnection->addClient(this); + + /* + std::vector id; + id.push_back(getNetworkId()); + std::set::const_iterator i; + for(i = m_roomCache.begin(); i != m_roomCache.end(); ++i) + { + ChatEnterRoomById chat(0, (*i)); + GameClientMessage gcm(id, true, chat); + c->send(gcm, true); + } + m_roomCache.clear(); + */ + + flushChatMessages(); + + ChatServerStatus status(true); + m_clientConnection->send(status, true); + } + else + { + connectToMessage("ChatServerConnectionOpened"); + ChatServerStatus status(false); + NOT_NULL(m_clientConnection); + m_clientConnection->send(status, false); + } +} + +//----------------------------------------------------------------------- + +void Client::setCustomerServiceConnection(CustomerServiceConnection * c) +{ + if(m_customerServiceConnection) + { + disconnectFromEmitter(*m_customerServiceConnection, "CustomerServiceConnectionClosed"); + NOT_NULL(m_clientConnection); + } + else + { + disconnectFromMessage("CustomerServiceConnectionOpened"); + } + + m_customerServiceConnection = c; + + if(c && m_clientConnection) + { + connectToEmitter(*m_customerServiceConnection, "CustomerServiceConnectionClosed"); + m_customerServiceConnection->addClient(this); + } + else + { + connectToMessage("CustomerServiceConnectionOpened"); + } +} + +//------------------------------------------------------------ + +void Client::setGameConnection(GameConnection* conn) +{ + if (m_gameConnection != conn) + { + if (m_gameConnection) + { + disconnectFromEmitter(*m_gameConnection, "GameConnectionClosed"); + } + if (conn) + { + connectToEmitter(*conn, "GameConnectionClosed"); + } + m_gameConnection = conn; + } +} + +//------------------------------------------------------------ + +void Client::kick(const std::string& reason) +{ + ClientConnection *c = getClientConnection(); + ConnectionServer::dropClient(c, reason); + + m_hasBeenKicked = true; + if (getClientConnection()) + { + LOG("CustomerService", ("Login:%s Dropped Reason: %s. Character: %s (%s). Play time: %s. Active play time: %s", ClientConnection::describeAccount(c).c_str(), reason.c_str(), getClientConnection()->getCharacterName().c_str(), getClientConnection()->getCharacterId().getValueString().c_str(), getClientConnection()->getPlayTimeDuration().c_str(), getClientConnection()->getActivePlayTimeDuration().c_str())); + } + else + { + LOG("CustomerService", ("Login: Account unknown (lost client connection) id %s Dropped Reason: %s. Character: . Play time: . Active play time: ", m_oid.getValueString().c_str(), reason.c_str())); + } +} + +//------------------------------------------------------------ + +bool Client::getSkipLoadScreen() const +{ + return m_skipLoadScreen; +} + +//------------------------------------------------------------ + +void Client::skipLoadScreen() +{ + m_skipLoadScreen = true; +} + +//---------------------------------------------------------------------- diff --git a/engine/server/application/ConnectionServer/src/shared/Client.h b/engine/server/application/ConnectionServer/src/shared/Client.h new file mode 100644 index 00000000..6fee22d0 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/Client.h @@ -0,0 +1,146 @@ +// ----------------------------------------- +// Client.h +// copyright 2001 Sony Online Entertainment +// ----------------------------------------- + + +#ifndef _Included_Client_H_ +#define _Included_Client_H_ + +#include "ChatServerConnection.h" +#include "ClientConnection.h" +#include "CustomerServiceConnection.h" +#include "GameConnection.h" +#include "sharedFoundation/NetworkId.h" +#include "sharedFoundation/Watcher.h" +#include "sharedMessageDispatch/Receiver.h" +#include + + +class ChatServerConnection; +class CustomerServiceConnection; +class ClientConnection; +class GameConnection; + +namespace MessageDispatch +{ + class Callback; +} + +/** + * class Client represents one actual player logged into the cluster. + * it knows what client connection they are on, as well as their oids and + * game server mappings + */ + +//---------------------------------------------------------------------- + +class Client : public MessageDispatch::Receiver +{ +public: + + Client(ClientConnection* cconn, const NetworkId& oid); //ControlAssumed message should set the rest, GameConnection* gconn, std::string sceneName); + ~Client(); + + void deferChatMessage (const Archive::ByteStream & message); +// void enterRoom (const unsigned int roomId); + void flushChatMessages (); + ClientConnection* getClientConnection (); + const NetworkId& getNetworkId () const; + GameConnection* getGameConnection () const; + ChatServerConnection* getChatConnection () const; + CustomerServiceConnection* getCustomerServiceConnection () const; + const std::string& getSceneName () const; + bool getSkipLoadScreen () const; + bool hasBeenKicked() const; + void skipLoadScreen (); + void handleTransfer (const std::string & sceneName, GameConnection* conn); + void kick (const std::string& reason); +// void leaveRoom (const unsigned int roomId); + void receiveMessage (const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message); + void setChatConnection (ChatServerConnection * chatServerConnection); + void setCustomerServiceConnection (CustomerServiceConnection * customerServiceConnection); + +private: + Client(); + Client(const Client&); + Client & operator= (const Client&); + void setGameConnection(GameConnection* conn); + void setSceneName(const std::string &name); + void onChatConnectionClosed(Connection *); + +private: + //@todo...right now there is a one to one mapping between oid and client. + //this may not always be the case. + Watcher m_chatConnection; + Watcher m_customerServiceConnection; + ClientConnection* m_clientConnection; + std::vector m_deferredChatMessages; + bool m_hasBeenKicked; + NetworkId m_oid; + GameConnection* m_gameConnection; +// std::set m_roomCache; + std::string m_sceneName; + bool m_skipLoadScreen; + MessageDispatch::Callback * m_callback; +}; + +//----------------------------------------------------------------------- + +inline ChatServerConnection * Client::getChatConnection() const +{ + return m_chatConnection; +} + +//----------------------------------------------------------------------- + +inline CustomerServiceConnection * Client::getCustomerServiceConnection() const +{ + return m_customerServiceConnection; +} + +//------------------------------------------------------------ + +inline ClientConnection* Client::getClientConnection() +{ + return m_clientConnection; +} + +//------------------------------------------------------------ + +inline const NetworkId& Client::getNetworkId() const +{ + return m_oid; +} + +//------------------------------------------------------------ + +inline GameConnection* Client::getGameConnection() const +{ + return m_gameConnection; +} + +//------------------------------------------------------------ + +inline const std::string& Client::getSceneName() const +{ + return m_sceneName; +} + +//------------------------------------------------------------ + +inline bool Client::hasBeenKicked() const +{ + return m_hasBeenKicked; +} + + +//------------------------------------------------------------ + +inline void Client::setSceneName(const std::string & name) +{ + m_sceneName = name; +} + +//------------------------------------------------------------ +#endif diff --git a/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp b/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp new file mode 100644 index 00000000..892937fb --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ClientConnection.cpp @@ -0,0 +1,1688 @@ +// 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" + +//----------------------------------------------------------------------- + +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(NULL), +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 = NULL; + } + + // 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::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 = NULL; +} + + +//----------------------------------------------------------------------- + +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(::time(NULL) - m_startPlayTime); + + return CalendarTime::convertSecondsToHMS(static_cast(playTimeDuration)); +} + +//----------------------------------------------------------------------- + +std::string ClientConnection::getActivePlayTimeDuration() const +{ + int activePlayTimeDuration = static_cast(m_activePlayTimeDuration); + + if (m_lastActiveTime > 0) + activePlayTimeDuration += static_cast(::time(NULL) - m_lastActiveTime); + + return CalendarTime::convertSecondsToHMS(static_cast(activePlayTimeDuration)); +} + +//----------------------------------------------------------------------- + +std::string ClientConnection::getCurrentActivePlayTimeDuration() const +{ + int activePlayTimeDuration = 0; + + if (m_lastActiveTime > 0) + activePlayTimeDuration = static_cast(::time(NULL) - m_lastActiveTime); + + return CalendarTime::convertSecondsToHMS(static_cast(activePlayTimeDuration)); +} + +//----------------------------------------------------------------------- + +void ClientConnection::sendPlayTimeInfoToGameServer() const +{ + if (m_client && m_client->getGameConnection()) + { + // update the game server with play time info + GenericValueTypeMessage > > const msgPlayTimeInfo( + "UpdateSessionPlayTimeInfo", + std::make_pair(static_cast(m_startPlayTime), + std::make_pair(static_cast(m_lastActiveTime), m_activePlayTimeDuration) + ) + ); + + std::vector 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) +{ + //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; + char sessionId[apiSessionIdWidth]; + + m_gameBitsToClear = msg.getGameBitsToClear(); + + if(msg.getTokenSize() > 0) + { + Archive::ByteStream t(msg.getToken(), msg.getTokenSize()); + Archive::ReadIterator ri(t); + KeyShare::Token token(ri); + + 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 loginTrace("TRACE_LOGIN"); + LOG(loginTrace, ("ClientConnection SUID = %d", m_suid)); + + } + if (result) + { + //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; + } + + if (ConfigConnectionServer::getValidateStationKey()) + { + SessionApiClient * session = ConnectionServer::getSessionApiClient(); + NOT_NULL(session); + if(session) + { + session->validateClient(this, sessionId); + } + else + { + ConnectionServer::dropClient(this, "SessionApiClient is not available!"); + disconnect(); + } + } + else + { + m_suid = atoi(m_accountName.c_str()); + if (m_suid == 0) + { + std::hash h; + m_suid = h(m_accountName.c_str()); + } + onValidateClient(m_suid, m_accountName, m_isSecure, NULL, ConfigConnectionServer::getDefaultGameFeatures(), ConfigConnectionServer::getDefaultSubscriptionFeatures(), 0, 0, 0, 0, ConfigConnectionServer::getFakeBuddyPoints()); + } + } + else + { + // 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 erver. Disconnecting.", m_suid)); + disconnect(); + } +} + +//----------------------------------------------------------------------- + +void ClientConnection::onIdValidated(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial, std::vector > const & consumedRewardEvents, std::vector > 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 = NULL; + + 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); + std::string name("Connection overflow server->client"); + std::string desc(errbuf); + LOG("Network", ("Disconnect: Client connection overflowing. %d bytes pending", bytesPending)); + + std::vector >::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(Clock::frameTime()*1000.0f)) < curTimeMs) + { + static HeartBeat h; + send(h, false); + } + + Archive::ReadIterator ri = message.begin(); + GameNetworkMessage m(ri); + ri = message.begin(); + + //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 + if( + m.isType("ChatAddFriend") || + m.isType("ChatAddModeratorToRoom") || + m.isType("ChatBanAvatarFromRoom") || + m.isType("ChatCreateRoom") || + m.isType("ChatDeletePersistentMessage") || + m.isType("ChatDeleteAllPersistentMessages") || + m.isType("ChatDestroyRoom") || + m.isType("ChatInstantMessageToCharacter") || + m.isType("ChatInviteAvatarToRoom") || + m.isType("ChatKickAvatarFromRoom") || + m.isType("ChatRemoveAvatarFromRoom") || + m.isType("ChatRemoveFriend") || + m.isType("ChatRemoveModeratorFromRoom") || + m.isType("ChatRequestPersistentMessage") || + m.isType("ChatRequestRoomList") || + m.isType("ChatSendToRoom") || + m.isType("ChatUninviteFromRoom") || + m.isType("ChatUnbanAvatarFromRoom") || + m.isType("VerifyPlayerNameMessage") || + m.isType("VoiceChatRequestPersonalChannel") || + m.isType("VoiceChatInvite") || + m.isType("VoiceChatKick") || + m.isType("VoiceChatRequestChannelInfo") + ) + { + DEBUG_REPORT_LOG(true, ("ConnServ: ClientConnection::onReceive()\n")); + + NOT_NULL(m_client); + if(m_client) + { + static std::vector 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 null while receiving a message!"); + disconnect(); + } + } + // 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 + else if (m.isType("ChatEnterRoom") || + m.isType("ChatEnterRoomById") + ) + { + NOT_NULL(m_client); + + unsigned int sequence; + std::string roomName; + + Archive::ReadIterator cri = message.begin(); + if (m.isType("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, 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 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); + } + } + // 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 + else if (m.isType("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, 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 v; + v.clear(); + v.push_back(m_client->getNetworkId()); + m_pendingChatQueryRoomRequests[cqr.getSequence()] = new GameClientMessage(v, true, ri); + } + } + } + // ChatInviteGroupToRoom needs to go to the game server to get group information + else if (m.isType("ChatInviteGroupToRoom")) + { + NOT_NULL(m_client); + if(m_client) + { + if (m_client->getGameConnection()) + { + static std::vector 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 null while receiving a message!"); + disconnect(); + } + } + // ChatPersistentMessageToServer may need to be passed off to the game server for guild or citizens messages + else if (m.isType("ChatPersistentMessageToServer")) + { + NOT_NULL(m_client); + if(m_client) + { + static std::vector 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 null while receiving a message!"); + disconnect(); + } + } + // if it is a cs message, send it directly to the cs server + else if ( + m.isType("ConnectPlayerMessage") || + m.isType("DisconnectPlayerMessage") || + m.isType("CreateTicketMessage") || + m.isType("AppendCommentMessage") || + m.isType("CancelTicketMessage") || + m.isType("GetTicketsMessage") || + m.isType("GetCommentsMessage") || + m.isType("SearchKnowledgeBaseMessage") || + m.isType("GetArticleMessage") || + m.isType("RequestCategoriesMessage") || + m.isType("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 != NULL) + { + static std::vector v; + v.clear(); + v.push_back(m_client->getNetworkId()); + if (m.isType("ConnectPlayerMessage")) + { + ConnectPlayerMessage message(ri); + message.setStationId(getSUID()); + + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm , true); + } + else if (m.isType("CreateTicketMessage")) + { + CreateTicketMessage message(ri); + message.setStationId(getSUID()); + + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm , true); + } + else if (m.isType("AppendCommentMessage")) + { + AppendCommentMessage message(ri); + message.setStationId(getSUID()); + + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm , true); + } + else if (m.isType("CancelTicketMessage")) + { + CancelTicketMessage message(ri); + message.setStationId(getSUID()); + + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm , true); + } + else if (m.isType("GetTicketsMessage")) + { + GetTicketsMessage message(ri); + message.setStationId(getSUID()); + + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm , true); + } + else if (m.isType("NewTicketActivityMessage")) + { + NewTicketActivityMessage message(ri); + message.setStationId(getSUID()); + + GameClientMessage gcm(v, true, message); + customerServiceConnection->send(gcm , true); + } + else + { + GameClientMessage gcm(v, true, ri); + customerServiceConnection->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 null while receiving a message!"); + disconnect(); + } + } + else if (m.isType("28afefcc187a11dc888b001")) // obfuscation for ClientInactivityMessage message + { + GenericValueTypeMessage 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(::time(NULL) - 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(NULL); + + // 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(); + } + } + } + } + else + { + //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 v; + v.clear(); + v.push_back(m_client->getNetworkId()); + GameClientMessage gcm(v, true, ri); + m_client->getGameConnection()->send(gcm, true); + } + } + } + + else if(m.isType("ClientIdMsg")) + { + DEBUG_REPORT_LOG(true,("Recieved ClientIdMsg\n")); + ClientIdMsg k(ri); + + handleClientIdMessage(k); + } + else if(m.isType("SelectCharacter")) + { + SelectCharacter s(ri); + DEBUG_REPORT_LOG(true,("Recvd SelectCharacter message for %s.\n", s.getId().getValueString().c_str())); + + handleSelectCharacterMessage(s); + } + else if(m.isType("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(clientCreate.getJedi()), + static_cast(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; + } + } + } + else if(m.isType("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())); + } + else if(m.isType("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())); + } + else if(m.isType("LagRequest")) + { + /* + handleLagRequest(); + */ + } + } + 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 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::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::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(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(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; + + + + // If they don't have access to mustafar but are on the planet, move them to a safe spot + uint32 features = getGameFeatures(); + if ( (strncmp(scene.c_str(), "mustafar", strlen("mustafar")) == 0 ) && + ( (features & ClientGameFeature::TrialsOfObiwanRetail) == 0 ) ) + { + // mos eisley starport + m_sendToStarport = true; + m_targetScene = "tatooine"; + m_targetCoordinates.x = 3528; + m_targetCoordinates.y = 4; + m_targetCoordinates.z = -4804; + + // Make sure Central knows the right sceneId for the player + GenericValueTypeMessage > > const msg( + "SetSceneForPlayer", + std::make_pair( + m_characterId, + std::make_pair("tatooine", false))); + ConnectionServer::sendToCentralProcess(msg); + + + LOG("TraceCharacterSelection", ("Character didn't have Mustafar feature bit, moving to Tattoine.")); + } + + + // 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 (uint32 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; + } + + //hack to prevent non-jtl users from using jtl assets. In this hack, clients who didn't patch through jtl patcher will send us information requesting we clear their jtl bit since they are supposed to go through the jtl patcher. We can remove this hack once the launch pad takes care of this for us. + m_featureBitsGame &= ~m_gameBitsToClear; + //end hack + + 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 > > const msgFeatureBits("AccountFeatureBits", std::make_pair(std::make_pair(gameFeatures, subscriptionFeatures), std::make_pair(ConfigConnectionServer::getConnectionServerNumber(), static_cast(::time(NULL))))); + 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 > 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 > const & ClientConnection::getConsumedRewardEvents() const +{ + return m_consumedRewardEvents; +} + +// ---------------------------------------------------------------------- + +std::vector > const & ClientConnection::getClaimedRewardItems() const +{ + return m_claimedRewardItems; +} + +// ---------------------------------------------------------------------- + +bool ClientConnection::isUsingAdminLogin() const +{ + return m_usingAdminLogin; +} + +// ---------------------------------------------------------------------- + +int ClientConnection::getBuddyPoints() const +{ + return m_buddyPoints; +} + +// ====================================================================== diff --git a/engine/server/application/ConnectionServer/src/shared/ClientConnection.h b/engine/server/application/ConnectionServer/src/shared/ClientConnection.h new file mode 100644 index 00000000..a0184cec --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ClientConnection.h @@ -0,0 +1,354 @@ +// ClientConnection.h +// copyright 2001 Verant Interactive + +#ifndef _ClientConnection_H +#define _ClientConnection_H + +//----------------------------------------------------------------------- + +#include "Client.h" +#include "Unicode.h" +#include "serverUtility/ServerConnection.h" +#include "sharedFoundation/NetworkId.h" +#include "sharedFoundation/StationId.h" +#include "sharedMath/Vector.h" +#include + +class ClientIdMsg; +class SelectCharacter; +class GameConnection; +class GameClientMessage; +class ConnectionCreateCharacter; + +//----------------------------------------------------------------------- + +class ClientConnection : public ServerConnection +{ +public: + struct CharacterData + { + Unicode::String name; + std::string narrowName; + std::string location; + std::string objectTemplate; + NetworkId characterId; + NetworkId containerId; + Vector coordinates; + }; + + explicit ClientConnection(UdpConnectionMT *, TcpClient *); + virtual ~ClientConnection(); + + void addCharacter (const Unicode::String & name, + const NetworkId &characterId, + const std::string & location, + const std::string & objectTemplate, + const NetworkId &containerId, + const Vector & coordinates); + + const std::string & getAccountName () const; + const NetworkId & getCharacterId () const; + const std::string & getCharacterName () const; + void setStartPlayTime (time_t startPlayTime); + time_t getStartPlayTime () const; + std::string getPlayTimeDuration () const; + std::string getActivePlayTimeDuration() const; + std::string getCurrentActivePlayTimeDuration() const; + void sendPlayTimeInfoToGameServer() const; + const Client * getClient () const; + Client * getClient (); + void setClient (Client* client); + + uint32 getSUID () const; + + const bool getHasSelectedCharacter () const; + bool getHasBeenSentToGameServer () const; + bool getIsSecure () const; + const std::string & getSessionId() const; + const std::string & getTargetSecene () const; + unsigned int getGameFeatures() const; + unsigned int getSubscriptionFeatures() const; + bool getHasCSLoggedAccountFeatureIds() const; + void setHasCSLoggedAccountFeatureIds(bool hasCSLoggedAccountFeatureIds); + unsigned int getEntitlementTotalTime () const; + unsigned int getEntitlementEntitledTime () const; + unsigned int getEntitlementTotalTimeSinceLastLogin () const; + unsigned int getEntitlementEntitledTimeSinceLastLogin () const; + int getBuddyPoints() const; + std::vector > const & getConsumedRewardEvents() const; + std::vector > const & getClaimedRewardItems() const; + bool isUsingAdminLogin () const; + bool getCanSkipTutorial () const; + + virtual void onConnectionClosed (); + virtual void onConnectionOpened (); + virtual void onConnectionOverflowing (const unsigned int bytesPending); + virtual void onReceive (const Archive::ByteStream & message); + virtual void send (const GameNetworkMessage & message, const bool reliable); + void sendByteStream (const Archive::ByteStream &bs, bool reliable); + const bool sendToGameServer (uint32 gameServerId); + const bool sendToGameServer (); + + void handleGameServerForLoginMessage(uint32 serverId); + void onIdValidated(bool canLogin, bool canCreateRegularCharacter, bool canCreateJediCharacter, bool canSkipTutorial, std::vector > const & consumedRewardEvents, std::vector > const & claimedRewardItems); + void onValidateClient (uint32 id, const std::string & username, bool, const char*, uint32 gameFeatures, uint32 subscriptionFeatures, unsigned int entitlementTotalTime, unsigned int entitlementEntitledTime, unsigned int entitlementTotalTimeSinceLastLogin, unsigned int entitlementEntitledTimeSinceLastLogin, int buddyPoints); + void onCharacterValidated(bool isValid, const NetworkId &character, const std::string &characterName, const NetworkId &container, const std::string &scene, const Vector &coordinates); + static std::string describeAccount(const ClientConnection *); + + static std::map< std::string, uint32 >& getPacketBytesPerMinStats(); + + void handleChatEnterRoomValidationResponse(unsigned int sequence, unsigned int result); + void handleChatQueryRoomValidationResponse(unsigned int sequence, bool success); + + void setHasRequestedCharacterCreate(bool value); + void setHasCreatedCharacter(bool value); + +private: + ClientConnection(); + ClientConnection(const ClientConnection&); + ClientConnection& operator=(const ClientConnection&); + void handleLagRequest (); + bool checkSpamLimit (unsigned int messageSize); + + static std::map< std::string, uint32 > sm_outgoingBytesMap_Working; // working stats that will rotate after 1 minute + static std::map< std::string, uint32 > sm_outgoingBytesMap_Stats; // computed stats from the last minute + static uint32 sm_outgoingBytesMap_Worktime; // time we started filling in the working map + + std::string m_accountName; + bool m_canCreateRegularCharacter; + bool m_canCreateJediCharacter; + + // to prevent exploit, a connection to the client is allowed to only request + // creating a new character once; we will disconnect the connection if we + // receive a second request; this will force the client to connect again, + // at which time another validation will be done to see if creating a new + // character is allowed + bool m_hasRequestedCharacterCreate; + + // to prevent exploit, a connection to the client is allowed to + // create a new character once; we will disconnect the connection if we + // receive a request to create another character; this will force the + // client to connect again, at which time another validation will be + // done to see if creating a new character is allowed + bool m_hasCreatedCharacter; + + // hang on to a character create request while we check with the LoginServer + // one last time to make sure the account can create a new character + ConnectionCreateCharacter * m_pendingCharacterCreate; + + bool m_canSkipTutorial; + NetworkId m_characterId; + std::string m_characterName; + time_t m_startPlayTime; // time when the player started playing the character + time_t m_lastActiveTime; // the client will detect when the player is "active" or "inactive"; this keeps track of the last time that the client said the player was "active"; if 0, it means the client is currently "inactive" + unsigned long m_activePlayTimeDuration; // total amount of play time player was active (i.e. at the mouse/keyboard/joystick) + Client * m_client; + NetworkId m_containerId; + uint32 m_featureBitsGame; + uint32 m_featureBitsSubscription; + uint32 m_gameBitsToClear; + bool m_hasBeenSentToGameServer; + bool m_hasBeenValidated; + bool m_hasSelectedCharacter; + bool m_isSecure; + bool m_isAdminAccount; // Note: means this account is on the admin list, not that the account has god powers in this session. Using SecureId and being on the right IP subnet are also required to run god commands. m_isAdminAccount will be true for admins playing from home, for example. + bool m_hasCSLoggedAccountFeatureIds; + StationId m_suid; + StationId m_requestedSuid; + bool m_usingAdminLogin; + Vector m_targetCoordinates; + std::string m_targetScene; + bool m_validatingCharacter; + unsigned int m_receiveHistoryBytes; + unsigned int m_receiveHistoryPackets; + unsigned long m_receiveHistoryMs; + unsigned long m_receiveLastTimeMs; + mutable unsigned long m_sendLastTimeMs; + std::string m_sessionId; + bool m_sessionValidated; + int m_connectionServerLag; + int m_gameServerLag; + unsigned long m_countSpamLimitResetTime; + unsigned int m_entitlementTotalTime; + unsigned int m_entitlementEntitledTime; + unsigned int m_entitlementTotalTimeSinceLastLogin; + unsigned int m_entitlementEntitledTimeSinceLastLogin; + int m_buddyPoints; + std::vector > m_consumedRewardEvents; + std::vector > m_claimedRewardItems; + + bool m_sendToStarport; + + // chat enter room requests that came from the client that's awaiting + // game sever approval before being forwarded to the chat server + std::map m_pendingChatEnterRoomRequests; + + // ChatQueryRoom requests that came from the client that's awaiting + // game sever approval before being forwarded to the chat server + std::map m_pendingChatQueryRoomRequests; + + //Message handler functions + void handleClientIdMessage(const ClientIdMsg& msg); + void handleSelectCharacterMessage(const SelectCharacter& msg); + bool validateSelection(const Unicode::String & characterName); + + bool sendToGameServer(GameConnection *c); +}; + +//----------------------------------------------------------------------- + +inline const std::string & ClientConnection::getAccountName() const +{ + return m_accountName; +} + +//----------------------------------------------------------------------- + +inline void ClientConnection::setStartPlayTime(time_t startPlayTime) +{ + m_startPlayTime = startPlayTime; +} + +//----------------------------------------------------------------------- + +inline time_t ClientConnection::getStartPlayTime() const +{ + return m_startPlayTime; +} + +//----------------------------------------------------------------------- + +inline const Client* ClientConnection::getClient() const +{ + return m_client; +} + +//----------------------------------------------------------------------- + +inline Client* ClientConnection::getClient() +{ + return m_client; +} + +//----------------------------------------------------------------------- + +inline const bool ClientConnection::getHasSelectedCharacter() const +{ + return m_hasSelectedCharacter; +} + +//----------------------------------------------------------------------- + +inline const std::string & ClientConnection::getTargetSecene() const +{ + return m_targetScene; +} + +//----------------------------------------------------------------------- + +inline uint32 ClientConnection::getSUID() const +{ + return m_suid; +} + +//----------------------------------------------------------------------- + +inline bool ClientConnection::getHasBeenSentToGameServer() const +{ + return m_hasBeenSentToGameServer; +} + +// ---------------------------------------------------------------------- + +inline bool ClientConnection::getIsSecure() const +{ + return m_isSecure; +} + +// ---------------------------------------------------------------------- + +inline const std::string & ClientConnection::getSessionId() const +{ + return m_sessionId; +} + +// ---------------------------------------------------------------------- + +inline unsigned int ClientConnection::getGameFeatures() const +{ + return m_featureBitsGame; +} + +// ---------------------------------------------------------------------- + +inline unsigned int ClientConnection::getSubscriptionFeatures() const +{ + return m_featureBitsSubscription; +} + +// ---------------------------------------------------------------------- + +inline unsigned int ClientConnection::getEntitlementTotalTime() const +{ + return m_entitlementTotalTime; +} + +// ---------------------------------------------------------------------- + +inline unsigned int ClientConnection::getEntitlementEntitledTime() const +{ + return m_entitlementEntitledTime; +} + +// ---------------------------------------------------------------------- + +inline unsigned int ClientConnection::getEntitlementTotalTimeSinceLastLogin() const +{ + return m_entitlementTotalTimeSinceLastLogin; +} + +// ---------------------------------------------------------------------- + +inline unsigned int ClientConnection::getEntitlementEntitledTimeSinceLastLogin() const +{ + return m_entitlementEntitledTimeSinceLastLogin; +} + +// ---------------------------------------------------------------------- + +inline bool ClientConnection::getCanSkipTutorial() const +{ + return m_canSkipTutorial; +} + +// ---------------------------------------------------------------------- + +inline void ClientConnection::setHasRequestedCharacterCreate(bool value) +{ + m_hasRequestedCharacterCreate = value; +} + +// ---------------------------------------------------------------------- + +inline void ClientConnection::setHasCreatedCharacter(bool value) +{ + m_hasCreatedCharacter = value; +} + +// ---------------------------------------------------------------------- + +inline bool ClientConnection::getHasCSLoggedAccountFeatureIds() const +{ + return m_hasCSLoggedAccountFeatureIds; +} + +// ---------------------------------------------------------------------- + +inline void ClientConnection::setHasCSLoggedAccountFeatureIds(bool hasCSLoggedAccountFeatureIds) +{ + m_hasCSLoggedAccountFeatureIds = hasCSLoggedAccountFeatureIds; +} + +// ---------------------------------------------------------------------- + +#endif // _ClientConnection_H diff --git a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp new file mode 100644 index 00000000..2fe48afb --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.cpp @@ -0,0 +1,166 @@ +// ConfigConnectionServer.cpp +// copyright 2001 Verant Interactive + + +//----------------------------------------------------------------------- + +#include "FirstConnectionServer.h" +#include "ConfigConnectionServer.h" +#include "SessionApiClient.h" +#include "serverUtility/ConfigServerUtility.h" +#include "sharedFoundation/ConfigFile.h" +#include "sharedGame/PlatformFeatureBits.h" +#include "sharedNetwork/SetupSharedNetwork.h" + +//----------------------------------------------------------------------- + +ConfigConnectionServer::Data * ConfigConnectionServer::data = 0; + +#define KEY_INT(a,b) (data->a = ConfigFile::getKeyInt("ConnectionServer", #a, b)) +#define KEY_BOOL(a,b) (data->a = ConfigFile::getKeyBool("ConnectionServer", #a, b)) +#define KEY_FLOAT(a,b) (data->a = ConfigFile::getKeyFloat("ConnectionServer", #a, b)) +#define KEY_STRING(a,b) (data->a = ConfigFile::getKeyString("ConnectionServer", #a, b)) + +//----------------------------------------------------------------------- + +namespace ConfigConnectionServerNamespace +{ + typedef std::vector StringPtrArray; + StringPtrArray ms_sessionServer; // ConfigFile owns the pointer +} + +using namespace ConfigConnectionServerNamespace; + +// ====================================================================== + +int ConfigConnectionServer::getNumberOfSessionServers() +{ + return static_cast(ms_sessionServer.size()); +} + +// ---------------------------------------------------------------------- + +char const * ConfigConnectionServer::getSessionServer(int index) +{ + VALIDATE_RANGE_INCLUSIVE_EXCLUSIVE(0, index, getNumberOfSessionServers()); + return ms_sessionServer[static_cast(index)]; +} + +void ConfigConnectionServer::install(void) +{ + ConfigServerUtility::install(); + + SetupSharedNetwork::SetupData networkSetupData; + SetupSharedNetwork::getDefaultServerSetupData(networkSetupData); + SetupSharedNetwork::install(networkSetupData); + + data = new ConfigConnectionServer::Data; + + KEY_STRING (centralServerAddress, "swo-dev5.station.sony.com"); + KEY_INT (centralServerPort, 0); + KEY_STRING (clientServiceBindInterface, ""); + KEY_INT (clientServicePortPrivate, 44464); + KEY_INT (clientServicePortPublic, 44463); + KEY_INT (clientOverflowLimit, 1024 * 1024); // 1MB overflow + KEY_INT (gameServicePort, 0); + KEY_INT (pingPort, 0); + KEY_STRING (clusterName, "devcluster"); + KEY_INT (maxClients, 200); + KEY_BOOL (spamLimitEnabled, false); + KEY_INT (spamLimitResetTimeMs, 30*1000); + KEY_INT (spamLimitResetScaleFactor, 8); + KEY_INT (spamLimitBytesPerSec, 32000); + KEY_INT (spamLimitPacketsPerSec, 50); + KEY_BOOL (startPublicServer, true); + KEY_BOOL (disableWorldSnapshot, true); + KEY_STRING (gameServiceBindInterface, ""); + KEY_STRING (chatServiceBindInterface, ""); + KEY_STRING (customerServiceBindInterface, ""); + KEY_BOOL (compressClientNetworkTraffic, true); + KEY_INT (crashRecoveryTimeout, 15*1000); // timeout for players to recover from a server crash + KEY_BOOL (shouldSleep, true); + KEY_INT (clientMaxOutstandingPackets, 1000); + KEY_INT (clientMaxRawPacketSize, 500); + KEY_INT (clientMaxConnections, 200); + KEY_INT (clientFragmentSize, 500); + KEY_INT (clientMaxDataHoldTime, 20); + KEY_INT (clientHashTableSize, 200); + KEY_INT (lagReportThreshold, 10000); + KEY_INT (defaultGameFeatures, 0xFFFFFFFF); + KEY_INT (defaultSubscriptionFeatures, 0xFFFFFFFF); + + KEY_BOOL (validateStationKey, false); + KEY_STRING (sessionServers, ""); + KEY_INT (sessionType, SESSION_TYPE_STARWARS); + KEY_BOOL (disableSessionLogout, false); + KEY_BOOL (sessionRecordPlayTime, true); + KEY_BOOL (disconnectOnInactive, false); + KEY_BOOL (disconnectFreeTrialOnInactive, false); + + KEY_STRING (adminAccountDataTable, "datatables/admin/us_admin.iff"); + + KEY_INT (requiredSubscriptionBits, 0); + KEY_INT (requiredGameBits, 0); + KEY_BOOL (setJtlRetailIfBetaIsSet, false); + + KEY_BOOL (validateClientVersion, true); + KEY_BOOL (setEpisode3RetailIfBetaIsSet, false); + KEY_BOOL (setTrialsOfObiwanRetailIfBetaIsSet, false); + KEY_INT (disabledFeatureBits, 0); + + KEY_FLOAT (timeBetweenSessionUpdates, 60.0f * 5.0f); + + KEY_INT (connectionServerNumber, 0); + KEY_INT (fakeBuddyPoints, 0); + + int index = 0; + char const * result = 0; + do + { + result = ConfigFile::getKeyString("ConnectionServer", "sessionServer", index++, 0); + if (result != 0) + { + ms_sessionServer.push_back(result); + } + } + while (result); +} + +//----------------------------------------------------------------------- + +void ConfigConnectionServer::remove(void) +{ + delete data; + data = 0; + ConfigServerUtility::remove(); +} + +//----------------------------------------------------------------------- + +int ConfigConnectionServer::getDisabledFeatureBits() +{ + return data->disabledFeatureBits; +} + +// ---------------------------------------------------------------------- + +bool ConfigConnectionServer::getDisconnectOnInactive() +{ + return data->disconnectOnInactive; +} + +// ---------------------------------------------------------------------- + +bool ConfigConnectionServer::getDisconnectFreeTrialOnInactive() +{ + return data->disconnectFreeTrialOnInactive; +} + +// ---------------------------------------------------------------------- + +int ConfigConnectionServer::getFakeBuddyPoints() +{ + return data->fakeBuddyPoints; +} + +// ====================================================================== diff --git a/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h new file mode 100644 index 00000000..46f67d1e --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ConfigConnectionServer.h @@ -0,0 +1,465 @@ +// ConfigConnectionServer.h +// copyright 2000 Verant Interactive +// Author: Justin Randall + +#ifndef _ConfigConnectionServer_H +#define _ConfigConnectionServer_H + +//----------------------------------------------------------------------- + +class ConfigConnectionServer +{ +public: + struct Data + { + const char* centralServerAddress; + int centralServerPort; + int clientServicePortPublic; + int clientServicePortPrivate; + int clientOverflowLimit; + int gameServicePort; + const char * clusterName; + bool disableWorldSnapshot; + int maxClients; + int pingPort; + bool spamLimitEnabled; + int spamLimitResetTimeMs; + int spamLimitResetScaleFactor; + int spamLimitBytesPerSec; + int spamLimitPacketsPerSec; + bool startPublicServer; + const char * chatServiceBindInterface; + const char * clientServiceBindInterface; + const char * customerServiceBindInterface; + const char * gameServiceBindInterface; + bool compressClientNetworkTraffic; + int crashRecoveryTimeout; + bool shouldSleep; + int clientMaxOutstandingPackets; + int clientMaxRawPacketSize; + int clientMaxConnections; + int clientFragmentSize; + int clientMaxDataHoldTime; + int clientHashTableSize; + int lagReportThreshold; + + bool validateStationKey; + const char * sessionServers; + int sessionType; + bool disableSessionLogout; + bool sessionRecordPlayTime; + bool disconnectOnInactive; + bool disconnectFreeTrialOnInactive; + const char * adminAccountDataTable; + + float timeBetweenSessionUpdates; + + int defaultGameFeatures; + int defaultSubscriptionFeatures; + int requiredSubscriptionBits; + int requiredGameBits; + bool setJtlRetailIfBetaIsSet; + bool setEpisode3RetailIfBetaIsSet; + bool setTrialsOfObiwanRetailIfBetaIsSet; + + int disabledFeatureBits; + + bool validateClientVersion; + + int connectionServerNumber; + int fakeBuddyPoints; + }; + + + static const char * getCentralServerAddress (); + static const uint16 getCentralServerPort (); + static const int getClientOverflowLimit (); + static const char * getClientServiceBindInterface (); + static const uint16 getClientServicePortPrivate (); + static const uint16 getClientServicePortPublic (); + static const char * getClusterName (); + static bool getDisableWorldSnapshot (); + static const uint16 getGameServicePort (); + static const int getMaxClients (); + static const uint16 getPingPort (); + static const bool getSpamLimitEnabled (); + static const unsigned int getSpamLimitResetTimeMs (); + static const unsigned int getSpamLimitResetScaleFactor (); + static const unsigned int getSpamLimitBytesPerSec (); + static const unsigned int getSpamLimitPacketsPerSec (); + static const bool getStartPublicServer (); + static const char * getChatServiceBindInterface (); + static const char * getCustomerServiceBindInterface (); + static const char * getGameServiceBindInterface (); + static const bool getCompressClientNetworkTraffic (); + static void install (); + static void remove (); + static const uint getCrashRecoveryTimeout (); + static bool getShouldSleep (); + static const int getClientMaxOutstandingPackets(); + static const int getClientMaxRawPacketSize (); + static const int getClientMaxConnections (); + static const int getClientFragmentSize (); + static const int getClientMaxDataHoldTime (); + static const int getClientHashTableSize (); + static const int getLagReportThreshold (); + + static bool getValidateStationKey(); + static const char * getSessionServers(); + static const int getSessionType(); + static bool getDisableSessionLogout(); + static bool getSessionRecordPlayTime(); + static bool getDisconnectOnInactive(); + static bool getDisconnectFreeTrialOnInactive(); + + static const char * getAdminAccountDataTable (void); + + static int getNumberOfSessionServers(); + static char const * getSessionServer(int index); + static float getTimeBetweenSessionUpdates(); + + static const uint32 getDefaultGameFeatures (); + static const uint32 getDefaultSubscriptionFeatures(); + static uint32 getRequiredSubscriptionBits(); + static uint32 getRequiredGameBits(); + static bool getSetJtlRetailIfBetaIsSet(); + static bool getSetEpisode3RetailIfBetaIsSet(); + static bool getSetTrialsOfObiwanRetailIfBetaIsSet(); + + static int getDisabledFeatureBits(); + + static bool getValidateClientVersion(); + + static int getConnectionServerNumber(); + static int getFakeBuddyPoints(); + +private: + static Data * data; +}; + +//----------------------------------------------------------------------- + +inline bool ConfigConnectionServer::getShouldSleep() +{ + return data->shouldSleep; +} + +//----------------------------------------------------------------------- + +inline const bool ConfigConnectionServer::getCompressClientNetworkTraffic() +{ + return data->compressClientNetworkTraffic; +} + +//----------------------------------------------------------------------- + +inline const char * ConfigConnectionServer::getCentralServerAddress () +{ + return data->centralServerAddress; +} + +//----------------------------------------------------------------------- + +inline const uint16 ConfigConnectionServer::getCentralServerPort () +{ + return static_cast(data->centralServerPort); +} + +//----------------------------------------------------------------------- + +inline const char* ConfigConnectionServer::getClientServiceBindInterface() +{ + return data->clientServiceBindInterface; +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getClientOverflowLimit() +{ + return data->clientOverflowLimit; +} + +//----------------------------------------------------------------------- + +inline const uint16 ConfigConnectionServer::getClientServicePortPrivate() +{ + return static_cast(data->clientServicePortPrivate); +} + +//----------------------------------------------------------------------- + +inline const uint16 ConfigConnectionServer::getClientServicePortPublic() +{ + return static_cast(data->clientServicePortPublic); +} + +//----------------------------------------------------------------------- + +inline const char * ConfigConnectionServer::getClusterName() +{ + return data->clusterName; +} + +//----------------------------------------------------------------------- + +inline bool ConfigConnectionServer::getDisableWorldSnapshot() +{ + return data->disableWorldSnapshot; +} + +//----------------------------------------------------------------------- + +inline const uint16 ConfigConnectionServer::getGameServicePort() +{ + return static_cast(data->gameServicePort); +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getMaxClients() +{ + return data->maxClients; +} + +//----------------------------------------------------------------------- + +inline const uint16 ConfigConnectionServer::getPingPort () +{ + return static_cast(data->pingPort); +} + +//----------------------------------------------------------------------- + +inline const bool ConfigConnectionServer::getSpamLimitEnabled () +{ + return data->spamLimitEnabled; +} + +//----------------------------------------------------------------------- + +inline const unsigned int ConfigConnectionServer::getSpamLimitResetTimeMs () +{ + return static_cast(data->spamLimitResetTimeMs); +} + +//----------------------------------------------------------------------- + +inline const unsigned int ConfigConnectionServer::getSpamLimitResetScaleFactor () +{ + return static_cast(data->spamLimitResetScaleFactor); +} + +//----------------------------------------------------------------------- + +inline const unsigned int ConfigConnectionServer::getSpamLimitBytesPerSec () +{ + return static_cast(data->spamLimitBytesPerSec); +} + +//----------------------------------------------------------------------- + +inline const unsigned int ConfigConnectionServer::getSpamLimitPacketsPerSec () +{ + return static_cast(data->spamLimitPacketsPerSec); +} + +//----------------------------------------------------------------------- + +inline const bool ConfigConnectionServer::getStartPublicServer() +{ + return data->startPublicServer; +} + +//----------------------------------------------------------------------- + +inline const char * ConfigConnectionServer::getChatServiceBindInterface() +{ + return data->chatServiceBindInterface; +} + +//----------------------------------------------------------------------- + +inline const char * ConfigConnectionServer::getCustomerServiceBindInterface() +{ + return data->customerServiceBindInterface; +} + +//----------------------------------------------------------------------- + +inline const char * ConfigConnectionServer::getGameServiceBindInterface() +{ + return data->gameServiceBindInterface; +} + +// ---------------------------------------------------------------------- + +inline const uint ConfigConnectionServer::getCrashRecoveryTimeout() +{ + return static_cast(data->crashRecoveryTimeout); +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getClientMaxOutstandingPackets() +{ + return data->clientMaxOutstandingPackets; +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getClientMaxRawPacketSize() +{ + return data->clientMaxRawPacketSize; +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getClientMaxConnections() +{ + return data->clientMaxConnections; +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getClientFragmentSize() +{ + return data->clientFragmentSize; +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getClientMaxDataHoldTime() +{ + return data->clientMaxDataHoldTime; +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getClientHashTableSize() +{ + return data->clientHashTableSize; +} + +//----------------------------------------------------------------------- + +inline const int ConfigConnectionServer::getLagReportThreshold() +{ + return data->lagReportThreshold; +} + +// ---------------------------------------------------------------------- + +//------------------------------------------------------------------------------------------ + +inline bool ConfigConnectionServer::getValidateStationKey() +{ + return data->validateStationKey; +} + +//------------------------------------------------------------------------------------------ + +inline const char * ConfigConnectionServer::getSessionServers() +{ + return data->sessionServers; +} + +//------------------------------------------------------------------------------------------ + +inline const int ConfigConnectionServer::getSessionType() +{ + return data->sessionType; +} + +//------------------------------------------------------------------------------------------ + +inline bool ConfigConnectionServer::getDisableSessionLogout() +{ + return data->disableSessionLogout; +} + +//------------------------------------------------------------------------------------------ + +inline bool ConfigConnectionServer::getSessionRecordPlayTime() +{ + return data->sessionRecordPlayTime; +} + +// ---------------------------------------------------------------------- + +inline const char * ConfigConnectionServer::getAdminAccountDataTable(void) +{ + return data->adminAccountDataTable; +} + +//----------------------------------------------------------------------- + +inline float ConfigConnectionServer::getTimeBetweenSessionUpdates() +{ + return data->timeBetweenSessionUpdates; +} + +//----------------------------------------------------------------------- + +inline uint32 ConfigConnectionServer::getRequiredSubscriptionBits() +{ + return static_cast(data->requiredSubscriptionBits); +} +//----------------------------------------------------------------------- + + +inline uint32 ConfigConnectionServer::getRequiredGameBits() +{ + return static_cast(data->requiredGameBits); +} + +//----------------------------------------------------------------------- + + +inline const uint32 ConfigConnectionServer::getDefaultGameFeatures() +{ + return static_cast(data->defaultGameFeatures); +} + +// ---------------------------------------------------------------------- + +inline const uint32 ConfigConnectionServer::getDefaultSubscriptionFeatures() +{ + return static_cast(data->defaultSubscriptionFeatures); +} + +//------------------------------------------------------------------------------------------ + +inline bool ConfigConnectionServer::getSetJtlRetailIfBetaIsSet() +{ + return data->setJtlRetailIfBetaIsSet; +} + +//------------------------------------------------------------------------------------------ + +inline bool ConfigConnectionServer::getSetEpisode3RetailIfBetaIsSet() +{ + return data->setEpisode3RetailIfBetaIsSet; +} + +//------------------------------------------------------------------------------------------ + +inline bool ConfigConnectionServer::getSetTrialsOfObiwanRetailIfBetaIsSet() +{ + return data->setTrialsOfObiwanRetailIfBetaIsSet; +} + +//------------------------------------------------------------------------------------------ + +inline bool ConfigConnectionServer::getValidateClientVersion() +{ + return data->validateClientVersion; +} + +// ---------------------------------------------------------------------- + +inline int ConfigConnectionServer::getConnectionServerNumber() +{ + return data->connectionServerNumber; +} + +#endif // _ConfigConnectionServer_H diff --git a/engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp b/engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp new file mode 100644 index 00000000..70ef1768 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ConnectionServer.cpp @@ -0,0 +1,1586 @@ + +// ConnectionServer.cpp +// copyright 2001 Verant Interactive + + +//----------------------------------------------------------------------- + +#include "FirstConnectionServer.h" +#include "ConnectionServer.h" + +#include "Archive/ByteStream.h" +#include "CentralConnection.h" +#include "ChatServerConnection.h" +#include "ClientConnection.h" +#include "ConfigConnectionServer.h" +#include "ConnectionServerMetricsData.h" +#include "CustomerServiceConnection.h" +#include "GameConnection.h" +#include "PseudoClientConnection.h" +#include "SessionApiClient.h" +#include "UdpLibrary.h" +#include "UnicodeUtils.h" +#include "serverKeyShare/KeyShare.h" +#include "serverMetrics/MetricsManager.h" +#include "serverNetworkMessages/CentralConnectionServerMessages.h" +#include "serverNetworkMessages/CharacterListMessage.h" +#include "serverNetworkMessages/ExcommunicateGameServerMessage.h" +#include "serverNetworkMessages/GameConnectionServerMessages.h" +#include "serverNetworkMessages/GameServerForLoginMessage.h" +#include "serverNetworkMessages/LoginKeyPush.h" +#include "serverNetworkMessages/NewClient.h" +#include "serverNetworkMessages/ProfilerOperationMessage.h" +#include "serverNetworkMessages/RandomName.h" +#include "serverNetworkMessages/SetConnectionServerPublic.h" +#include "serverNetworkMessages/UpdateConnectionServerStatus.h" +#include "serverNetworkMessages/UpdatePlayerCountMessage.h" +#include "serverNetworkMessages/ValidateAccountReplyMessage.h" +#include "serverNetworkMessages/ValidateCharacterForLoginReplyMessage.h" +#include "serverNetworkMessages/VerifyAndLockName.h" +#include "serverUtility/AdminAccountManager.h" +#include "sharedDebug/Profiler.h" +#include "sharedFoundation/Clock.h" +#include "sharedFoundation/ConfigFile.h" +#include "sharedFoundation/Os.h" +#include "sharedFoundation/Timer.h" +#include "sharedGame/PlatformFeatureBits.h" +#include "sharedLog/Log.h" +#include "sharedLog/LogManager.h" +#include "sharedLog/SetupSharedLog.h" +#include "sharedMemoryManager/MemoryManager.h" +#include "sharedNetwork/NetworkSetupData.h" +#include "sharedNetwork/Service.h" +#include "sharedNetwork/UdpSock.h" +#include "sharedNetworkMessages/ClientCentralMessages.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" +#include "sharedUtility/DataTableManager.h" +#include + +// ====================================================================== +namespace ConnectionServerNamespace +{ + ConnectionServer * s_connectionServer = 0; + NetworkSetupData * s_clientServiceSetup = 0; + + const std::string SCENE_NAME_TUTORIAL = "tutorial"; + const std::string SCENE_NAME_FALCON_PREFIX = "space_npe_falcon"; +}; + +using namespace ConnectionServerNamespace; + +// ====================================================================== + +ConnectionServer & ConnectionServer::instance() +{ + return *s_connectionServer; +} + +//----------------------------------------------------------------------- + +ConnectionServer::ConnectionServer() : +MessageDispatch::Receiver(), +chatService(0), +customerService(0), +clientServicePrivate(0), +clientServicePublic(0), +gameService(0), +loginServerKeys(0), +done(false), +m_id(0), +m_metricsData(0), +centralConnection(0), +chatServers(), +customerServiceServers(), +clientMap(), +connectedMap(), +gameServerMap(), +freeTrials(), +networkBarrier(0), +pingSocket (new UdpSock), +m_recoverTime(0), +m_sessionApiClient(0), +m_pingTrafficNumBytes(0), +m_recoveringClientList() +{ + if(s_clientServiceSetup == 0) + s_clientServiceSetup = new NetworkSetupData; + + s_clientServiceSetup->maxOutstandingPackets = ConfigConnectionServer::getClientMaxOutstandingPackets(); + s_clientServiceSetup->maxRawPacketSize = ConfigConnectionServer::getClientMaxRawPacketSize(); + s_clientServiceSetup->maxConnections = ConfigConnectionServer::getClientMaxConnections(); + s_clientServiceSetup->fragmentSize = ConfigConnectionServer::getClientFragmentSize(); + s_clientServiceSetup->maxDataHoldTime = ConfigConnectionServer::getClientMaxDataHoldTime(); + s_clientServiceSetup->hashTableSize=ConfigConnectionServer::getClientHashTableSize(); + s_clientServiceSetup->port = ConfigConnectionServer::getClientServicePortPublic(); + s_clientServiceSetup->compress = ConfigConnectionServer::getCompressClientNetworkTraffic(); + s_clientServiceSetup->useTcp = false; + + loginServerKeys = new KeyServer(20); + + Address a("", ConfigConnectionServer::getPingPort()); + IGNORE_RETURN(pingSocket->bind (a)); + + if (ConfigConnectionServer::getValidateStationKey()) + { + installSessionValidation(); + } + +} + +//----------------------------------------------------------------------- + +ConnectionServer::~ConnectionServer() +{ + delete pingSocket; + pingSocket = 0; + + delete loginServerKeys; + loginServerKeys = 0; + + centralConnection=0; + + chatServers.clear(); + + customerServiceServers.clear(); + + connectedMap.clear(); + + clientServicePublic = 0; + clientServicePrivate = 0; + chatService = 0; + customerService = 0; + gameService = 0; + gameServerMap.clear(); + freeTrials.clear(); + delete s_clientServiceSetup; +} + +//----------------------------------------------------------------------- + +const CustomerServiceConnection * ConnectionServer::getCustomerServiceConnection () +{ + if(! instance().customerServiceServers.empty()) + { + return (*(instance().customerServiceServers.begin())); + } + return NULL; +} + +//----------------------------------------------------------------------- + +//TODO: This assumes that all characters that aren't on a game server +// are waiting for this gameserver. This assumption is bad: they could +// be waiting on a different gameserver, or they could be in the process +// of being validated + +void ConnectionServer::addGameConnection(unsigned long gameServerId, GameConnection* gc) +{ + static ConnectionServer & cs = instance(); + + cs.gameServerMap[gameServerId] = gc;//@todo check for dupe + // find characters pending for THIS gameserver + SuidMap::iterator i; + for(i = cs.connectedMap.begin(); i != cs.connectedMap.end(); ++i) + { + ClientConnection * c = (*i).second; + if(!c->getHasBeenSentToGameServer()) + IGNORE_RETURN(c->sendToGameServer()); + } +} + +//----------------------------------------------------------------------- + +bool ConnectionServer::decryptToken(const KeyShare::Token & token, uint32 & stationUserId, bool & secure, std::string & accountName) +{ + static ConnectionServer & cs = instance(); + + //Also the sizeof(int) is likewise magic from the session api + uint32 len = sizeof(uint32) + sizeof(bool) + MAX_ACCOUNT_NAME_LENGTH + 1; + unsigned char * keyBuffer = new unsigned char[len]; + unsigned char * keyBufferPointer = keyBuffer; + NOT_NULL(keyBuffer); + memset(keyBuffer, 0, len); + + + bool retval = cs.loginServerKeys->decipherToken(token, keyBuffer, len); + + if (! retval) + return retval; + + char *tmpBuffer = new char[MAX_ACCOUNT_NAME_LENGTH + 1]; + memset(tmpBuffer, 0, MAX_ACCOUNT_NAME_LENGTH + 1); + + memcpy(&stationUserId, keyBufferPointer, sizeof(uint32)); + keyBufferPointer += sizeof(uint32); + memcpy(&secure, keyBufferPointer, sizeof(bool)); + keyBufferPointer += sizeof(bool); + memcpy(tmpBuffer, keyBufferPointer, MAX_ACCOUNT_NAME_LENGTH); + accountName = tmpBuffer; + delete[] tmpBuffer; + delete [] keyBuffer; + return retval; +} + +bool ConnectionServer::decryptToken(const KeyShare::Token & token, char* sessionKey, StationId & stationId) +{ + static ConnectionServer & cs = instance(); + + uint32 len = apiSessionIdWidth + sizeof(StationId); + unsigned char * keyBuffer = new unsigned char[len + 1]; + unsigned char * keyBufferPointer = keyBuffer; + NOT_NULL(keyBuffer); + memset(keyBuffer, 0, len); + + + bool retval = cs.loginServerKeys->decipherToken(token, keyBuffer, len); + + if (! retval) + return retval; + + memcpy(sessionKey, keyBufferPointer, apiSessionIdWidth); + keyBufferPointer += apiSessionIdWidth; + memcpy(&stationId, keyBufferPointer, sizeof(StationId)); + delete [] keyBuffer; + return retval; +} + + +//----------------------------------------------------------------------- + +const Service * ConnectionServer::getChatService() +{ + static ConnectionServer & cs = instance(); + return cs.chatService; +} + +//----------------------------------------------------------------------- + +const Service * ConnectionServer::getCustomerService() +{ + static ConnectionServer & cs = instance(); + return cs.customerService; +} + +//----------------------------------------------------------------------- + +Service * ConnectionServer::getClientServicePrivate() +{ + static ConnectionServer & cs = instance(); + return cs.clientServicePrivate; +} + +//----------------------------------------------------------------------- + +Service * ConnectionServer::getClientServicePublic() +{ + static ConnectionServer & cs = instance(); + return cs.clientServicePublic; +} + +//----------------------------------------------------------------------- + +KeyShare::Token ConnectionServer::makeToken(const unsigned char * newData, const uint32 dataLen) +{ + static ConnectionServer & cs = instance(); + return cs.loginServerKeys->makeToken(newData, dataLen); +} + +//----------------------------------------------------------------------- + +void ConnectionServer::pushKey(const KeyShare::Key & newKey) +{ + static ConnectionServer & cs = instance(); + cs.loginServerKeys->pushKey(newKey); +} + +//----------------------------------------------------------------------- + +void ConnectionServer::addNewClient(ClientConnection* cconn, const NetworkId &oid, GameConnection* gconn, const std::string &, bool sendToStarport) +{ + static ConnectionServer & cs = instance(); + ClientMap::iterator i = cs.clientMap.find(oid); + if(i != cs.clientMap.end()) + { + if(cconn->getClient()) + { + WARNING_STRICT_FATAL(true, ("Client already connected, attempting to drop old one\n")); + dropClient(cconn, "Duplicate Login"); + return; + } + else + { + // stale connection in map + cs.removeFromClientMap(oid); + } + } + + // Create a new entry in the client map + cs.addToClientMap(oid, cconn); + + // Get the client that was just created + Client * newClient = cs.getClient(oid); + NOT_NULL(newClient); + + // select a chat server connection for the client + // if non exists, then the client will be notified when + // one starts + if(! cs.chatServers.empty()) + { + //ChatServerConnection * c = (*chatServers.begin()); + // find chat server with least load + size_t max = 0xFFFFFFFF; + ChatServerConnection * candidate = 0; + std::set::const_iterator iter; + for(iter = cs.chatServers.begin(); iter != cs.chatServers.end(); ++iter) + { + if((*iter)->getClients().size() <= max) + { + candidate = (*iter); + max = (*iter)->getClients().size(); + } + } + if(candidate) + newClient->setChatConnection(candidate); + } + + // select a cs server connection for the client + // if non exists, then the client will be notified when + // one starts + if(! cs.customerServiceServers.empty()) + { + //ChatServerConnection * c = (*chatServers.begin()); + // find chat server with least load + size_t max = 0xFFFFFFFF; + CustomerServiceConnection * candidate = 0; + std::set::const_iterator iter; + for(iter = cs.customerServiceServers.begin(); iter != cs.customerServiceServers.end(); ++iter) + { + if((*iter)->getClients().size() <= max) + { + candidate = (*iter); + max = (*iter)->getClients().size(); + } + } + if(candidate) + newClient->setCustomerServiceConnection(candidate); + } + + //send the game server a message about this client. + static const std::string loginTrace("TRACE_LOGIN"); + LOG(loginTrace, ("NewClient(%d, %s, %s)", cconn->getSUID(), oid.getValueString().c_str(), cconn->getAccountName().c_str())); + NewClient m(oid, cconn->getAccountName(), cconn->getRemoteAddress(), cconn->getIsSecure(), false, cconn->getSUID(), NULL, cconn->getGameFeatures(), cconn->getSubscriptionFeatures(), cconn->getEntitlementTotalTime(), cconn->getEntitlementEntitledTime(), cconn->getEntitlementTotalTimeSinceLastLogin(), cconn->getEntitlementEntitledTimeSinceLastLogin(), cconn->getBuddyPoints(), cconn->getConsumedRewardEvents(), cconn->getClaimedRewardItems(), cconn->isUsingAdminLogin(), cconn->getCanSkipTutorial(), sendToStarport ); + gconn->send(m, true); + //@todo move this to ClientConnection.cpp +} + +//----------------------------------------------------------------------- + +void ConnectionServer::dropClient(ClientConnection * conn, const std::string& description) +{ + DEBUG_FATAL(!conn, ("Cannot call dropClient with NULL connection")); + if (!conn) //lint !e774 // boolean within 'if' always evaluates to False //suppresed because this is only relevant in DEBUG builds + return; + + static ConnectionServer & cs = instance(); + //Client dropped. Tell game server if they've logged in. + LOG("ClientDisconnect", ("Dropping client for SUID %d\n", conn->getSUID())); + LOG("CustomerService", ("Login:%s Dropped Reason: %s. Character: %s (%s). Play time: %s. Active play time: %s", ClientConnection::describeAccount(conn).c_str(), description.c_str(), conn->getCharacterName().c_str(), conn->getCharacterId().getValueString().c_str(), conn->getPlayTimeDuration().c_str(), conn->getActivePlayTimeDuration().c_str())); + + Client *client = conn->getClient(); + if (client) + { + DropClient msg(client->getNetworkId()); + GameConnection* gconn = client->getGameConnection(); + //Don't worry about sending a message to a non-existant game server + if (gconn) + gconn->send(msg, true); + else + DEBUG_REPORT_LOG(true, ("Could not find game server to drop this client\n")); + + // Remove the entry from the client map + cs.removeFromClientMap(client->getNetworkId()); + } + else + { + //If they aren't connected to the game yet, they're probably on the pending list. + //@todo ensure we don't need to send a cleanup message to central +// removePendingCharacter(conn->getSUID()); + } + + // Remove entry from the connected map + cs.removeFromConnectedMap(conn->getSUID()); + + conn->disconnect(); +} + +//----------------------------------------------------------------------- +// void ConnectionServer::addPendingCharacter(uint32 suid, ClientConnection* conn) +// { +// if (pendingMap.find(suid) == pendingMap.end()) +// pendingMap[suid] = conn; +// else +// { +// WARNING_STRICT_FATAL(true, ("Attepting to add duplicate pending chatacter")); +// pendingMap[suid] = conn; +// } +// } + +//----------------------------------------------------------------------- + +void ConnectionServer::addConnectedClient(uint32 suid, ClientConnection* conn) +{ + static ConnectionServer & cs = instance(); + cs.addToConnectedMap(suid, conn); +} + +//----------------------------------------------------------------------- + +ClientConnection* ConnectionServer::getClientConnection(const uint32 suid) +{ + static ConnectionServer & cs = instance(); + ClientConnection * result = 0; + SuidMap::const_iterator i = cs.connectedMap.find(suid); + if(i != cs.connectedMap.end()) + { + result = (*i).second; + } + return result; +} + +// ---------------------------------------------------------------------- + +const Service * ConnectionServer::getGameService() +{ + static ConnectionServer & cs = instance(); + return cs.gameService; +} + +// ---------------------------------------------------------------------- + +GameConnection* ConnectionServer::getGameConnection(const std::string &sceneName) +{ + static ConnectionServer & cs = instance(); + GameServerMap::iterator i = cs.gameServerMap.begin(); + for(; i != cs.gameServerMap.end(); ++i) + { + if (sceneName == (*i).second->getSceneName()) + { + return (*i).second; + } + } + return NULL; +} + +// ---------------------------------------------------------------------- +void ConnectionServer::handleConnectionServerIdMessage(const ConnectionServerId & m) +{ + // Connection established with central server. Set everything up. + m_id = m.getId(); + + const Service * const servicePrivate = getClientServicePrivate(); + const Service * const servicePublic = getClientServicePublic(); + FATAL(servicePrivate == NULL && servicePublic == NULL, ("No client service is active!")); + + const Service * const g = getGameService(); + FATAL(g == NULL, ("No game service is active!")); + const Service * const c = getChatService(); + const Service * const cs = getCustomerService(); + const uint16 pingPort = getPingPort (); + + if((servicePrivate != NULL || servicePublic != NULL) && g != NULL) //lint !e774 // always evaluates to false // suppresed because the FATAL macro triggers this lint warning + { + uint16 chatPort = 0; + uint16 csPort = 0; + if(c) + chatPort = c->getBindPort(); + + if (cs) + { + csPort = cs->getBindPort(); + } + + uint16 publicPort = 0; + if(servicePublic) + publicPort = servicePublic->getBindPort(); + uint16 privatePort = 0; + if(servicePrivate) + privatePort = servicePrivate->getBindPort(); + + std::string clientServicePublicBindAddress = NetworkHandler::getHostName(); + + NOT_NULL(gameService); + NOT_NULL(chatService); + NOT_NULL(customerService); + + const NewCentralConnectionServer ncs(gameService->getBindAddress(), clientServicePublicBindAddress, chatService->getBindAddress(), customerService->getBindAddress(), privatePort, publicPort, g->getBindPort(), chatPort, csPort, pingPort, ConfigConnectionServer::getConnectionServerNumber()); + sendToCentralProcess(ncs); + } + else + FATAL(true, ("Error in connection server startup")); +} + +// ---------------------------------------------------------------------- + +void ConnectionServer::receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message) +{ + // it's reasonably safe to cast, message type verified + // determine message type + + if(message.isType("GameConnectionOpened")) + { + DEBUG_REPORT_LOG(true,("Game Connection opened\n")); + } + else if (message.isType("GameConnectionClosed")) + { + //@todo handle case where game server drops and we have users connected to it. + //Drop all connected clients. + const GameConnection & downConnection = static_cast(source); + DEBUG_REPORT_LOG(true, ("Game Server connection went down. Dropping clients.\n")); + //remove Game Conection from list + + PseudoClientConnection::gameConnectionClosed(&downConnection); + + GameServerMap::iterator j = gameServerMap.find(downConnection.getGameServerId()); + if (j != gameServerMap.end()) + { + gameServerMap.erase(j); + } + } + + else if(message.isType("CentralConnectionOpened")) + { + DEBUG_REPORT_LOG(true,("Opened connection with central\n")); + centralConnection = const_cast(static_cast(&source));//lint !e826 // info: Suspiscious pointer-to-pointer conversion (area too small) + if(s_clientServiceSetup == 0) + s_clientServiceSetup = new NetworkSetupData; + + s_clientServiceSetup->useTcp = false; + if(ConfigConnectionServer::getStartPublicServer()) + clientServicePublic = new Service(ConnectionAllocator(), *s_clientServiceSetup); + s_clientServiceSetup->port = ConfigConnectionServer::getClientServicePortPrivate(); + clientServicePrivate = new Service(ConnectionAllocator(), *s_clientServiceSetup); + s_clientServiceSetup->port = ConfigConnectionServer::getClientServicePortPublic(); + + connectToMessage("ClientConnectionOpened"); + connectToMessage("ClientConnectionClosed"); + + } + else if (message.isType("CentralConnectionClosed")) + { + centralConnection = const_cast(static_cast(&source));//lint !e826 // info: Suspiscious pointer-to-pointer conversion (area too small) + setDone("CentralConnectionClosed: %s", centralConnection ? centralConnection->getDisconnectReason().c_str() : ""); + centralConnection = 0; + DEBUG_REPORT_LOG(true, ("CentralDied. So we will too\n")); + //@todo Drop all pending clients. + } + else if(message.isType("ClientConnectionOpened")) + { + DEBUG_REPORT_LOG(true, ("Opened connection with client\n")); + } + else if (message.isType("ClientConnectionClosed")) + { + DEBUG_REPORT_LOG(true, ("Client is Dropping connection\n")); + ClientConnection * cconn = const_cast(static_cast(&source));//lint !e826 // info: Suspiscious pointer-to-pointer conversion (area too small) + + //tell CentralServer + if (centralConnection) + { + GenericValueTypeMessage const msg("ClientConnectionClosed", cconn->getSUID()); + centralConnection->send(msg,true); + } + + //Client dropped. Tell game server if they've logged in. + Client *client = cconn->getClient(); + if (client) + { + DropClient msg(client->getNetworkId()); + GameConnection* gconn = client->getGameConnection(); + //Don't worry about sending a message to a non-existant game server + if (gconn) + gconn->send(msg, true); + + // Remove the entry from the client map + removeFromClientMap(client->getNetworkId()); + } + else + { + //If they aren't connected to the game yet, they're probably on the pending list. +// removePendingCharacter(cconn->getSUID()); + } + removeFromConnectedMap(cconn->getSUID()); + } + else if (message.isType("ConnectionServerId")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ConnectionServerId m(ri); + handleConnectionServerIdMessage(m); + } + + else if (message.isType("ConnectionKeyPush")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ConnectionKeyPush pk(ri); + loginServerKeys->pushKey(pk.getKey()); + } + + else if (message.isType("LoginKeyPush")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const LoginKeyPush k(ri); + loginServerKeys->pushKey(k.getKey()); + } + else if (message.isType("CharacterListMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const CharacterListMessage msg(ri); + WARNING_STRICT_FATAL(true,("CharacterListMessage is deprecated on the ConnectionServer -- fix whoever is sending it.\n")); + } + else if (message.isType("ConnectionCreateCharacterSuccess")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const ConnectionCreateCharacterSuccess msg(ri); + LOG("TraceCharacterCreation", ("Received ConnectionCreateCharacterSuccess for %d", msg.getStationId())); + ClientConnection* const client = getClientConnection(msg.getStationId()); + if (client) + { + const ClientCreateCharacterSuccess m (msg.getNetworkId ()); + client->send(m, true); + } + else + { + LOG("CustomerService", ("CharacterTransfer: Trying to deliver ConnectionCreateCharacterSuccess to PsuedoClientConnection(%d)", msg.getStationId())); + PseudoClientConnection::tryToDeliverMessageTo(static_cast(msg.getStationId()), static_cast(message).getByteStream()); + } + } + else if (message.isType("ConnectionCreateCharacterFailed")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const ConnectionCreateCharacterFailed msg(ri); + ClientConnection* client = getClientConnection(msg.getStationId()); + if (client) + { + client->setHasRequestedCharacterCreate(false); + + ClientCreateCharacterFailed m(msg.getName(), msg.getErrorMessage()); //lint !e1013 !e1055 !e746 (Symbol 'getErrorMessage' not a member of class 'const ConnectionCreateCharacterFailed') // supressed because it IS a member of that class. //lint !e1055 //lint !e746 + client->send(m, true); + } + else + { + PseudoClientConnection::tryToDeliverMessageTo(static_cast(msg.getStationId()), static_cast(message).getByteStream()); + } + } + else if (message.isType("NewCharacterCreated")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage const msg(ri); + ClientConnection* client = getClientConnection(msg.getValue()); + if (client) + { + // don't allow this client to request another character create; + // this will forced the client to disconnect and reconnect at which time + // a check will be done (taking the newly created character into account) + // to see if the client is allowed to create another character on this account + client->setHasCreatedCharacter(true); + } + } + else if (message.isType("RandomNameResponse")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + RandomNameResponse connMsg(ri); + + ClientConnection* cconn = getClientConnection(connMsg.getStationId()); + ClientRandomNameResponse cnr(connMsg.getCreatureTemplate(), connMsg.getName(), connMsg.getErrorMessage());//lint !e1013 (Symbol 'getErrorMessage' not a member of class 'RandomNameResponse') // supressed because it IS a member of that class. + if (cconn) + cconn->send(cnr, true); + } + else if (message.isType("VerifyAndLockNameResponse")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + VerifyAndLockNameResponse connMsg(ri); + + ClientConnection* cconn = getClientConnection(connMsg.getStationId()); + ClientVerifyAndLockNameResponse cvalnr(connMsg.getCharacterName(), connMsg.getErrorMessage()); + if (cconn) + cconn->send(cvalnr, true); + } + else if (message.isType("ChatServerConnectionOpened")) + { + ChatServerConnection * c = const_cast(static_cast(&source));//lint !e826 suspiscious pointer-to-pointer conversion // suppressed, you bet it is + IGNORE_RETURN(chatServers.insert(c)); + } + else if (message.isType("ChatServerConnectionClosed")) + { + ChatServerConnection * c = const_cast(static_cast(&source));//lint !e826 suspiscious pointer-to-pointer conversion // suppressed, you bet it is + std::set::iterator f = chatServers.find(c); + if(f != chatServers.end()) + { + // migrate players that were on this chat server + // to another chat server + + // now remove the server from the set + chatServers.erase(f); + if(!chatServers.empty()) + { + std::set::iterator ic = chatServers.begin(); + + const std::set & clients = c->getClients(); + std::set::const_iterator i; + for(i = clients.begin(); i != clients.end(); ++ i) + { + ChatServerConnection * newConn = (*ic); + Client * cl = (*i); + cl->setChatConnection(newConn); + ++ic; + if(ic == chatServers.end()) + ic = chatServers.begin(); + } + } + else + { + const std::set & clients = c->getClients(); + std::set::const_iterator i; + for(i = clients.begin(); i != clients.end(); ++ i) + { + (*i)->setChatConnection(NULL); + } + } + } + } + else if (message.isType("CustomerServiceConnectionOpened")) + { + CustomerServiceConnection * c = const_cast(static_cast(&source));//lint !e826 suspiscious pointer-to-pointer conversion // suppressed, you bet it is + IGNORE_RETURN(customerServiceServers.insert(c)); + } + else if (message.isType("CustomerServiceConnectionClosed")) + { + CustomerServiceConnection * c = const_cast(static_cast(&source));//lint !e826 suspiscious pointer-to-pointer conversion // suppressed, you bet it is + std::set::iterator f = customerServiceServers.find(c); + if(f != customerServiceServers.end()) + { + // migrate players that were on this chat server + // to another chat server + + // now remove the server from the set + customerServiceServers.erase(f); + if(!customerServiceServers.empty()) + { + std::set::iterator ic = customerServiceServers.begin(); + + const std::set & clients = c->getClients(); + std::set::const_iterator i; + for(i = clients.begin(); i != clients.end(); ++ i) + { + CustomerServiceConnection * newConn = (*ic); + Client * cl = (*i); + cl->setCustomerServiceConnection(newConn); + ++ic; + if(ic == customerServiceServers.end()) + ic = customerServiceServers.begin(); + } + } + else + { + const std::set & clients = c->getClients(); + std::set::const_iterator i; + for(i = clients.begin(); i != clients.end(); ++ i) + { + (*i)->setCustomerServiceConnection(NULL); + } + } + + } + } + else if (message.isType("GameServerForLoginMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const GameServerForLoginMessage msg(ri); + + ClientConnection* client = getClientConnection(msg.getStationId()); + + // see if the client is for the same character as the login message. + // this is required to prevent admin login via the CS Tool from + // disconnecting other characters logged in from the same account + // (but not the same character) as the character we're administratively + // logging in. + bool clientIsForSameCharacterId = false; + if(client) + { + clientIsForSameCharacterId = client->getCharacterId() == msg.getCharacterId(); + } + + bool handledByPseudoClient = false; + // if the character id in the message doesn't match the character id for + // the existing client, it may be for a pseudoclient. Check, and if so, + // handle there. + if (!clientIsForSameCharacterId) + { + PseudoClientConnection * pcc = PseudoClientConnection::getPseudoClientConnection(msg.getCharacterId()); + // hand off to the PCC only if we do have a pseudoclient, and either we don't have a client, or + // the pseudoclient has a tool id, telling us that it's for cs tool login. + if(pcc && ((!client) || (pcc->getTransferCharacterData().getCSToolId() > 0))) + { + handledByPseudoClient = true; + bool result; + result = PseudoClientConnection::tryToDeliverMessageTo(msg.getStationId(), static_cast(message).getByteStream()); + UNREF(result); + DEBUG_REPORT_LOG(! result,("Received GameServerForLoginMessage for %lu, who was not connected.\n",msg.getStationId())); + } + } + + if (client && (!handledByPseudoClient)) + { + static const std::string loginTrace("TRACE_LOGIN"); + LOG(loginTrace, ("GameServerForLoginMessage(%d, %s)", client->getSUID(), client->getCharacterId().getValueString().c_str())); + client->handleGameServerForLoginMessage(msg.getServer()); + } + } + else if (message.isType("ValidateCharacterForLoginReplyMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const ValidateCharacterForLoginReplyMessage msg(ri); + + ClientConnection* cconn = getClientConnection(msg.getSuid()); + if (!cconn) + DEBUG_REPORT_LOG(true, ("Received ValidateCharacterForLoginReplyMessage for account %lu, which is no longer connected.\n",msg.getSuid())); + else + { + cconn->onCharacterValidated(msg.getApproved(),msg.getCharacterId(), Unicode::wideToNarrow(msg.getCharacterName()), msg.getContainerId(), msg.getScene(), msg.getCoordinates()); + } + } + else if (message.isType("ValidateAccountReplyMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + const ValidateAccountReplyMessage msg(ri); + + ClientConnection* cconn = getClientConnection(msg.getStationId()); + if (!cconn) + DEBUG_REPORT_LOG(true, ("Received ValidateAccountReplyMessage for account %lu, which is no longer connected.\n",msg.getStationId())); + else + { + cconn->onIdValidated(msg.getCanLogin(),msg.getCanCreateRegular(),msg.getCanCreateJedi(),msg.getCanSkipTutorial(),msg.getConsumedRewardEvents(),msg.getClaimedRewardItems()); + } + } + else if(message.isType("SetConnectionServerPublic")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + SetConnectionServerPublic p(ri);//lint !e40 !e522 !e10 // Undeclared identifier 'SetConnectionServerPublic' // suppressed because it IS declared + bool statusChanged = false; + DEBUG_REPORT_LOG(true, ("Conn Server: attempting to chang status\n")); + if(p.getIsPublic())//lint !e40 !e1013 !e10 // Undeclared identifier 'p' // suppressed because it IS declared + { + if(! clientServicePublic) + { + statusChanged = true; + } + } + else + { + delete clientServicePublic; + clientServicePublic = 0; + statusChanged = true; + } + + if(statusChanged && centralConnection) + { + const Service * publicService = getClientServicePublic(); + const Service * privateService = getClientServicePrivate(); + if(publicService || privateService) + { + uint16 publicServicePort = 0; + uint16 privateServicePort = 0; + if(publicService) + { + publicServicePort = publicService->getBindPort(); + } + if(privateService) + { + privateServicePort = privateService->getBindPort(); + } + const UpdateConnectionServerStatus ucs(publicServicePort, privateServicePort); + centralConnection->send(ucs, true); + } + } + }//lint !e529 // Symbol 'ri' not subsequently referenced // suppressed because it IS referenced. I think lint is very confused for some reason. + else if(message.isType("ProfilerOperationMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ProfilerOperationMessage msg(ri); + unsigned int processId = msg.getProcessId(); + if (!processId) + Profiler::handleOperation(msg.getOperation().c_str()); + } + else if (message.isType("ExcommunicateGameServerMessage")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + ri = static_cast(message).getByteStream().begin(); + ExcommunicateGameServerMessage msg(ri); + + LOG("GameGameConnect",("Told to drop connection to %lu by Central",msg.getServerId())); + + GameConnection *conn =getGameConnection(msg.getServerId()); + if (conn) + conn->disconnect(); + } + else if (message.isType("CntrlSrvDropDupeConns")) + { + Archive::ReadIterator ri = static_cast(message).getByteStream().begin(); + GenericValueTypeMessage > const msg(ri); + + ClientConnection* client = getClientConnection(msg.getValue().first); + if (client && !client->isUsingAdminLogin() && !client->getIsSecure()) + { + std::string s = "New Connection on galaxy "; + s += msg.getValue().second; + + dropClient(client, s); + } + } +} + +//----------------------------------------------------------------------- + +void ConnectionServer::install() +{ + s_connectionServer = new ConnectionServer; + + char tmp[128] = {"\0"}; + IGNORE_RETURN(snprintf(tmp, sizeof(tmp), "ConnectionServer:%d", Os::getProcessId())); + SetupSharedLog::install(tmp); + s_connectionServer->setupConnections(); + s_connectionServer->m_metricsData = new ConnectionServerMetricsData; + MetricsManager::install(s_connectionServer->m_metricsData, true, "ConnectionServer" , "", ConfigConnectionServer::getConnectionServerNumber()); + DataTableManager::install(); + AdminAccountManager::install(ConfigConnectionServer::getAdminAccountDataTable()); +} + +//----------------------------------------------------------------------- + +void ConnectionServer::remove() +{ + MetricsManager::remove(); + delete s_connectionServer->m_metricsData; + s_connectionServer->m_metricsData = 0; + + // explicitly delete all connections that were setup from setupConnections rather than letting + // Connection::remove do it. There are connections that require a valid s_connectionServer which is deleted + // and set to NULL. + s_connectionServer->unsetupConnections(); + + SetupSharedLog::remove(); + + delete s_connectionServer; + s_connectionServer = 0; +} + +//----------------------------------------------------------------------- + + +void ConnectionServer::run(void) +{ + static const bool shouldSleep = ConfigConnectionServer::getShouldSleep(); + static ConnectionServer & cserver = instance(); + DEBUG_FATAL (!cserver.m_metricsData, ("Connection server not installed properly")); + + unsigned long startTime = Clock::timeMs(); + Clock::setFrameRateLimit(50.0f); + + LOG("ServerStartup",("ConnectionServer starting on %s", NetworkHandler::getHostName().c_str())); + while (!cserver.done) + { + PROFILER_AUTO_BLOCK_DEFINE("main loop"); + + bool barrierReached = true; + + do + { + { + PROFILER_AUTO_BLOCK_DEFINE("Os::update"); + if (!Os::update()) + cserver.setDone("Os condition (Parent pid change)"); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::update1"); + NetworkHandler::update(); + } + + { + GenericValueTypeMessage > const syncStampMessage("SetSyncStamp", std::make_pair(static_cast(UdpMisc::LocalSyncStampShort()), static_cast(UdpMisc::LocalSyncStampLong()))); + GameServerMap::iterator end = cserver.gameServerMap.end(); + for(GameServerMap::iterator iter = cserver.gameServerMap.begin(); iter != end; ++iter) + iter->second->send(syncStampMessage, true); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::dispatch"); + NetworkHandler::dispatch(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("update"); + cserver.update(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("NetworkHandler::update2"); + NetworkHandler::update(); + } + + { + PROFILER_AUTO_BLOCK_DEFINE("MetricsManager::update"); + unsigned long curTime = Clock::timeMs(); + MetricsManager::update(static_cast(curTime - startTime)); + startTime = curTime; + } + + + if (shouldSleep) + { + PROFILER_AUTO_BLOCK_DEFINE("Os::sleep"); + Os::sleep(1); + } + + } while (!barrierReached && !cserver.done); + + NetworkHandler::clearBytesThisFrame(); + + cserver.updateRecoveringClientList(static_cast(Clock::frameTime()*1000.0f)); + } +} + +// ---------------------------------------------------------------------- +/** + * Invoked every frame to do whatever updates are needed + */ +void ConnectionServer::update() +{ + if (centralConnection) + { + static Timer t(5.0f); + if(t.updateZero(Clock::frameTime())) + { + // Update the population on the central server + updatePopulationOnCentralServer(); + + if (getNumberOfClients() >= ConfigConnectionServer::getMaxClients() - 1) + { + //@todo need an Alert here + WARNING(true, ("We've reached maximum client capacity on a connection server with %d clients", getNumberOfClients())); + } + } + } + + if (m_sessionApiClient) + { + m_sessionApiClient->update(); + } + + static const int ping_throttle_max = 1024; + + static char buffer [4]; + + for (int throttle = 0; pingSocket->canRecv () && throttle < ping_throttle_max; ++throttle) + { + Address addr; + const uint32 count = pingSocket->recvFrom (addr, buffer, 4); + m_pingTrafficNumBytes += static_cast(count); + if (m_pingTrafficNumBytes < 0) + m_pingTrafficNumBytes = 0; + IGNORE_RETURN(pingSocket->sendTo (addr, buffer, count)); + } +} + +// ---------------------------------------------------------------------- + +/** + * The recovering client list holds players who are trying to recover from a server + * crash. If another game server takes authority within a few frames, the player + * is OK and we don't do anything. If no game server takes authority for them, + * we drop them and force them to log in again. + */ +void ConnectionServer::updateRecoveringClientList(uint elapsedTime) +{ + if (!m_recoveringClientList.empty()) + { + m_recoverTime += elapsedTime; + + static std::set listPlayersDropped; + for (RecoveringClientListType::iterator i=m_recoveringClientList.begin(); i!=m_recoveringClientList.end();) + { + if (m_recoverTime > i->first) + { + Client *client = getClient(i->second); + if (client) + { + if (client->getGameConnection()) + DEBUG_REPORT_LOG(true,("Player %s recovered from a server crash and will not be dropped.\n",i->second.getValueString().c_str())); + else + { + LOG("Network", ("Dropping player %s because game server crashed and no other server took authority.\n",i->second.getValueString().c_str())); + dropClient(client->getClientConnection(), "Game Server Crash"); + + IGNORE_RETURN(listPlayersDropped.insert(i->second)); + } + } + i=m_recoveringClientList.erase(i); + } + else + ++i; + } + + if (!listPlayersDropped.empty()) + { + GenericValueTypeMessage > const m("PlayerDroppedFromGameServerCrash", listPlayersDropped); + + // let one of the game server know that we dropped the player(s) due to a game server crash + GameConnection * const anyGameConnection = getAnyGameConnection(); + if (anyGameConnection) + anyGameConnection->send(m, true); + + // let CentralServer know that we dropped the player(s) due to a game server crash + sendToCentralProcess(m); + + listPlayersDropped.clear(); + } + + if (m_recoveringClientList.empty()) + m_recoverTime = 0; // prevent rollover and other problems I don't want to worry about + } +} + +// ---------------------------------------------------------------------- + +void ConnectionServer::addRecoveringClient(const NetworkId& networkId) +{ + instance().m_recoveringClientList.push_back(std::pair(static_cast(instance().m_recoverTime) + ConfigConnectionServer::getCrashRecoveryTimeout(), networkId));//lint !e737 !e713 !e1703 !e1025 // going nuts over unsigned/signed conversion and type mismatch on template +} + +// ---------------------------------------------------------------------- + +void ConnectionServer::unsetupConnections() +{ + // remove all ClientConnection objects so when Connection::remove() is called we don't crash since + // ConnectionServer no longer exists + while( !instance().connectedMap.empty() ) + { + SuidMap::iterator i = instance().connectedMap.begin(); + if( i != instance().connectedMap.end() ) + { + ClientConnection * c = (*i).second; + uint32 suid = (*i).first; + IGNORE_RETURN(instance().connectedMap.erase(suid)); + if( c ) + { + ConnectionServer::dropClient(c, "ConnectionServer shutting down."); + delete c; + } + } + } + + if( customerService ) + { + delete customerService; + customerService = 0; + } + + if( chatService ) + { + delete chatService; + chatService = 0; + } + + if( centralConnection ) + { + delete centralConnection; + centralConnection = 0; + } + + if( gameService ) + { + delete gameService; + gameService = 0; + } + +} +//----------------------------------------------------------------------- + +void ConnectionServer::setupConnections() +{ + // set up message connections + connectToMessage("ConnectionGameServerConnect"); + connectToMessage("SetConnectionServerPublic"); + + // set up port to listen for clients, central, and game servers + + connectToMessage("CentralConnectionOpened"); + connectToMessage("CentralConnectionClosed"); + + NetworkSetupData setup; + setup.port = ConfigConnectionServer::getGameServicePort(); + setup.bindInterface = ConfigConnectionServer::getGameServiceBindInterface(); + setup.maxConnections = 100; + + gameService = new Service(ConnectionAllocator(), setup); + connectToMessage("GameConnectionOpened"); + connectToMessage("GameConnectionClosed"); + + // connect to central server + centralConnection = new CentralConnection(ConfigConnectionServer::getCentralServerAddress(), ConfigConnectionServer::getCentralServerPort()); + + setup.port = 0; + setup.bindInterface = ConfigConnectionServer::getChatServiceBindInterface(); + chatService = new Service(ConnectionAllocator(), setup); + connectToMessage("ChatServerConnectionOpened"); + connectToMessage("ChatServerConnectionClosed"); + + setup.bindInterface = ConfigConnectionServer::getCustomerServiceBindInterface(); + customerService = new Service(ConnectionAllocator(), setup); + connectToMessage("CustomerServiceConnectionOpened"); + connectToMessage("CustomerServiceConnectionClosed"); + + connectToMessage("ConnectionServerId"); + connectToMessage("ConnectionKeyPush"); + connectToMessage("LoginKeyPush"); + connectToMessage("CharacterListMessage"); + + //Create Characters Messages + connectToMessage("ClientCreateCharacter"); + connectToMessage("ConnectionCreateCharacterSuccess"); + connectToMessage("ConnectionCreateCharacterFailed"); + connectToMessage("NewCharacterCreated"); + connectToMessage("GameServerForLoginMessage"); + + // name query messages + connectToMessage("ClientRandomNameRequest"); // from client + connectToMessage("RandomNameResponse"); // from game server + + connectToMessage("ClientVerifyAndLockNameRequest"); // from client + connectToMessage("VerifyAndLockNameResponse"); // from game server + + connectToMessage("ValidateCharacterForLoginReplyMessage"); + connectToMessage("ValidateAccountReplyMessage"); + + connectToMessage("ProfilerOperationMessage"); + connectToMessage("CentralConnectionClosed"); + connectToMessage("CentralConnectionOpened"); + connectToMessage("ChunkCompleteMessage"); + connectToMessage("FrameEndMessage"); + connectToMessage("GameConnectionClosed"); + connectToMessage("GameGameServerConnect"); + connectToMessage("GameServerReadyMessage"); + connectToMessage("GameServerUniverseLoadedMessage"); + connectToMessage("PersistedPlayerMessage"); + connectToMessage("PreloadListMessage"); + connectToMessage("PreloadRequestCompleteMessage"); + connectToMessage("ProfilerOperationMessage"); + connectToMessage("RequestGameServerForLoginMessage"); + connectToMessage("RequestSceneTransfer"); + connectToMessage("ShutdownMessage"); + connectToMessage("TaskConnectionOpened"); + connectToMessage("UnloadedPlayerMessage"); + connectToMessage("WatcherConnectionClosed"); + connectToMessage("WatcherConnectionOpened"); + connectToMessage("ExcommunicateGameServerMessage"); + connectToMessage("CntrlSrvDropDupeConns"); +} + +//---------------------------------------------------------------------- + +uint16 ConnectionServer::getPingPort () +{ + static ConnectionServer & cs = instance(); + return cs.pingSocket->getBindAddress ().getHostPort (); +} + +//----------------------------------------------------------------------- + +Client* ConnectionServer::getClient(const NetworkId & oid) +{ + static ConnectionServer & cs = instance(); + ClientMap::const_iterator iter = cs.clientMap.find(oid); + if (iter != cs.clientMap.end()) + return (*iter).second; + return 0; +} + +//----------------------------------------------------------------------- + +const ConnectionServer::ClientMap & ConnectionServer::getClientMap() +{ + return instance().clientMap; +} + +//----------------------------------------------------------------------- + +GameConnection* ConnectionServer::getGameConnection(uint32 gameServerId) +{ + static ConnectionServer & cs = instance(); + const GameServerMap::const_iterator i = cs.gameServerMap.find(gameServerId); + if (i != cs.gameServerMap.end()) + return (*i).second; + + return NULL; +} + +//----------------------------------------------------------------------- + +GameConnection* ConnectionServer::getAnyGameConnection() +{ + static ConnectionServer & cs = instance(); + if (!cs.gameServerMap.empty()) + return cs.gameServerMap.begin()->second; + + return NULL; +} + +//----------------------------------------------------------------------- + +int ConnectionServer::getPingTrafficNumBytes() +{ + static ConnectionServer & cs = instance(); + return cs.m_pingTrafficNumBytes; +} + +//----------------------------------------------------------------------- + +int ConnectionServer::getNumberOfClients() +{ + static ConnectionServer & cs = instance(); + return static_cast(cs.connectedMap.size()); +} + +//----------------------------------------------------------------------- + +int ConnectionServer::getNumberOfFreeTrials() +{ + static ConnectionServer & cs = instance(); + return static_cast(cs.freeTrials.size()); +} + +//----------------------------------------------------------------------- + +int ConnectionServer::getNumberOfGameServers() +{ + static ConnectionServer & cs = instance(); + return static_cast(cs.gameServerMap.size()); +} + +//----------------------------------------------------------------------- + +void ConnectionServer::removeConnectedCharacter(uint32 suid) +{ + static ConnectionServer & cs = instance(); + cs.removeFromConnectedMap(suid); +} +//----------------------------------------------------------------------- + +void ConnectionServer::sendToCentralProcess(const GameNetworkMessage & msg) +{ + static ConnectionServer & cs = instance(); + if (cs.centralConnection) + cs.centralConnection->send(msg, true); + else + WARNING(true, ("Connection tried to send a message Central, but there is no Central Connection")); +} + +//----------------------------------------------------------------------- + +CentralConnection * ConnectionServer::getCentralConnection() +{ + static ConnectionServer & cs = instance(); + return cs.centralConnection; +} + +//----------------------------------------------------------------------- + +void ConnectionServer::installSessionValidation() +{ + int i = 0; + std::vector sessionServers; + int const numberOfSessionServers = ConfigConnectionServer::getNumberOfSessionServers(); + for (i = 0; i < numberOfSessionServers; ++i) + { + char const * const p = ConfigConnectionServer::getSessionServer(i); + if (p) + { + REPORT_LOG(true, ("Using session server %s\n", p)); + sessionServers.push_back(p); + } + } + + // if there were none specified, use defaults + FATAL(i == 0, ("No session servers specified for session API")); + m_sessionApiClient = new SessionApiClient(&sessionServers[0], i); +} + +// ---------------------------------------------------------------------- + +void ConnectionServer::addToClientMap(const NetworkId &oid, ClientConnection* cconn) +{ + Client * newClient = new Client(cconn, oid); + NOT_NULL(newClient); + + clientMap[oid] = newClient; + + //associate the ClientConnection object. + cconn->setClient(newClient); +} + +// ---------------------------------------------------------------------- + +void ConnectionServer::removeFromClientMap(const NetworkId &oid) +{ + ClientMap::iterator i = clientMap.find(oid); + if (i != clientMap.end()) + { + clientMap.erase(i); + } +} + +//----------------------------------------------------------------------- + +void ConnectionServer::addToConnectedMap(uint32 suid, ClientConnection* cconn) +{ + SuidMap::iterator i = connectedMap.find(suid); + if (i == connectedMap.end()) + { + connectedMap[suid] = cconn; + } + else + { + WARNING_STRICT_FATAL(true, ("Attepting to add duplicate connected chatacter")); + connectedMap[suid] = cconn; + } + + if ( ((cconn->getSubscriptionFeatures() & ClientSubscriptionFeature::FreeTrial) != 0) + && ((cconn->getSubscriptionFeatures() & ClientSubscriptionFeature::Base) == 0)) + { + freeTrials.insert(suid); + } + + // Update the population on the CentralServer immediately + // since we are trying to avoid people "rushing" the server + updatePopulationOnCentralServer(); +} + +// ---------------------------------------------------------------------- + +void ConnectionServer::removeFromConnectedMap(uint32 suid) +{ + SuidMap::iterator i = connectedMap.find(suid); + if (i != connectedMap.end()) + { + connectedMap.erase(i); + } + + FreeTrialsSet::iterator j = freeTrials.find(suid); + if (j != freeTrials.end()) + { + freeTrials.erase(j); + } + + // We could update the CentralServer population but people + // leaving the server are not as important as people connecting + // and so we will wait for update() to handle things +} + +// ---------------------------------------------------------------------- + +void ConnectionServer::updatePopulationOnCentralServer() +{ + if (centralConnection) + { + // Total number of clients and how many of those are free trials + const int numPlayers = getNumberOfClients(); + const int numFreeTrial = getNumberOfFreeTrials(); + + // We are concerned about too many people piling up at the beginning + // of the tutorial, so count how many players could be a problem + int numPlayersEmptyScene = 0; + int numPlayersTutorialScene = 0; + int numPlayersFalconScene = 0; + + // Walk through the clients and evaluate what scene they are in + SuidMap::const_iterator i; + for(i = connectedMap.begin(); i != connectedMap.end(); ++i) + { + const ClientConnection * const conn = (*i).second; + const Client * const client = conn->getClient(); + + if (client && client->getGameConnection()) + { + const std::string& scene = client->getGameConnection()->getSceneName(); + + if (scene.empty()) + { + numPlayersEmptyScene += 1; + } + else if (scene == SCENE_NAME_TUTORIAL) + { + numPlayersTutorialScene += 1; + } + else if (scene.substr(0, SCENE_NAME_FALCON_PREFIX.length()) == SCENE_NAME_FALCON_PREFIX) + { + numPlayersFalconScene += 1; + } + } + else + { + numPlayersEmptyScene += 1; + } + } + + const UpdatePlayerCountMessage msg(false, numPlayers, numFreeTrial, numPlayersEmptyScene, numPlayersTutorialScene, numPlayersFalconScene); + centralConnection->send(msg,true); + } +} + + +// ---------------------------------------------------------------------- + +SessionApiClient* ConnectionServer::getSessionApiClient() +{ + // this is causing crashes when ConnectionServer is shutdown and something calls this function + // because instance() returns 0. + if( s_connectionServer ) + { + return instance().m_sessionApiClient; + } + else + { + return 0; + } +} + +// ---------------------------------------------------------------------- + +void ConnectionServer::setDone(char const *reasonfmt, ...) +{ + if (!done) + { + char reason[1024]; + va_list ap; + va_start(ap, reasonfmt); + IGNORE_RETURN(_vsnprintf(reason, sizeof(reason), reasonfmt, ap));//lint !e530 Symbol 'ap' not initialized + reason[sizeof(reason)-1] = '\0'; + + LOG( + "ServerShutdown", + ( + "ConnectionServer (pid %d) shutdown, reason: %s", + static_cast(Os::getProcessId()), + reason)); + + REPORT_LOG( + true, + ( + "ConnectionServer (pid %d) shutdown, reason: %s\n", + static_cast(Os::getProcessId()), + reason)); + + done = true; + } +} + +// ====================================================================== + diff --git a/engine/server/application/ConnectionServer/src/shared/ConnectionServer.h b/engine/server/application/ConnectionServer/src/shared/ConnectionServer.h new file mode 100644 index 00000000..4d220497 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ConnectionServer.h @@ -0,0 +1,138 @@ +// ConnectionServer.h +// copyright 2001 Verant Interactive + +#ifndef _ConnectionServer_H +#define _ConnectionServer_H + +//----------------------------------------------------------------------- + +#include +#include +#include + +#include "CentralConnection.h" +#include "Client.h" +#include "GameConnection.h" +#include "Singleton/Singleton.h" +#include "serverKeyShare/KeyServer.h" +#include "serverKeyShare/KeyShare.h" +#include "sharedFoundation/StationId.h" +#include "sharedMessageDispatch/Receiver.h" + +class ClientConnection; +class CharacterListMessageData; +class ChatServerConnection; +class ConnectionServerId; +class CustomerServiceConnection; +class ConnectionServerMetricsData; +class NetworkBarrier; +class SessionApiClient; +class UdpSock; + +//----------------------------------------------------------------------- + +class ConnectionServer : public MessageDispatch::Receiver +{ + public: + static void install(); + static void remove(); + void setDone(char const *reasonfmt, ...); + + ~ConnectionServer (); + + typedef std::hash_map GameServerMap; + typedef std::hash_map ClientMap; + typedef std::hash_map SuidMap; + typedef std::set FreeTrialsSet; + + static void addNewClient(ClientConnection* cconn, const NetworkId &oid, GameConnection* gconn, const std::string &sceneName, bool sendToStarport ); + static void addConnectedClient(uint32 suid, ClientConnection* conn); + static void addGameConnection(unsigned long gameServerId, GameConnection* gc); + static bool decryptToken(const KeyShare::Token & token, uint32 & stationUserId, bool & secure, std::string & accountName); + static bool decryptToken(const KeyShare::Token & token, char* sessionKey, StationId & stationId); + static CentralConnection * getCentralConnection(); + static void dropClient(ClientConnection * conn, const std::string &description); + static const Service * getChatService (); + static const Service * getCustomerService (); + static const CustomerServiceConnection * getCustomerServiceConnection (); + static Client* getClient(const NetworkId & oid); + static const ClientMap & getClientMap(); + static Service * getClientServicePrivate (); + static Service * getClientServicePublic (); + static ClientConnection* getClientConnection(uint32 suid); + static uint16 getPingPort (); + static int getPingTrafficNumBytes(); + static int getNumberOfClients(); + static int getNumberOfFreeTrials(); + static int getNumberOfGameServers(); + static GameConnection* getGameConnection(uint32 gameServerId); + static GameConnection* getGameConnection(const std::string &sceneName); + static GameConnection* getAnyGameConnection(); + static const Service * getGameService (); + static SessionApiClient* getSessionApiClient(); + void handleConnectionServerIdMessage(const ConnectionServerId& msg); + static KeyShare::Token makeToken(const unsigned char * newData, const uint32 dataLen); + static void pushKey(const KeyShare::Key & newKey); + void receiveMessage(const MessageDispatch::Emitter & source, const MessageDispatch::MessageBase & message); + static void removeConnectedCharacter(uint32 suid); + static void run(void); + static void sendToCentralProcess(const GameNetworkMessage& msg); + static void addRecoveringClient(const NetworkId& networkId); + +private: + ConnectionServer (); + ConnectionServer (const ConnectionServer & source); + ConnectionServer & operator=(const ConnectionServer & rhs); + static ConnectionServer & instance(); + + void setupConnections(); + void unsetupConnections(); + void update(); + void updateRecoveringClientList(uint elapsedTime); + void installSessionValidation(); + + void addToClientMap(const NetworkId &oid, ClientConnection* cconn); + void removeFromClientMap(const NetworkId &oid); + + void addToConnectedMap(uint32 suid, ClientConnection* conn); + void removeFromConnectedMap(uint32 suid); + + void updatePopulationOnCentralServer(); + +private: + Service * chatService; + Service * customerService; + Service * clientServicePrivate; + Service * clientServicePublic; + Service * gameService; + KeyServer* loginServerKeys; + + bool done; + int m_id; + ConnectionServerMetricsData* m_metricsData; + + CentralConnection * centralConnection; + std::set chatServers; + std::set customerServiceServers; + ClientMap clientMap; + SuidMap connectedMap; + GameServerMap gameServerMap; + FreeTrialsSet freeTrials; + NetworkBarrier * networkBarrier; + UdpSock * pingSocket; + uint m_recoverTime; + SessionApiClient* m_sessionApiClient; + int m_pingTrafficNumBytes; + + typedef std::vector > RecoveringClientListType; + RecoveringClientListType m_recoveringClientList; +}; + +//----------------------------------------------------------------------- + +#endif //_ConnectionServer_H + + + + + diff --git a/engine/server/application/ConnectionServer/src/shared/ConnectionServerMetricsData.cpp b/engine/server/application/ConnectionServer/src/shared/ConnectionServerMetricsData.cpp new file mode 100644 index 00000000..98693bab --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ConnectionServerMetricsData.cpp @@ -0,0 +1,62 @@ +//ConnectionServerMetricsData.cpp +//Copyright 2002 Sony Online Entertainment + +#include "FirstConnectionServer.h" +#include "ConnectionServerMetricsData.h" + +#include "ConnectionServer.h" +#include "serverNetworkMessages/MetricsDataMessage.h" +#include "sharedNetworkMessages/GameNetworkMessage.h" +#include "ClientConnection.h" +#include "sharedLog/Log.h" + + +//----------------------------------------------------------------------- + +ConnectionServerMetricsData::ConnectionServerMetricsData() : + MetricsData() +{ + MetricsPair p; + + ADD_METRICS_DATA(numUsers, 0, false); + ADD_METRICS_DATA(numGameServers, 0, false); + ADD_METRICS_DATA(pingTrafficNumBytes, 0, false); +} + +//----------------------------------------------------------------------- + +ConnectionServerMetricsData::~ConnectionServerMetricsData() +{ +} + +//----------------------------------------------------------------------- + +void ConnectionServerMetricsData::updateData() +{ + MetricsData::updateData(); + m_data[m_numUsers].m_value = ConnectionServer::getNumberOfClients(); + m_data[m_numGameServers].m_value = ConnectionServer::getNumberOfGameServers(); + m_data[m_pingTrafficNumBytes].m_value = ConnectionServer::getPingTrafficNumBytes(); + +/****************** disabled due to stats failing to update on live ********************* + std::map< std::string, uint32 >& cpmap = ClientConnection::getPacketBytesPerMinStats(); + std::map< std::string, uint32 >::iterator cpiter; + + // Shared packet metrics + for ( cpiter = cpmap.begin(); cpiter != cpmap.end(); ++cpiter ) + { + std::map< std::string, unsigned long>::iterator miter = m_packetDataMap.find( cpiter->first ); + if ( miter == m_packetDataMap.end() ) // If we haven't added this packet metric yet.... + { + MetricsData* p_met = MetricsData::getInstance(); + std::string s_key("ConnectionClientPackets_BytesPerMin."); + s_key+=cpiter->first; + m_packetDataMap[ cpiter->first ] = p_met->addMetric(s_key.c_str(), 0, 0, true, true); // add the ID to the local map + miter = m_packetDataMap.find( cpiter->first ); + } + m_data[ miter->second ].m_value = cpiter->second; + } +***********************************************************************************************/ +} + +//----------------------------------------------------------------------- diff --git a/engine/server/application/ConnectionServer/src/shared/ConnectionServerMetricsData.h b/engine/server/application/ConnectionServer/src/shared/ConnectionServerMetricsData.h new file mode 100644 index 00000000..fe3898a7 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/ConnectionServerMetricsData.h @@ -0,0 +1,39 @@ +//ConnectionServerMetricsData.h +//Copyright 2002 Sony Online Entertainment + + +#ifndef _ConnectionServerMetricsData_H +#define _ConnectionServerMetricsData_H + +//----------------------------------------------------------------------- + +#include "serverMetrics/MetricsData.h" +#include + +//----------------------------------------------------------------------- + +class ConnectionServerMetricsData : public MetricsData +{ +public: + ConnectionServerMetricsData(); + ~ConnectionServerMetricsData(); + + virtual void updateData(); + +private: + unsigned long m_numUsers; + unsigned long m_numGameServers; + unsigned long m_pingTrafficNumBytes; + + std::map< std::string, unsigned long > m_packetDataMap; + +private: + + // Disabled. + ConnectionServerMetricsData(const ConnectionServerMetricsData&); + ConnectionServerMetricsData &operator =(const ConnectionServerMetricsData&); +}; + + +//----------------------------------------------------------------------- +#endif diff --git a/engine/server/application/ConnectionServer/src/shared/CustomerServiceConnection.cpp b/engine/server/application/ConnectionServer/src/shared/CustomerServiceConnection.cpp new file mode 100644 index 00000000..124f7839 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/CustomerServiceConnection.cpp @@ -0,0 +1,104 @@ +// CustomerServiceConnection.cpp +// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +//----------------------------------------------------------------------- + +#include "FirstConnectionServer.h" +#include "CustomerServiceConnection.h" +#include "ClientConnection.h" +#include "serverNetworkMessages/GameConnectionServerMessages.h" + +//----------------------------------------------------------------------- + +CustomerServiceConnection::CustomerServiceConnection(UdpConnectionMT * u, TcpClient * t) : + ServerConnection(u, t), + clients() +{ +} + +//----------------------------------------------------------------------- + +CustomerServiceConnection::~CustomerServiceConnection() +{ +} + +//----------------------------------------------------------------------- + +void CustomerServiceConnection::addClient(Client * newClient) +{ + if (clients.find(newClient) == clients.end()) + IGNORE_RETURN( clients.insert(newClient) ); + else + DEBUG_WARNING(true, ("called CustomerServiceConnection::addClient with a client that already exists in the map.")); +} + +//----------------------------------------------------------------------- + +void CustomerServiceConnection::onConnectionClosed() +{ + ServerConnection::onConnectionClosed(); + static MessageConnectionCallback m("CustomerServiceConnectionClosed"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +const std::set & CustomerServiceConnection::getClients() const +{ + return clients; +} + +//----------------------------------------------------------------------- + +void CustomerServiceConnection::onConnectionOpened() +{ + ServerConnection::onConnectionOpened(); + static MessageConnectionCallback m("CustomerServiceConnectionOpened"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void CustomerServiceConnection::onReceive(const Archive::ByteStream & message) +{ + Archive::ReadIterator ri = message.begin(); + GameNetworkMessage m(ri); + ri = message.begin(); + + if (m.isType("GameClientMessage")) + { + //we're receiving a message to forward to the client. + //it is prefixed with NetworkId and reliable. + const GameClientMessage msg(ri); + Archive::ReadIterator mri(msg.getByteStream()); + GameNetworkMessage gnm(mri); + mri = msg.getByteStream().begin(); + + const std::vector & d = msg.getDistributionList(); + std::vector::const_iterator i; + for(i = d.begin(); i != d.end(); ++i) + { + + Client* client = ConnectionServer::getClient((*i)); + DEBUG_REPORT_LOG(!client, ("Error, could not map %s to a client\n", (*i).getValueString().c_str())); + if (client) + { + client->getClientConnection()->sendByteStream(msg.getByteStream(), msg.getReliable()); + } + } + } +} + +//----------------------------------------------------------------------- + +void CustomerServiceConnection::removeClient(Client * oldClient) +{ + std::set::iterator f = clients.find(oldClient); + if(f != clients.end()) + clients.erase(f); +} + +//----------------------------------------------------------------------- + + diff --git a/engine/server/application/ConnectionServer/src/shared/CustomerServiceConnection.h b/engine/server/application/ConnectionServer/src/shared/CustomerServiceConnection.h new file mode 100644 index 00000000..a5defc4c --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/CustomerServiceConnection.h @@ -0,0 +1,34 @@ +// CustomerServiceConnection.h +// Copyright 2000-02, Sony Online Entertainment Inc., all rights reserved. +// Author: Justin Randall + +#ifndef _INCLUDED_CustomerServiceConnection_H +#define _INCLUDED_CustomerServiceConnection_H + +//----------------------------------------------------------------------- + +#include "serverUtility/ServerConnection.h" + +//----------------------------------------------------------------------- + +class CustomerServiceConnection : public ServerConnection +{ +public: + CustomerServiceConnection(UdpConnectionMT *, TcpClient *); + virtual ~CustomerServiceConnection(); + void addClient(Client *); + void onConnectionClosed (); + void onConnectionOpened (); + void onReceive (const Archive::ByteStream &); + void removeClient(Client *); + const std::set & getClients() const; +private: + CustomerServiceConnection(); + CustomerServiceConnection & operator = (const CustomerServiceConnection & rhs); + CustomerServiceConnection(const CustomerServiceConnection & source); + std::set clients; +}; + +//----------------------------------------------------------------------- + +#endif // _INCLUDED_CustomerServiceConnection_H diff --git a/engine/server/application/ConnectionServer/src/shared/FirstConnectionServer.h b/engine/server/application/ConnectionServer/src/shared/FirstConnectionServer.h new file mode 100644 index 00000000..6fd3b73b --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/FirstConnectionServer.h @@ -0,0 +1,23 @@ +// ====================================================================== +// +// FirstConnectionServer.h +// copyright (c) 2001 Sony Online Entertainment +// +// ====================================================================== + +#ifndef INCLUDED_FirstConnectionServer_H +#define INCLUDED_FirstConnectionServer_H + +// ====================================================================== + +#include "sharedFoundation/FirstSharedFoundation.h" +#include "sharedMemoryManager/FirstSharedMemoryManager.h" +#include "sharedDebug/FirstSharedDebug.h" +#include "ConnectionServer.h" +#include "sharedFoundation/NetworkIdArchive.h" +#include "sharedNetworkMessages/GameNetworkMessage.h" + +// ====================================================================== + +#endif + diff --git a/engine/server/application/ConnectionServer/src/shared/GameConnection.cpp b/engine/server/application/ConnectionServer/src/shared/GameConnection.cpp new file mode 100644 index 00000000..ddf0fe38 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/GameConnection.cpp @@ -0,0 +1,348 @@ +// GameConnection.cpp +// copyright 2001 Verant Interactive + +//----------------------------------------------------------------------- + +#include "FirstConnectionServer.h" +#include "GameConnection.h" + +#include "Archive/ByteStream.h" +#include "Client.h" +#include "ClientConnection.h" +#include "ConfigConnectionServer.h" +#include "ConnectionServer.h" +#include "CustomerServiceConnection.h" +#include "PseudoClientConnection.h" +#include "serverKeyShare/KeyShare.h" +#include "serverNetworkMessages/AccountFeatureIdRequest.h" +#include "serverNetworkMessages/AdjustAccountFeatureIdRequest.h" +#include "serverNetworkMessages/ClaimRewardsMessage.h" +#include "serverNetworkMessages/GameConnectionServerMessages.h" +#include "serverNetworkMessages/NewClient.h" +#include "serverNetworkMessages/TransferCharacterData.h" +#include "serverNetworkMessages/TransferCharacterDataArchive.h" +#include "SessionApiClient.h" +#include "sharedGame/PlatformFeatureBits.h" +#include "sharedLog/Log.h" +#include "sharedNetwork/NetworkSetupData.h" +#include "sharedNetwork/Service.h" +#include "sharedNetworkMessages/CommandChannelMessages.h" +#include "sharedNetworkMessages/CreateTicketMessage.h" +#include "sharedNetworkMessages/GenericValueTypeMessage.h" +#include "unicodeArchive/UnicodeArchive.h" + +//----------------------------------------------------------------------- + +GameConnection::GameConnection(const std::string & a, const unsigned short p) : +ServerConnection(a, p, NetworkSetupData()), +gameServerId(0), +sceneName() +{ +} + +//----------------------------------------------------------------------- + +GameConnection::GameConnection(UdpConnectionMT * u, TcpClient * t) : +ServerConnection(u, t), +gameServerId(0), +sceneName() +{ +} + +//----------------------------------------------------------------------- + +GameConnection::~GameConnection() +{ +} + +//----------------------------------------------------------------------- + +void GameConnection::onConnectionClosed() +{ + ServerConnection::onConnectionClosed(); + static MessageConnectionCallback m("GameConnectionClosed"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void GameConnection::onConnectionOpened() +{ + ServerConnection::onConnectionOpened(); + static MessageConnectionCallback m("GameConnectionOpened"); + emitMessage(m); +} + +//----------------------------------------------------------------------- + +void GameConnection::onReceive(const Archive::ByteStream & message) +{ + ServerConnection::onReceive(message); + Archive::ReadIterator ri = message.begin(); + GameNetworkMessage m(ri); + ri = message.begin(); + + if (m.isType("GameClientMessage")) + { + //we're receiving a message to forward to the client. + //it is prefixed with NetworkId and reliable. + const GameClientMessage msg(ri); + const std::vector & v = msg.getDistributionList(); + std::vector::const_iterator i; + const bool reliable = msg.getReliable(); + + Service *service = ConnectionServer::getClientServicePrivate(); + LogicalPacket const * p = service->createPacket(msg.getByteStream().getBuffer(), static_cast(msg.getByteStream().getSize())); + for(i = v.begin(); i != v.end(); ++i) + { + Client* client = ConnectionServer::getClient((*i)); + if (client) + { + client->getClientConnection()->sendSharedPacket(p, reliable); + } + } + service->releasePacket(p); + } + else if (m.isType("CreateTicketMessage")) + { + Archive::ReadIterator cri(m.getByteStream()); + CreateTicketMessage const c(cri); + CustomerServiceConnection * const customerServiceConnection = + const_cast(ConnectionServer::getCustomerServiceConnection()); + if (customerServiceConnection) + { + customerServiceConnection->send(c, true); + } + } + else if(m.isType("ControlAssumed")) + { + ControlAssumed ca(ri); + + static const std::string loginTrace("TRACE_LOGIN"); + LOG(loginTrace, ("Received Control Assumed Message from game server %lu for %s skipLoadScreen=%s", getGameServerId(), ca.getNetworkId().getValueString().c_str(), (ca.getSkipLoadScreen() ? "yes" : "no"))); + + Client *client = ConnectionServer::getClient(ca.getNetworkId()); + if (!client) + { + // perhaps it's a transfer client? + PseudoClientConnection * pseudoClient = PseudoClientConnection::getPseudoClientConnection(ca.getNetworkId()); + if(! pseudoClient) + { + DEBUG_REPORT_LOG(true, ("Client %s was already dropped, notifying GameServer.\n", ca.getNetworkId().getValueString().c_str())); + DropClient const drop(ca.getNetworkId()); + send(drop, true); + return; + } + else + { + pseudoClient->controlAssumed(); + return; + } + } + if (!client->getClientConnection()) + { + WARNING_STRICT_FATAL(true, ("We have a client with no client connection\n")); + return; + } + + if (ca.getSkipLoadScreen()) + client->skipLoadScreen(); + + if (!client->getSkipLoadScreen()) + { + CmdStartScene const startScene( + ca.getNetworkId(), + ca.getSceneName(), + ca.getStartPosition(), + ca.getStartYaw(), + ca.getTemplateName(), + ca.getTimeSeconds(), + static_cast(::time(NULL)), + ConfigConnectionServer::getDisableWorldSnapshot()); + client->getClientConnection()->send(startScene, true); + } + client->handleTransfer(ca.getSceneName(), this); + + // record the time when play started for the character + if (client->getClientConnection()->getStartPlayTime() == 0) + { + client->getClientConnection()->setStartPlayTime(::time(NULL)); + } + + // update the play time info on the game server + // must be called after client->handleTransfer() so the + // client object has been updated to point to the correct + // game server for the client->sendPlayTimeInfoToGameServer() + // call to work properly + client->getClientConnection()->sendPlayTimeInfoToGameServer(); + } + else if (m.isType("ReplyBankCTSLoaded")) + { + LOG("CustomerService", ("CharacterTransfer: Game Connection received ReplyBankCTSLoaded message")); + GenericValueTypeMessage characterId(ri); + + IGNORE_RETURN(PseudoClientConnection::tryToDeliverMessageTo(characterId.getValue(), message)); + } + else if (m.isType("PackedHousesLoaded")) + { + LOG("CustomerService", ("CharacterTransfer: Game Connection received PackedHousesLoaded message")); + GenericValueTypeMessage characterId(ri); + + IGNORE_RETURN(PseudoClientConnection::tryToDeliverMessageTo(characterId.getValue(), message)); + } + else if (m.isType("NewGameServer")) + { + DEBUG_REPORT_LOG(true, ("Received NewGameServerMessage.\n")); + // a game server has connected. Add it to the map + const NewGameServer newGameServer(ri); + + setGameServerId(newGameServer.getServerId()); + setSceneName(newGameServer.getSceneName()); + + // set the GameServerId @todo + // add it to the map if a game process + ConnectionServer::addGameConnection(newGameServer.getServerId(), this); + } + + else if (m.isType("KickPlayer")) + { + const KickPlayer kickPlayer(ri); + Client *client = ConnectionServer::getClient(kickPlayer.getNetworkId()); + if (client) + client->kick(kickPlayer.getReason()); + } + + else if (m.isType("TransferControlMessage")) + { + // a game server is giving up authority for an object we control + const TransferControlMessage transferControl(ri); + + DEBUG_REPORT_LOG(true, ("Received TransferControlMessage for %s.\n", transferControl.getNetworkId().getValueString().c_str())); + //question, do we need to check scene here? + Client* client = ConnectionServer::getClient(transferControl.getNetworkId()); + + if(client) + { + if (transferControl.getSkipLoadScreen()) + client->skipLoadScreen(); + if (client->getClientConnection()) + { + ClientConnection* cconn = client->getClientConnection(); + GameConnection* newConnection = ConnectionServer::getGameConnection(transferControl.getGameServerId()); + if(cconn && newConnection) + { + //We have a client, and a game connection, so send the newClient message off to the game server in question. + //It will respond with a control assumed message. + NewClient const newClient(transferControl.getNetworkId(), cconn->getAccountName(), cconn->getRemoteAddress(), cconn->getIsSecure(), transferControl.getSkipLoadScreen(), cconn->getSUID(), &transferControl.getObservedObjects(), cconn->getGameFeatures(), cconn->getSubscriptionFeatures(), cconn->getEntitlementTotalTime(), cconn->getEntitlementEntitledTime(), cconn->getEntitlementTotalTimeSinceLastLogin(), cconn->getEntitlementEntitledTimeSinceLastLogin(), cconn->getBuddyPoints(), cconn->getConsumedRewardEvents(), cconn->getClaimedRewardItems(), cconn->isUsingAdminLogin(), cconn->getCanSkipTutorial()); + newConnection->send(newClient, true); + } + else + { + WARNING_STRICT_FATAL(true, ("A TransferControl message was received to transfer client %s to GameServer %lu, but that game server is no longer available. The client will be dropped.", transferControl.getNetworkId().getValueString().c_str(), transferControl.getGameServerId())); + DropClient const drop(transferControl.getNetworkId()); + send(drop, true); + } + } + } + else + { + DEBUG_WARNING(true, ("Received a TransferControllMessage but the client is no longer available. This might be a lost race between the connection server notifiying a game server that the client has disconnected, and the game server notifying the connection server that the client is transferring.")); + DropClient const drop(transferControl.getNetworkId()); + send(drop, true); + } + } + else if(m.isType("ReplyTransferData") || m.isType("ApplyTransferDataSuccess") || m.isType("ApplyTransferDataFail")) + { + GenericValueTypeMessage reply(ri); + + IGNORE_RETURN(PseudoClientConnection::tryToDeliverMessageTo(reply.getValue().getCharacterId(), message)); + } + else if (m.isType("ChatEnterRoomValidationResponse")) + { + GenericValueTypeMessage, unsigned int> > const reply(ri); + + Client* client = ConnectionServer::getClient(reply.getValue().first.first); + if (client && client->getClientConnection()) + { + client->getClientConnection()->handleChatEnterRoomValidationResponse(reply.getValue().second, reply.getValue().first.second); + } + } + else if (m.isType("ChatQueryRoomValidationResponse")) + { + GenericValueTypeMessage, unsigned int> > const reply(ri); + + Client* client = ConnectionServer::getClient(reply.getValue().first.first); + if (client && client->getClientConnection()) + { + client->getClientConnection()->handleChatQueryRoomValidationResponse(reply.getValue().second, reply.getValue().first.second); + } + } + else if (m.isType("AccountFeatureIdRequest")) + { + AccountFeatureIdRequest * const msg = new AccountFeatureIdRequest(ri); + + SessionApiClient * const sessionApiClient = ConnectionServer::getSessionApiClient(); + if (sessionApiClient) + { + // SessionApiClient will own (and delete) msg + sessionApiClient->getFeatures(msg->getTargetStationId(), msg->getGameCode(), msg); + } + else + { + // a cluster running without session authentication stores + // account feature id information on the LoginServer, so + // pass request to the LoginServer (via CentralServer) + CentralConnection * const cc = ConnectionServer::getCentralConnection(); + if (cc) + cc->send(*msg, true); + + delete msg; + } + } + else if (m.isType("AdjustAccountFeatureIdRequest")) + { + AdjustAccountFeatureIdRequest * const msg = new AdjustAccountFeatureIdRequest(ri); + + SessionApiClient * const sessionApiClient = ConnectionServer::getSessionApiClient(); + if (sessionApiClient) + { + // SessionApiClient will own (and delete) msg + sessionApiClient->getFeatures(msg->getTargetStationId(), msg->getGameCode(), msg); + } + else + { + // a cluster running without session authentication stores + // account feature id information on the LoginServer, so + // pass request to the LoginServer (via CentralServer) + CentralConnection * const cc = ConnectionServer::getCentralConnection(); + if (cc) + cc->send(*msg, true); + + delete msg; + } + } + else if (m.isType("ClaimRewardsMessage")) + { + ClaimRewardsMessage * const msg = new ClaimRewardsMessage(ri); + + SessionApiClient * const sessionApiClient = ConnectionServer::getSessionApiClient(); + if ((msg->getAccountFeatureId() > 0) && sessionApiClient) + { + // SessionApiClient will own (and delete) msg + sessionApiClient->getFeatures(msg->getStationId(), PlatformGameCode::SWG, msg); + } + else + { + // a cluster running without session authentication stores + // account feature id information on the LoginServer, so + // pass request to the LoginServer (via CentralServer) + CentralConnection * const cc = ConnectionServer::getCentralConnection(); + if (cc) + cc->send(*msg, true); + + delete msg; + } + } +} + +//----------------------------------------------------------------------- diff --git a/engine/server/application/ConnectionServer/src/shared/GameConnection.h b/engine/server/application/ConnectionServer/src/shared/GameConnection.h new file mode 100644 index 00000000..30fc3265 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/GameConnection.h @@ -0,0 +1,76 @@ +// GameConnection.h +// copyright 2000 Verant Interactive +// Author: Justin Randall + +#ifndef _GameConnection_H +#define _GameConnection_H + +//----------------------------------------------------------------------- + +#include "serverUtility/ServerConnection.h" + +class GameCommandChannel; + +//----------------------------------------------------------------------- + +class GameConnection : public ServerConnection +{ +public: + GameConnection(const std::string & remoteAddress, const unsigned short port); + GameConnection(UdpConnectionMT *, TcpClient *); + virtual ~GameConnection(); + + uint32 getGameServerId() const; + const std::string & getSceneName() const; + + void setGameServerId(uint32 id); + void setSceneName(const std::string & name); + + void onConnectionClosed (); + void onConnectionOpened (); + void onReceive (const Archive::ByteStream & message); + +private: + GameConnection(); + GameConnection(const GameConnection&); + GameConnection& operator=(const GameConnection&); + + uint32 gameServerId; ///< The DB assigned Id of the connecting gameserver. + std::string sceneName; +}; + + +//----------------------------------------------------------------------- + +inline uint32 GameConnection::getGameServerId() const +{ + return gameServerId; +} + +//----------------------------------------------------------------------- + +inline const std::string & GameConnection::getSceneName() const +{ + return sceneName; +} + +//----------------------------------------------------------------------- + +inline void GameConnection::setGameServerId(uint32 id) +{ + gameServerId = id; +} + +//----------------------------------------------------------------------- + +inline void GameConnection::setSceneName(const std::string & name) +{ + sceneName = name; +} + +//----------------------------------------------------------------------- + + + + +#endif // _GameConnection_H diff --git a/engine/server/application/ConnectionServer/src/shared/PseudoClientConnection.cpp b/engine/server/application/ConnectionServer/src/shared/PseudoClientConnection.cpp new file mode 100644 index 00000000..49553a04 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/PseudoClientConnection.cpp @@ -0,0 +1,502 @@ +// 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, NULL, 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, NULL, 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); + } +} + +// ====================================================================== diff --git a/engine/server/application/ConnectionServer/src/shared/PseudoClientConnection.h b/engine/server/application/ConnectionServer/src/shared/PseudoClientConnection.h new file mode 100644 index 00000000..6902231b --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/PseudoClientConnection.h @@ -0,0 +1,57 @@ +// PseudoClientConnection.h +// copyright 2001 Verant Interactive + +#ifndef _PseudoClientConnection_H +#define _PseudoClientConnection_H + +#include "serverNetworkMessages/TransferCharacterData.h" + +namespace Archive +{ + class ByteStream; +} + +class GameConnection; + +//----------------------------------------------------------------------- + +class PseudoClientConnection +{ +public: + PseudoClientConnection(const TransferCharacterData & transferDataFromCentralServer, unsigned int stationId); + virtual ~PseudoClientConnection(); + + void controlAssumed (); + void onBankLoaded (); + void onPackedHousesLoaded (); + + static PseudoClientConnection * getPseudoClientConnection (const NetworkId & characterId); + static PseudoClientConnection * getPseudoClientConnection (unsigned int stationId); + static void install (); + void requestGameServerForLogin () const; + static bool tryToDeliverMessageTo (unsigned int stationId, const Archive::ByteStream & msg); + static bool tryToDeliverMessageTo (const NetworkId & characterId, const Archive::ByteStream & msg); + static void gameConnectionClosed (const GameConnection *); + + const TransferCharacterData & getTransferCharacterData () const; + +protected: + friend class CentralConnection; + static void destroyAllPseudoClientConnectionInstances (); + +private: + PseudoClientConnection(); + PseudoClientConnection(const PseudoClientConnection&); + PseudoClientConnection& operator=(const PseudoClientConnection&); + + void receiveMessage (const Archive::ByteStream &); + void gameConnectionClosed (); + + TransferCharacterData m_transferCharacterData; + GameConnection * m_gameConnection; + unsigned int m_trackStationId; +}; + +//----------------------------------------------------------------------- +#endif // _PseudoClientConnection_H + diff --git a/engine/server/application/ConnectionServer/src/shared/SessionApiClient.cpp b/engine/server/application/ConnectionServer/src/shared/SessionApiClient.cpp new file mode 100644 index 00000000..5a4de5fc --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/SessionApiClient.cpp @@ -0,0 +1,1001 @@ +// SessionApiClient.cpp +// copyright 2002 Sony Online Entertainment + + +#include "FirstConnectionServer.h" +#include "SessionApiClient.h" + +#include "ClientConnection.h" +#include "ConfigConnectionServer.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 "Session/CommonAPI/CommonAPIStrings.h" +#include "sharedFoundation/FormattedString.h" +#include "sharedGame/PlatformFeatureBits.h" +#include "sharedLog/Log.h" + +#include +#include + +//------------------------------------------------------------------------------------------ + +namespace SessionApiClientNamespace +{ + std::map ms_sessionIdMap; + std::map ms_getFeaturesTrackingNumberMap; + std::map ms_modifyFeatureTrackingNumberMap; + std::map ms_grantFeatureTrackingNumberMap; +} + +using namespace SessionApiClientNamespace; + + +//------------------------------------------------------------------------------------------ + +std::map SessionApiClient::m_validationMap; + +//------------------------------------------------------------------------------------------ + +namespace SessionApiClientNamespace +{ + +}; + +using namespace SessionApiClientNamespace; + +//------------------------------------------------------------ + +SessionApiClient::SessionApiClient(const char ** serverList, int serverCount) : + Client(serverList, static_cast(serverCount), "Starwars Connection Server"), + m_sessionTimer(ConfigConnectionServer::getTimeBetweenSessionUpdates()) + +{ +} + +//------------------------------------------------------------ + +SessionApiClient::~SessionApiClient() +{ + std::map::iterator j = ms_sessionIdMap.begin(); + std::vector sessionList; + sessionList.reserve(ms_sessionIdMap.size()); + for (; j != ms_sessionIdMap.end(); ++j) + { + sessionList.push_back(j->first.c_str()); + } + + if (!ConfigConnectionServer::getDisableSessionLogout()) + { + //SessionLogout all clients + SessionLogout(&sessionList[0], sessionList.size()); + } +} + +//--------------------------------------------------------------------- + +void SessionApiClient::OnSessionLogin(const apiTrackingNumber trackingNumber, + const apiResult result, + const apiAccount & account, + const apiSubscription & subscription, + const apiSession & session, + const LoginAPI::UsageLimit & usageLimit, + const LoginAPI::Entitlement & entitlement, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(account); + UNREF(subscription); + UNREF(session); + UNREF(usageLimit); + UNREF(entitlement); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Session login.\n")); +} + +//--------------------------------------------------------------------- + +void SessionApiClient::OnSessionLoginInternal(const apiTrackingNumber trackingNumber, + const apiResult result, + const apiAccount & account, + const apiSubscription & subscription, + const apiSession & session, + const LoginAPI::UsageLimit & usageLimit, + const LoginAPI::Entitlement & entitlement, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(account); + UNREF(subscription); + UNREF(session); + UNREF(usageLimit); + UNREF(entitlement); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Session login internal.\n")); +} + +//--------------------------------------------------------------------- + +void SessionApiClient::OnSessionValidate(const apiTrackingNumber trackingNumber, + const apiResult result, + const apiAccount & account, + const apiSubscription & subscription, + const apiSession & session, + const LoginAPI::UsageLimit & usageLimit, + const LoginAPI::Entitlement & entitlement, + void * userData) +{ + UNREF(userData); + UNREF(usageLimit); + + std::map::iterator i = m_validationMap.find(trackingNumber); + DEBUG_REPORT_LOG(true, ("OnSessionValidate result: %d for suid: %d (entitlement total: %u/%u, since last login: %u/%u, reason unentitled: %d)\n", result, account.GetId(), entitlement.GetEntitledTime(), entitlement.GetTotalTime(), entitlement.GetEntitledTimeSinceLastLogin(), entitlement.GetTotalTimeSinceLastLogin(), static_cast(entitlement.GetReasonUnentitled()))); + if (i != m_validationMap.end()) + { + if (result == RESULT_SUCCESS) + { + if (i->second) + { + i->second->onValidateClient(account.GetId(), account.GetName(), session.GetIsSecure(), session.GetId(), subscription.GetGameFeatures(), subscription.GetSubscriptionFeatures(), entitlement.GetTotalTime(), entitlement.GetEntitledTime(), entitlement.GetTotalTimeSinceLastLogin(), entitlement.GetEntitledTimeSinceLastLogin(), ConfigConnectionServer::getFakeBuddyPoints()); //TODO: get buddy points from station + std::map::iterator j = ms_sessionIdMap.find(session.GetId()); + if (j != ms_sessionIdMap.end()) + { + LOG("ClientDisconnect", ("Client %s Disconnected by duplicate sessionId login.", j->second->getSessionId().c_str())); + LOG("CustomerService", ("Login:%s SessionId %s Disconnected by duplicate sessionId login.", ClientConnection::describeAccount(j->second).c_str(), j->second->getSessionId().c_str())); + j->second->disconnect(); + } + + ms_sessionIdMap[session.GetId()] = i->second; + } + m_validationMap.erase(i); + } + else + { +// ErrorMessage err("VALIDATION FAILED", "Your station Id was not valid. Wrong password? Account closed?"); +// i->second->send(err, true); + LOG("ClientDisconnect", ("Suid %s by session denial reason %d.", account.GetId(), result)); + LOG("CustomerService", ("Login:%s by session denial reason %d.", ClientConnection::describeAccount(i->second).c_str(), result)); + i->second->disconnect(); + } + } + else + { + DEBUG_REPORT_LOG(true, ("Could not find client in the validation map\n.")); + } +} + +//--------------------------------------------------------------------- + +void SessionApiClient::OnSessionConsume(const apiTrackingNumber trackingNumber, + const apiResult result, + const apiAccount & account, + const apiSubscription & subscription, + const apiSession & session, + const LoginAPI::UsageLimit & usageLimit, + const LoginAPI::Entitlement & entitlement, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(account); + UNREF(subscription); + UNREF(session); + UNREF(usageLimit); + UNREF(entitlement); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Session consume.\n")); +} + +//--------------------------------------------------------------------- + +void SessionApiClient::OnSessionStartPlay(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Session start play.\n")); +} + +//--------------------------------------------------------------------- + +void SessionApiClient::OnSessionStopPlay(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Session stop play.\n")); +} + +//--------------------------------------------------------------------- + +void SessionApiClient::OnSessionKick(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Session kick.\n")); +} + +//--------------------------------------------------------------------- + +void SessionApiClient::OnGetSessions(const apiTrackingNumber trackingNumber, + const apiResult result, + const unsigned count, + const apiSession session[], + const apiSubscription subscription[], + const LoginAPI::UsageLimit usageLimit[], + const unsigned timeCreated[], + const unsigned timeTouched[], + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(count); + UNREF(session); + UNREF(subscription); + UNREF(usageLimit); + UNREF(timeCreated); + UNREF(timeTouched); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Get sessions.\n")); +} + +//------------------------------------------------------------ + +void SessionApiClient::OnGetFeatures(const apiTrackingNumber trackingNumber, + const apiResult result, + const unsigned featureCount, + const LoginAPI::Feature featureArray[], + const void * userData) +{ + UNREF(userData); + UNREF(AccountStatusString); + UNREF(SessionTypeString); + UNREF(GamecodeString); + UNREF(SubscriptionStatusString); + + const char * const resultString = ResultString[result]; + std::string sResultString; + if (resultString) + { + sResultString = resultString; + } + else + { + char buffer[32]; + snprintf(buffer, sizeof(buffer)-1, "%u", result); + buffer[sizeof(buffer)-1] = '\0'; + + sResultString = buffer; + } + + const char * const resultText = ResultText[result]; + std::string sResultText; + if (resultText) + { + sResultText = resultText; + } + else + { + char buffer[32]; + snprintf(buffer, sizeof(buffer)-1, "%u", result); + buffer[sizeof(buffer)-1] = '\0'; + + sResultText = buffer; + } + + DEBUG_REPORT_LOG(true, ("SessionApiClient::OnGetFeatures() - [%u][%u][%u][%s][%s]\n", trackingNumber, featureCount, result, sResultString.c_str(), sResultText.c_str())); + + std::map::iterator i = ms_getFeaturesTrackingNumberMap.find(trackingNumber); + if (i != ms_getFeaturesTrackingNumberMap.end()) + { + bool reuseMessage = false; + AccountFeatureIdRequest const * accountFeatureIdRequest = NULL; + AdjustAccountFeatureIdRequest const * adjustAccountFeatureIdRequest = NULL; + AdjustAccountFeatureIdResponse * adjustAccountFeatureIdResponse = NULL; + ClaimRewardsMessage * claimRewardsMessage = NULL; + + if (i->second->isType("AccountFeatureIdRequest")) + accountFeatureIdRequest = dynamic_cast(i->second); + else if (i->second->isType("AdjustAccountFeatureIdRequest")) + adjustAccountFeatureIdRequest = dynamic_cast(i->second); + else if (i->second->isType("AdjustAccountFeatureIdResponse")) + adjustAccountFeatureIdResponse = dynamic_cast(i->second); + else if (i->second->isType("ClaimRewardsMessage")) + claimRewardsMessage = dynamic_cast(i->second); + + if (result == RESULT_SUCCESS) + { + ClientConnection * clientConnection = NULL; + if (accountFeatureIdRequest) + clientConnection = ConnectionServer::getClientConnection(accountFeatureIdRequest->getTargetStationId()); + else if (adjustAccountFeatureIdRequest) + clientConnection = ConnectionServer::getClientConnection(adjustAccountFeatureIdRequest->getTargetStationId()); + else if (adjustAccountFeatureIdResponse) + clientConnection = ConnectionServer::getClientConnection(adjustAccountFeatureIdResponse->getTargetStationId()); + else if (claimRewardsMessage) + clientConnection = ConnectionServer::getClientConnection(claimRewardsMessage->getStationId()); + + if (clientConnection && !clientConnection->getHasCSLoggedAccountFeatureIds()) + { + clientConnection->setHasCSLoggedAccountFeatureIds(true); + + std::string featureCodes; + for (unsigned k = 0; k < featureCount; ++k) + { + if (!featureCodes.empty()) + featureCodes += ", "; + + featureCodes += FormattedString<512>().sprintf("%u (%s)", featureArray[k].GetID(), featureArray[k].GetData().c_str()); + } + + LOG("CustomerService", ("Login:%s has feature count %u (%s:%s) {%s}", + ClientConnection::describeAccount(clientConnection).c_str(), featureCount, sResultString.c_str(), sResultText.c_str(), featureCodes.c_str())); + } + } + + if (accountFeatureIdRequest) + { + GameConnection * const gc = ConnectionServer::getGameConnection(accountFeatureIdRequest->getGameServer()); + if (gc) + { + std::map featureIds; + std::map featureIdsData; + + if (result == RESULT_SUCCESS) + { + for (unsigned k = 0; k < featureCount; ++k) + { + featureIdsData[featureArray[k].GetID()] = featureArray[k].GetData(); + featureIds[featureArray[k].GetID()] = featureArray[k].GetConsumeCount(); + } + } + + AccountFeatureIdResponse const rsp(accountFeatureIdRequest->getRequester(), accountFeatureIdRequest->getGameServer(), accountFeatureIdRequest->getTarget(), accountFeatureIdRequest->getTargetStationId(), accountFeatureIdRequest->getGameCode(), accountFeatureIdRequest->getRequestReason(), result, true, featureIds, featureIdsData, sResultString.c_str(), sResultText.c_str()); + gc->send(rsp,true); + } + } + else if (adjustAccountFeatureIdRequest) + { + if (result != RESULT_SUCCESS) + { + // CS log SWG TCG or reward trade in account feature grant failure + if (!adjustAccountFeatureIdRequest->getTargetPlayerDescription().empty() && adjustAccountFeatureIdRequest->getTargetItem().isValid() && !adjustAccountFeatureIdRequest->getTargetItemDescription().empty()) + { + if (adjustAccountFeatureIdRequest->getGameCode() == PlatformGameCode::SWGTCG) + LOG("CustomerService",("TcgRedemption: %s ***FAILED TO REDEEM*** %s for SWGTCG account feature Id %lu with OnGetFeatures() error code (%u, %s:%s)", adjustAccountFeatureIdRequest->getTargetPlayerDescription().c_str(), adjustAccountFeatureIdRequest->getTargetItemDescription().c_str(), adjustAccountFeatureIdRequest->getFeatureId(), result, sResultString.c_str(), sResultText.c_str())); + else if (adjustAccountFeatureIdRequest->getGameCode() == PlatformGameCode::SWG) + LOG("CustomerService",("VeteranRewards: %s ***FAILED TO TRADE IN*** %s for SWG account feature Id %lu with OnGetFeatures() error code (%u, %s:%s)", adjustAccountFeatureIdRequest->getTargetPlayerDescription().c_str(), adjustAccountFeatureIdRequest->getTargetItemDescription().c_str(), adjustAccountFeatureIdRequest->getFeatureId(), result, sResultString.c_str(), sResultText.c_str())); + } + + GameConnection * const gc = ConnectionServer::getGameConnection(adjustAccountFeatureIdRequest->getGameServer()); + if (gc) + { + AdjustAccountFeatureIdResponse const rsp(adjustAccountFeatureIdRequest->getRequestingPlayer(), adjustAccountFeatureIdRequest->getGameServer(), adjustAccountFeatureIdRequest->getTargetPlayer(), adjustAccountFeatureIdRequest->getTargetPlayerDescription(), adjustAccountFeatureIdRequest->getTargetStationId(), adjustAccountFeatureIdRequest->getTargetItem(), adjustAccountFeatureIdRequest->getTargetItemDescription(), adjustAccountFeatureIdRequest->getGameCode(), adjustAccountFeatureIdRequest->getFeatureId(), 0, 0, result, true, sResultString.c_str(), sResultText.c_str()); + gc->send(rsp,true); + } + } + else + { + // if account already has the feature, adjust it, otherwise add the feature + LoginAPI::Feature const * existingFeature = NULL; + + for (unsigned k = 0; k < featureCount; ++k) + { + if (featureArray[k].GetID() == adjustAccountFeatureIdRequest->getFeatureId()) + { + existingFeature = &(featureArray[k]); + break; + } + } + + if (existingFeature) + { + int const currentCount = existingFeature->GetConsumeCount(); + int const updatedCount = std::max(0, currentCount + adjustAccountFeatureIdRequest->getAdjustment()); + + LoginAPI::Feature updatedFeature; + updatedFeature.SetID(existingFeature->GetID()); + updatedFeature.SetData(existingFeature->GetData()); + updatedFeature.SetParameter("count", updatedCount); + + apiTrackingNumber const tn = ModifyFeature_v2(adjustAccountFeatureIdRequest->getTargetStationId(), PlatformGameCode::getGamecodeName(adjustAccountFeatureIdRequest->getGameCode()).c_str(), *existingFeature, updatedFeature); + ms_modifyFeatureTrackingNumberMap[tn] = new AdjustAccountFeatureIdResponse(adjustAccountFeatureIdRequest->getRequestingPlayer(), adjustAccountFeatureIdRequest->getGameServer(), adjustAccountFeatureIdRequest->getTargetPlayer(), adjustAccountFeatureIdRequest->getTargetPlayerDescription(), adjustAccountFeatureIdRequest->getTargetStationId(), adjustAccountFeatureIdRequest->getTargetItem(), adjustAccountFeatureIdRequest->getTargetItemDescription(), adjustAccountFeatureIdRequest->getGameCode(), adjustAccountFeatureIdRequest->getFeatureId(), currentCount, updatedCount, RESULT_SUCCESS, true); + } + else + { + apiTrackingNumber const tn = GrantFeatureByStationID(adjustAccountFeatureIdRequest->getTargetStationId(), adjustAccountFeatureIdRequest->getFeatureId(), PlatformGameCode::getGamecodeName(adjustAccountFeatureIdRequest->getGameCode()).c_str()); + ms_grantFeatureTrackingNumberMap[tn] = new AdjustAccountFeatureIdResponse(adjustAccountFeatureIdRequest->getRequestingPlayer(), adjustAccountFeatureIdRequest->getGameServer(), adjustAccountFeatureIdRequest->getTargetPlayer(), adjustAccountFeatureIdRequest->getTargetPlayerDescription(), adjustAccountFeatureIdRequest->getTargetStationId(), adjustAccountFeatureIdRequest->getTargetItem(), adjustAccountFeatureIdRequest->getTargetItemDescription(), adjustAccountFeatureIdRequest->getGameCode(), adjustAccountFeatureIdRequest->getFeatureId(), 0, std::max(0, adjustAccountFeatureIdRequest->getAdjustment()), RESULT_SUCCESS, true); + } + } + } + else if (adjustAccountFeatureIdResponse) + { + if (result != RESULT_SUCCESS) + { + // CS log SWG TCG or reward trade in account feature grant failure + if (!adjustAccountFeatureIdResponse->getTargetPlayerDescription().empty() && adjustAccountFeatureIdResponse->getTargetItem().isValid() && !adjustAccountFeatureIdResponse->getTargetItemDescription().empty()) + { + if (adjustAccountFeatureIdResponse->getGameCode() == PlatformGameCode::SWGTCG) + LOG("CustomerService",("TcgRedemption: %s ***FAILED TO REDEEM*** %s for SWGTCG account feature Id %lu with OnGetFeatures() error code (%u, %s:%s)", adjustAccountFeatureIdResponse->getTargetPlayerDescription().c_str(), adjustAccountFeatureIdResponse->getTargetItemDescription().c_str(), adjustAccountFeatureIdResponse->getFeatureId(), result, sResultString.c_str(), sResultText.c_str())); + else if (adjustAccountFeatureIdResponse->getGameCode() == PlatformGameCode::SWG) + LOG("CustomerService",("VeteranRewards: %s ***FAILED TO TRADE IN*** %s for SWG account feature Id %lu with OnGetFeatures() error code (%u, %s:%s)", adjustAccountFeatureIdResponse->getTargetPlayerDescription().c_str(), adjustAccountFeatureIdResponse->getTargetItemDescription().c_str(), adjustAccountFeatureIdResponse->getFeatureId(), result, sResultString.c_str(), sResultText.c_str())); + } + + GameConnection * const gc = ConnectionServer::getGameConnection(adjustAccountFeatureIdResponse->getGameServer()); + if (gc) + { + adjustAccountFeatureIdResponse->setSessionResultCode(result, sResultString.c_str(), sResultText.c_str()); + gc->send(*adjustAccountFeatureIdResponse,true); + } + } + else + { + LoginAPI::Feature const * newlyAddedFeature = NULL; + + for (unsigned k = 0; k < featureCount; ++k) + { + if (featureArray[k].GetID() == adjustAccountFeatureIdResponse->getFeatureId()) + { + newlyAddedFeature = &(featureArray[k]); + break; + } + } + + if (newlyAddedFeature) + { + LoginAPI::Feature updatedFeature; + updatedFeature.SetID(newlyAddedFeature->GetID()); + updatedFeature.SetData(newlyAddedFeature->GetData()); + updatedFeature.SetParameter("count", adjustAccountFeatureIdResponse->getNewValue()); + + if (adjustAccountFeatureIdResponse->getGameCode() == PlatformGameCode::SWGTCG) + updatedFeature.SetActive(true); + + apiTrackingNumber const tn = ModifyFeature_v2(adjustAccountFeatureIdResponse->getTargetStationId(), PlatformGameCode::getGamecodeName(adjustAccountFeatureIdResponse->getGameCode()).c_str(), *newlyAddedFeature, updatedFeature); + ms_modifyFeatureTrackingNumberMap[tn] = adjustAccountFeatureIdResponse; + reuseMessage = true; + } + else + { + // CS log SWG TCG or reward trade in account feature grant failure + if (!adjustAccountFeatureIdResponse->getTargetPlayerDescription().empty() && adjustAccountFeatureIdResponse->getTargetItem().isValid() && !adjustAccountFeatureIdResponse->getTargetItemDescription().empty()) + { + if (adjustAccountFeatureIdResponse->getGameCode() == PlatformGameCode::SWGTCG) + LOG("CustomerService",("TcgRedemption: %s ***FAILED TO REDEEM*** %s for SWGTCG account feature Id %lu with OnGetFeatures() error code (MISSING_NEWLY_ADDED_FEATURE_ID:The feature Id was just successfully added, but now it's gone)", adjustAccountFeatureIdResponse->getTargetPlayerDescription().c_str(), adjustAccountFeatureIdResponse->getTargetItemDescription().c_str(), adjustAccountFeatureIdResponse->getFeatureId())); + else if (adjustAccountFeatureIdResponse->getGameCode() == PlatformGameCode::SWG) + LOG("CustomerService",("VeteranRewards: %s ***FAILED TO TRADE IN*** %s for SWG account feature Id %lu with OnGetFeatures() error code (MISSING_NEWLY_ADDED_FEATURE_ID:The feature Id was just successfully added, but now it's gone)", adjustAccountFeatureIdResponse->getTargetPlayerDescription().c_str(), adjustAccountFeatureIdResponse->getTargetItemDescription().c_str(), adjustAccountFeatureIdResponse->getFeatureId())); + } + + // this situation shouldn't happen, as we just successfully added the feature Id, + // and now it's not there anymore when we retrieve it; + // just pick some generic session error message to indicate this situation + GameConnection * const gc = ConnectionServer::getGameConnection(adjustAccountFeatureIdResponse->getGameServer()); + if (gc) + { + adjustAccountFeatureIdResponse->setSessionResultCode(RESULT_CANCELLED, "MISSING_NEWLY_ADDED_FEATURE_ID", "The feature Id was just successfully added, but now it's gone"); + gc->send(*adjustAccountFeatureIdResponse,true); + } + } + } + } + else if (claimRewardsMessage) + { + if (result != RESULT_SUCCESS) + { + GameConnection * const gc = ConnectionServer::getGameConnection(claimRewardsMessage->getGameServer()); + if (gc) + { + ClaimRewardsReplyMessage const rsp(claimRewardsMessage->getGameServer(), claimRewardsMessage->getStationId(), claimRewardsMessage->getPlayer(), claimRewardsMessage->getRewardEvent(), claimRewardsMessage->getRewardItem(), claimRewardsMessage->getAccountFeatureId(), claimRewardsMessage->getConsumeAccountFeatureId(), 0, 0, false); + gc->send(rsp,true); + } + } + else + { + // see if account already has the required feature + LoginAPI::Feature const * existingFeature = NULL; + + for (unsigned k = 0; k < featureCount; ++k) + { + if (featureArray[k].GetID() == claimRewardsMessage->getAccountFeatureId()) + { + existingFeature = &(featureArray[k]); + break; + } + } + + if (existingFeature) + { + int const currentCount = existingFeature->GetConsumeCount(); + if (currentCount > 0) + { + // if the reward consumes the account feature Id, or + // is "once per account", then forward message to + // LoginServer to complete the transaction; otherwise, + // verification is complete, so send success response + if (claimRewardsMessage->getConsumeAccountFeatureId() || claimRewardsMessage->getConsumeEvent() || claimRewardsMessage->getConsumeItem()) + { + int const updatedCount = (claimRewardsMessage->getConsumeAccountFeatureId() ? (currentCount - 1) : currentCount); + + LoginAPI::Feature updatedFeature; + updatedFeature.SetID(existingFeature->GetID()); + updatedFeature.SetData(existingFeature->GetData()); + updatedFeature.SetParameter("count", updatedCount); + + // forward message to LoginServer to complete the transaction + claimRewardsMessage->setAccountFeatureIdOldValue(existingFeature->GetData()); + claimRewardsMessage->setAccountFeatureIdNewValue(updatedFeature.GetData()); + + CentralConnection * const cc = ConnectionServer::getCentralConnection(); + if (cc) + cc->send(*claimRewardsMessage, true); + } + else + { + GameConnection * const gc = ConnectionServer::getGameConnection(claimRewardsMessage->getGameServer()); + if (gc) + { + ClaimRewardsReplyMessage const rsp(claimRewardsMessage->getGameServer(), claimRewardsMessage->getStationId(), claimRewardsMessage->getPlayer(), claimRewardsMessage->getRewardEvent(), claimRewardsMessage->getRewardItem(), claimRewardsMessage->getAccountFeatureId(), claimRewardsMessage->getConsumeAccountFeatureId(), currentCount, currentCount, true); + gc->send(rsp,true); + } + } + } + else + { + GameConnection * const gc = ConnectionServer::getGameConnection(claimRewardsMessage->getGameServer()); + if (gc) + { + // if the account feature id indicates the account no longer + // qualifies for the reward, then there must be a mismatch between + // the account feature id cache in the game server, and what's + // really on the account, so send this fresh set of the account + // feature id to the game server + std::map featureIds; + std::map featureIdsData; + + for (unsigned k = 0; k < featureCount; ++k) + { + featureIdsData[featureArray[k].GetID()] = featureArray[k].GetData(); + featureIds[featureArray[k].GetID()] = featureArray[k].GetConsumeCount(); + } + + AccountFeatureIdResponse const updatedFeatureIds(NetworkId::cms_invalid, claimRewardsMessage->getGameServer(), claimRewardsMessage->getPlayer(), claimRewardsMessage->getStationId(), PlatformGameCode::SWG, AccountFeatureIdRequest::RR_Reload, result, true, featureIds, featureIdsData, sResultString.c_str(), sResultText.c_str()); + gc->send(updatedFeatureIds,true); + + ClaimRewardsReplyMessage const rsp(claimRewardsMessage->getGameServer(), claimRewardsMessage->getStationId(), claimRewardsMessage->getPlayer(), claimRewardsMessage->getRewardEvent(), claimRewardsMessage->getRewardItem(), claimRewardsMessage->getAccountFeatureId(), claimRewardsMessage->getConsumeAccountFeatureId(), 0, 0, false); + gc->send(rsp,true); + } + } + } + else + { + GameConnection * const gc = ConnectionServer::getGameConnection(claimRewardsMessage->getGameServer()); + if (gc) + { + // if the account feature id indicates the account no longer + // qualifies for the reward, then there must be a mismatch between + // the account feature id cache in the game server, and what's + // really on the account, so send this fresh set of the account + // feature id to the game server + std::map featureIds; + std::map featureIdsData; + + for (unsigned k = 0; k < featureCount; ++k) + { + featureIdsData[featureArray[k].GetID()] = featureArray[k].GetData(); + featureIds[featureArray[k].GetID()] = featureArray[k].GetConsumeCount(); + } + + AccountFeatureIdResponse const updatedFeatureIds(NetworkId::cms_invalid, claimRewardsMessage->getGameServer(), claimRewardsMessage->getPlayer(), claimRewardsMessage->getStationId(), PlatformGameCode::SWG, AccountFeatureIdRequest::RR_Reload, result, true, featureIds, featureIdsData, sResultString.c_str(), sResultText.c_str()); + gc->send(updatedFeatureIds,true); + + ClaimRewardsReplyMessage const rsp(claimRewardsMessage->getGameServer(), claimRewardsMessage->getStationId(), claimRewardsMessage->getPlayer(), claimRewardsMessage->getRewardEvent(), claimRewardsMessage->getRewardItem(), claimRewardsMessage->getAccountFeatureId(), claimRewardsMessage->getConsumeAccountFeatureId(), 0, 0, false); + gc->send(rsp,true); + } + } + } + } + + if (!reuseMessage) + delete i->second; + + ms_getFeaturesTrackingNumberMap.erase(i); + } + else + { + DEBUG_REPORT_LOG(true, ("SessionApiClient::OnGetFeatures() - could not find session id for tracking number [%u]\n", trackingNumber)); + } +} + +//------------------------------------------------------------ + +void SessionApiClient::OnGrantFeature(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Grant feature.\n")); +} + +//------------------------------------------------------------ + +void SessionApiClient::OnGrantFeatureByStationID(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData) +{ + UNREF(userData); + + const char * const resultString = ResultString[result]; + std::string sResultString; + if (resultString) + { + sResultString = resultString; + } + else + { + char buffer[32]; + snprintf(buffer, sizeof(buffer)-1, "%u", result); + buffer[sizeof(buffer)-1] = '\0'; + + sResultString = buffer; + } + + const char * const resultText = ResultText[result]; + std::string sResultText; + if (resultText) + { + sResultText = resultText; + } + else + { + char buffer[32]; + snprintf(buffer, sizeof(buffer)-1, "%u", result); + buffer[sizeof(buffer)-1] = '\0'; + + sResultText = buffer; + } + + DEBUG_REPORT_LOG(true, ("SessionApiClient::OnGrantFeatureByStationID() - [%u][%u][%s][%s]\n", trackingNumber, result, sResultString.c_str(), sResultText.c_str())); + + std::map::iterator i = ms_grantFeatureTrackingNumberMap.find(trackingNumber); + if (i != ms_grantFeatureTrackingNumberMap.end()) + { + if (result != RESULT_SUCCESS) + { + // CS log SWG TCG or reward trade in account feature grant failure + if (!i->second->getTargetPlayerDescription().empty() && i->second->getTargetItem().isValid() && !i->second->getTargetItemDescription().empty()) + { + if (i->second->getGameCode() == PlatformGameCode::SWGTCG) + LOG("CustomerService",("TcgRedemption: %s ***FAILED TO REDEEM*** %s for SWGTCG account feature Id %lu with OnGrantFeatureByStationID() error code (%u, %s:%s)", i->second->getTargetPlayerDescription().c_str(), i->second->getTargetItemDescription().c_str(), i->second->getFeatureId(), result, sResultString.c_str(), sResultText.c_str())); + else if (i->second->getGameCode() == PlatformGameCode::SWG) + LOG("CustomerService",("VeteranRewards: %s ***FAILED TO TRADE IN*** %s for SWG account feature Id %lu with OnGrantFeatureByStationID() error code (%u, %s:%s)", i->second->getTargetPlayerDescription().c_str(), i->second->getTargetItemDescription().c_str(), i->second->getFeatureId(), result, sResultString.c_str(), sResultText.c_str())); + } + + GameConnection * const gc = ConnectionServer::getGameConnection(i->second->getGameServer()); + if (gc) + { + i->second->setSessionResultCode(result, sResultString.c_str(), sResultText.c_str()); + gc->send(*(i->second),true); + } + + delete i->second; + } + else + { + apiTrackingNumber const tn = GetFeatures(i->second->getTargetStationId(), i->second->getGameCode()); + ms_getFeaturesTrackingNumberMap[tn] = i->second; + } + + ms_grantFeatureTrackingNumberMap.erase(i); + } + else + { + DEBUG_REPORT_LOG(true, ("SessionApiClient::OnGrantFeatureByStationID() - could not find session id for tracking number [%u]\n", trackingNumber)); + } +} + +//------------------------------------------------------------ + +void SessionApiClient::OnModifyFeature(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Modify feature.\n")); +} + +//------------------------------------------------------------ + +void SessionApiClient::OnModifyFeature_v2(const apiTrackingNumber trackingNumber, + const apiResult result, + const LoginAPI::Feature & currentFeature, + void * userData) +{ + UNREF(currentFeature); + UNREF(userData); + + const char * const resultString = ResultString[result]; + std::string sResultString; + if (resultString) + { + sResultString = resultString; + } + else + { + char buffer[32]; + snprintf(buffer, sizeof(buffer)-1, "%u", result); + buffer[sizeof(buffer)-1] = '\0'; + + sResultString = buffer; + } + + const char * const resultText = ResultText[result]; + std::string sResultText; + if (resultText) + { + sResultText = resultText; + } + else + { + char buffer[32]; + snprintf(buffer, sizeof(buffer)-1, "%u", result); + buffer[sizeof(buffer)-1] = '\0'; + + sResultText = buffer; + } + + DEBUG_REPORT_LOG(true, ("SessionApiClient::OnModifyFeature_v2() - [%u][%u][%s][%s]\n", trackingNumber, result, sResultString.c_str(), sResultText.c_str())); + + std::map::iterator i = ms_modifyFeatureTrackingNumberMap.find(trackingNumber); + if (i != ms_modifyFeatureTrackingNumberMap.end()) + { + if (result != RESULT_SUCCESS) + { + // CS log SWG TCG or reward trade in account feature grant failure + if (!i->second->getTargetPlayerDescription().empty() && i->second->getTargetItem().isValid() && !i->second->getTargetItemDescription().empty()) + { + if (i->second->getGameCode() == PlatformGameCode::SWGTCG) + LOG("CustomerService",("TcgRedemption: %s ***FAILED TO REDEEM*** %s for SWGTCG account feature Id %lu with OnModifyFeature_v2() error code (%u, %s:%s)", i->second->getTargetPlayerDescription().c_str(), i->second->getTargetItemDescription().c_str(), i->second->getFeatureId(), result, sResultString.c_str(), sResultText.c_str())); + else if (i->second->getGameCode() == PlatformGameCode::SWG) + LOG("CustomerService",("VeteranRewards: %s ***FAILED TO TRADE IN*** %s for SWG account feature Id %lu with OnModifyFeature_v2() error code (%u, %s:%s)", i->second->getTargetPlayerDescription().c_str(), i->second->getTargetItemDescription().c_str(), i->second->getFeatureId(), result, sResultString.c_str(), sResultText.c_str())); + } + } + else + { + // CS log SWG TCG or reward trade in account feature grant + if (!i->second->getTargetPlayerDescription().empty() && i->second->getTargetItem().isValid() && !i->second->getTargetItemDescription().empty()) + { + if (i->second->getGameCode() == PlatformGameCode::SWGTCG) + LOG("CustomerService",("TcgRedemption: %s redeemed %s for SWGTCG account feature Id %lu (%d -> %d)", i->second->getTargetPlayerDescription().c_str(), i->second->getTargetItemDescription().c_str(), i->second->getFeatureId(), i->second->getOldValue(), i->second->getNewValue())); + else if (i->second->getGameCode() == PlatformGameCode::SWG) + LOG("CustomerService",("VeteranRewards: %s traded in %s for SWG account feature Id %lu (%d -> %d)", i->second->getTargetPlayerDescription().c_str(), i->second->getTargetItemDescription().c_str(), i->second->getFeatureId(), i->second->getOldValue(), i->second->getNewValue())); + } + } + + GameConnection * const gc = ConnectionServer::getGameConnection(i->second->getGameServer()); + if (gc) + { + i->second->setSessionResultCode(result, sResultString.c_str(), sResultText.c_str()); + gc->send(*(i->second),true); + } + + delete i->second; + ms_modifyFeatureTrackingNumberMap.erase(i); + } + else + { + DEBUG_REPORT_LOG(true, ("SessionApiClient::OnModifyFeature_v2() - could not find session id for tracking number [%u]\n", trackingNumber)); + } +} + +//------------------------------------------------------------ + +void SessionApiClient::OnRevokeFeature(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Revoke feature.\n")); +} + +//------------------------------------------------------------ + +void SessionApiClient::OnEnumerateFeatures(const apiTrackingNumber trackingNumber, + const apiResult result, + const unsigned featureCount, + const LoginAPI::FeatureDescription featureArray[], + const void * userData) +{ + UNREF(trackingNumber); + UNREF(result); + UNREF(featureCount); + UNREF(featureArray); + UNREF(userData); +// DEBUG_REPORT_LOG(true, ("Enumerate features.\n")); +} + +//------------------------------------------------------------ + +void SessionApiClient::getFeatures(apiAccountId accountId, apiGamecode gameCode, GameNetworkMessage* gnm) +{ + if (!gnm) + return; + + apiTrackingNumber const tn = GetFeatures(accountId, gameCode); + ms_getFeaturesTrackingNumberMap[tn] = gnm; +} + +//------------------------------------------------------------ + +void SessionApiClient::dropClient(const ClientConnection* client, bool forceSessionLogout) +{ + std::map::iterator i = m_validationMap.begin(); + for (;i != m_validationMap.end(); ++i) + { + if (i->second == client) + { + m_validationMap.erase(i); + break; + } + } + + const std::string& sessionId = client->getSessionId(); + if (!sessionId.empty()) + { + std::map::iterator j = ms_sessionIdMap.find(sessionId); + if (j != ms_sessionIdMap.end()) + ms_sessionIdMap.erase(j); + + if (!ConfigConnectionServer::getDisableSessionLogout() || forceSessionLogout) + { + SessionLogout(sessionId.c_str()); + } + } +} + + +//------------------------------------------------------------ + +void SessionApiClient::validateClient (ClientConnection* client, const std::string & key) +{ + + //Key will be the real session key + //We will only use id if we aren't validating. + //The key will provide a username and an id from the station. + + //We call SessionValidate(key, type) on the Session client + //It will callback with OnSessionValidate(trackingNumber, result, account, subscription, userdata); + + //type will be ConfigConnectionServer::getSessionType() + //result is hopefully RESULT_SUCCESS + //acount is struct (name, id, status) where status is ACCOUNT_STATUS_ACTIVE and id is unsigned + //subscription is hopefully SUBSCRIPTION_STATUS_ACTIVE could be trial + //user data is a void* and I think un-used. + + //Then store the client in a map based on the key. + + apiTrackingNumber track = SessionValidate(key.c_str(), static_cast(ConfigConnectionServer::getSessionType())); + //apiTrackingNumber track = SessionConsume(key.c_str(), static_cast(ConfigConnectionServer::getSessionType())); + + //Ok to overwrite old or add new here. + m_validationMap[track] = client; +} + +//------------------------------------------------------------ + +void SessionApiClient::startPlay(const ClientConnection& client) +{ + IGNORE_RETURN(SessionStartPlay(client.getSessionId().c_str(), ConfigConnectionServer::getClusterName(), client.getCharacterName().c_str(), NULL)); +} + +//------------------------------------------------------------ + +void SessionApiClient::stopPlay(const ClientConnection& client) +{ + IGNORE_RETURN(SessionStopPlay(client.getSessionId().c_str(), ConfigConnectionServer::getClusterName(), client.getCharacterName().c_str())); +} + +//------------------------------------------------------------ +void SessionApiClient::OnConnectionOpened(const char * address, unsigned port) +{ + UNREF(address); + UNREF(port); +// DEBUG_REPORT_LOG(true, ("Connection success\n")); +} +void SessionApiClient::OnConnectionClosed(const char * address, unsigned port) +{ + UNREF(address); + UNREF(port); +// DEBUG_REPORT_LOG(true, ("Connection closed.\n")); +} +void SessionApiClient::OnConnectionFailed(const char * address, unsigned port) +{ + UNREF(address); + UNREF(port); +// DEBUG_FATAL(true, ("Connection failed")); +} +void SessionApiClient::OnException() +{ +// DEBUG_REPORT_LOG(true, ("Connection exception.\n")); +} + + +//------------------------------------------------------------------------------------------ + +void SessionApiClient::update() +{ + Process(); + if (m_sessionTimer.updateZero(Clock::frameTime())) + { + std::map::iterator j = ms_sessionIdMap.begin(); + std::vector sessionList; + sessionList.reserve(ms_sessionIdMap.size()); + for (; j != ms_sessionIdMap.end(); ++j) + { + sessionList.push_back(j->first.c_str()); + } + + //SessionTouch all clients. + SessionTouch(&sessionList[0], sessionList.size()); + } +} + +//------------------------------------------------------------------------------------------ + +void SessionApiClient::NotifySessionKickRequest(const apiAccount & account, + const apiSession & session, + const apiKickReason reason) +{ + UNREF(reason); + UNREF(account); + std::map::iterator j = ms_sessionIdMap.find(session.GetId()); + if (j != ms_sessionIdMap.end()) + { + SessionKickReply(session.GetId(), KICK_REPLY_ALLOW); + LOG("ClientDisconnect", ("Client %s Disconnected by session kick request", j->second->getSessionId().c_str())); + j->second->disconnect(); + } + else + { + SessionKickReply(session.GetId(), KICK_REPLY_UNKNOWN_SESSION); + } +} + +//------------------------------------------------------------------------------------------ + +void SessionApiClient::NotifySessionKick(const char ** sessionList, + const unsigned sessionCount) +{ + for (unsigned i = 0; i < sessionCount; ++i) + { + std::map::iterator j = ms_sessionIdMap.find(sessionList[i]); + if (j != ms_sessionIdMap.end()) + { + LOG("ClientDisconnect", ("Client %s Disconnected by notify session kick", j->second->getSessionId().c_str())); + j->second->disconnect(); + } + } +} + +//------------------------------------------------------------------------------------------ + + + diff --git a/engine/server/application/ConnectionServer/src/shared/SessionApiClient.h b/engine/server/application/ConnectionServer/src/shared/SessionApiClient.h new file mode 100644 index 00000000..d6edb922 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/shared/SessionApiClient.h @@ -0,0 +1,146 @@ +// SessionApiClient.h +// copyright 2002 Sony Online Entertainment + +#ifndef _SessionApiClient_H +#define _SessionApiClient_H + +#include +#pragma warning(push) +#pragma warning(disable: 4100) // Client.h has inlined functions with unreferenced formal parameters +#include "Session/LoginAPI/Client.h" +#pragma warning(pop) + +#include "sharedFoundation/Clock.h" +#include "sharedFoundation/Timer.h" + + +class ClientConnection; + +class SessionApiClient : public LoginAPI::Client +{ +public: + + SessionApiClient(const char ** serverList, int serverCount); + ~SessionApiClient(); + + virtual void OnSessionLogin(const apiTrackingNumber trackingNumber, + const apiResult result, + const apiAccount & account, + const apiSubscription & subscription, + const apiSession & session, + const LoginAPI::UsageLimit & usageLimit, + const LoginAPI::Entitlement & entitlement, + void * userData); + + virtual void OnSessionLoginInternal(const apiTrackingNumber trackingNumber, + const apiResult result, + const apiAccount & account, + const apiSubscription & subscription, + const apiSession & session, + const LoginAPI::UsageLimit & usageLimit, + const LoginAPI::Entitlement & entitlement, + void * userData); + + virtual void OnSessionValidate(const apiTrackingNumber trackingNumber, + const apiResult result, + const apiAccount & account, + const apiSubscription & subscription, + const apiSession & session, + const LoginAPI::UsageLimit & usageLimit, + const LoginAPI::Entitlement & entitlement, + void * userData); + + virtual void OnSessionConsume(const apiTrackingNumber trackingNumber, + const apiResult result, + const apiAccount & account, + const apiSubscription & subscription, + const apiSession & session, + const LoginAPI::UsageLimit & usageLimit, + const LoginAPI::Entitlement & entitlement, + void * userData); + + virtual void OnSessionStartPlay(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData); + + virtual void OnSessionStopPlay(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData); + + virtual void OnSessionKick(const unsigned trackingNumber, + const apiResult result, + void * userData); + + virtual void OnGetSessions(const apiTrackingNumber trackingNumber, + const apiResult result, + const unsigned count, + const apiSession session[], + const apiSubscription subscription[], + const LoginAPI::UsageLimit usageLimit[], + const unsigned timeCreated[], + const unsigned timeTouched[], + void * userData); + + virtual void OnGetFeatures(const apiTrackingNumber trackingNumber, + const apiResult result, + const unsigned featureCount, + const LoginAPI::Feature featureArray[], + const void * userData = 0); + + virtual void OnGrantFeature(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData); + + virtual void OnGrantFeatureByStationID(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData); + + virtual void OnModifyFeature(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData); + + virtual void OnModifyFeature_v2(const apiTrackingNumber trackingNumber, + const apiResult result, + const LoginAPI::Feature & currentFeature, + void * userData); + + virtual void OnRevokeFeature(const apiTrackingNumber trackingNumber, + const apiResult result, + void * userData); + + virtual void OnEnumerateFeatures(const apiTrackingNumber trackingNumber, + const apiResult result, + const unsigned featureCount, + const LoginAPI::FeatureDescription featureArray[], + const void * userData = 0); + + virtual void OnConnectionOpened(const char * address, unsigned port); + virtual void OnConnectionClosed(const char * address, unsigned port); + virtual void OnConnectionFailed(const char * address, unsigned port); + virtual void OnException(); + + virtual void NotifySessionKickRequest(const apiAccount & account, + const apiSession & session, + const apiKickReason reason); + + virtual void NotifySessionKick(const char ** sessionList, + const unsigned sessionCount); + + void getFeatures(apiAccountId accountId, apiGamecode gameCode, GameNetworkMessage* gnm); + void dropClient(const ClientConnection* client, bool forceSessionLogout); + void validateClient(ClientConnection* client, const std::string & key); + void startPlay(const ClientConnection& client); + void stopPlay(const ClientConnection& client); + void update(); + + static void FlushSessionQueue(); + +private: + + static std::map m_validationMap; + SessionApiClient(); + Timer m_sessionTimer; +}; + + +#endif diff --git a/engine/server/application/ConnectionServer/src/win32/FirstConnectionServer.cpp b/engine/server/application/ConnectionServer/src/win32/FirstConnectionServer.cpp new file mode 100644 index 00000000..137cad41 --- /dev/null +++ b/engine/server/application/ConnectionServer/src/win32/FirstConnectionServer.cpp @@ -0,0 +1 @@ +#include "FirstConnectionServer.h" diff --git a/engine/server/application/ConnectionServer/src/win32/WinMain.cpp b/engine/server/application/ConnectionServer/src/win32/WinMain.cpp new file mode 100644 index 00000000..236a8ebf --- /dev/null +++ b/engine/server/application/ConnectionServer/src/win32/WinMain.cpp @@ -0,0 +1,58 @@ +#include "FirstConnectionServer.h" +#include "ConfigConnectionServer.h" +#include "ConnectionServer.h" + +#include "sharedCompression/SetupSharedCompression.h" +#include "sharedDebug/SetupSharedDebug.h" +#include "sharedFile/SetupSharedFile.h" +#include "sharedFoundation/PerThreadData.h" +#include "sharedFoundation/SetupSharedFoundation.h" +#include "sharedNetworkMessages/SetupSharedNetworkMessages.h" +#include "sharedRandom/SetupSharedRandom.h" +#include "sharedThread/SetupSharedThread.h" + +#include + +int main(int argc, char ** argv) +{ + // command line hack + std::string cmdLine; + for(int i = 1; i < argc; ++i) + { + cmdLine += argv[i]; + if(i + 1 < argc) + { + cmdLine += " "; + } + } + + SetupSharedThread::install(); + SetupSharedDebug::install(1024); + +//-- setup foundation + SetupSharedFoundation::Data setupFoundationData(SetupSharedFoundation::Data::D_game); + +// setupFoundationData.hInstance = hInstance; + setupFoundationData.commandLine = cmdLine.c_str(); + setupFoundationData.createWindow = false; + setupFoundationData.clockUsesSleep = true; + + SetupSharedFoundation::install (setupFoundationData); + SetupSharedFile::install(false); + SetupSharedCompression::install(); + + SetupSharedNetworkMessages::install(); + SetupSharedRandom::install(int(time(NULL))); + + //-- setup game server + ConfigConnectionServer::install (); + + ConnectionServer::install(); + //-- run game + SetupSharedFoundation::callbackWithExceptionHandling(ConnectionServer::run); + ConnectionServer::remove(); + ConfigConnectionServer::remove(); + SetupSharedFoundation::remove(); + PerThreadData::threadRemove(); + return 0; +}