From 32ba77709e804493b532afbb761bf91c1130f0dd Mon Sep 17 00:00:00 2001 From: lotodore Date: Sun, 28 Aug 2011 17:14:03 +0000 Subject: [PATCH] More work on rejoin: The server now detects when a rejoin is possible and allows the player to login directly, without waiting for a possible existing connection to be terminated. A session GUID is stored in the cache directory on client side for this purpose. --- docs/pokerth.asn1 | 2 +- src/engine/game.cpp | 18 ++++++ src/engine/game.h | 1 + src/engine/local_engine/localplayer.h | 10 +++ src/engine/network_engine/clientplayer.cpp | 14 +++++ src/engine/network_engine/clientplayer.h | 3 + src/engine/playerinterface.h | 4 ++ src/net/clientcontext.h | 9 +++ src/net/clientthread.h | 3 + src/net/common/clientstate.cpp | 11 ++++ src/net/common/clientthread.cpp | 34 +++++++++- src/net/common/servergame.cpp | 11 ++++ src/net/common/serverlobbythread.cpp | 72 ++++++++++++++++------ src/net/servergame.h | 2 + src/net/serverlobbythread.h | 2 + src/playerdata.cpp | 16 ++++- src/playerdata.h | 3 + src/session.cpp | 4 +- 18 files changed, 195 insertions(+), 24 deletions(-) diff --git a/docs/pokerth.asn1 b/docs/pokerth.asn1 index c09a2bae..3b537fb5 100644 --- a/docs/pokerth.asn1 +++ b/docs/pokerth.asn1 @@ -1,5 +1,5 @@ --************************************************************************** --- Copyright (C) 2009-2010 by Lothar May * +-- Copyright (C) 2009-2011 by Lothar May * -- * -- This program is free software; you can redistribute it and/or modify * -- it under the terms of the GNU General Public License as published by * diff --git a/src/engine/game.cpp b/src/engine/game.cpp index ea7b7ddd..46313dac 100755 --- a/src/engine/game.cpp +++ b/src/engine/game.cpp @@ -78,6 +78,7 @@ Game::Game(GuiInterface* gui, boost::shared_ptr factory, string myName; string myAvatarFile; + string myGuid; unsigned uniqueId = 0; PlayerType type = PLAYER_TYPE_COMPUTER; @@ -86,12 +87,14 @@ Game::Game(GuiInterface* gui, boost::shared_ptr factory, type = (*player_i)->GetType(); myName = (*player_i)->GetName(); myAvatarFile = (*player_i)->GetAvatarFile(); + myGuid = (*player_i)->GetGuid(); ++player_i; } // create player objects boost::shared_ptr tmpPlayer = myFactory->createPlayer(i, uniqueId, type, myName, myAvatarFile, startCash, startQuantityPlayers > i, 0); tmpPlayer->setIsConnected(true); + tmpPlayer->setMyGuid(myGuid); // fill player lists seatsList->push_back(tmpPlayer); @@ -219,6 +222,21 @@ boost::shared_ptr Game::getCurrentPlayer() return tmpPlayer; } +boost::shared_ptr Game::getPlayerByName(const std::string &name) +{ + boost::shared_ptr tmpPlayer; + PlayerListIterator i = getSeatsList()->begin(); + PlayerListIterator end = getSeatsList()->end(); + while (i != end) { + if ((*i)->getMyName() == name) { + tmpPlayer = *i; + break; + } + ++i; + } + return tmpPlayer; +} + void Game::raiseBlinds() { diff --git a/src/engine/game.h b/src/engine/game.h index 2bf2655c..26ed3917 100755 --- a/src/engine/game.h +++ b/src/engine/game.h @@ -104,6 +104,7 @@ public: } boost::shared_ptr getPlayerByUniqueId(unsigned id); + boost::shared_ptr getPlayerByName(const std::string &name); boost::shared_ptr getCurrentPlayer(); void raiseBlinds(); diff --git a/src/engine/local_engine/localplayer.h b/src/engine/local_engine/localplayer.h index bfde631a..452543aa 100755 --- a/src/engine/local_engine/localplayer.h +++ b/src/engine/local_engine/localplayer.h @@ -43,6 +43,15 @@ public: unsigned getMyUniqueID() const { return myUniqueID; } + + void setMyGuid(const std::string &theValue) { + myGuid = theValue; + } + + std::string getMyGuid() const { + return myGuid; + } + PlayerType getMyType() const { return myType; } @@ -290,6 +299,7 @@ private: // Konstanten int myID; unsigned myUniqueID; + std::string myGuid; PlayerType myType; std::string myName; std::string myAvatar; diff --git a/src/engine/network_engine/clientplayer.cpp b/src/engine/network_engine/clientplayer.cpp index eaf5c188..d02b0881 100644 --- a/src/engine/network_engine/clientplayer.cpp +++ b/src/engine/network_engine/clientplayer.cpp @@ -60,9 +60,23 @@ ClientPlayer::getMyUniqueID() const return myUniqueID; } +void +ClientPlayer::setMyGuid(const std::string &theValue) +{ + myGuid = theValue; +} + +std::string +ClientPlayer::getMyGuid() const +{ + boost::recursive_mutex::scoped_lock lock(m_syncMutex); + return myGuid; +} + PlayerType ClientPlayer::getMyType() const { + boost::recursive_mutex::scoped_lock lock(m_syncMutex); return myType; } diff --git a/src/engine/network_engine/clientplayer.h b/src/engine/network_engine/clientplayer.h index 89116fca..79f4ed2d 100644 --- a/src/engine/network_engine/clientplayer.h +++ b/src/engine/network_engine/clientplayer.h @@ -38,6 +38,8 @@ public: int getMyID() const; unsigned getMyUniqueID() const; + void setMyGuid(const std::string &theValue); + std::string getMyGuid() const; PlayerType getMyType() const; void setMyDude(int theValue); @@ -151,6 +153,7 @@ private: // Konstanten const int myID; const unsigned myUniqueID; + std::string myGuid; const PlayerType myType; std::string myName; std::string myAvatar; diff --git a/src/engine/playerinterface.h b/src/engine/playerinterface.h index c3e195fe..93345ddf 100644 --- a/src/engine/playerinterface.h +++ b/src/engine/playerinterface.h @@ -34,6 +34,10 @@ public: virtual int getMyID() const =0; virtual unsigned getMyUniqueID() const =0; + + virtual void setMyGuid(const std::string &theValue) =0; + virtual std::string getMyGuid() const =0; + virtual PlayerType getMyType() const =0; virtual void setMyDude(int theValue) =0; diff --git a/src/net/clientcontext.h b/src/net/clientcontext.h index 560af431..282d2616 100644 --- a/src/net/clientcontext.h +++ b/src/net/clientcontext.h @@ -120,6 +120,14 @@ public: return m_receiveBuffer; } + const std::string &GetSessionGuid() const { + return m_sessionGuid; + } + + void SetSessionGuid(const std::string &sessionGuid) { + m_sessionGuid = sessionGuid; + } + private: boost::shared_ptr m_sessionData; boost::shared_ptr m_resolver; @@ -137,6 +145,7 @@ private: std::string m_cacheDir; bool m_hasSubscribedLobbyMsg; ReceiveBuffer m_receiveBuffer; + std::string m_sessionGuid; }; #endif diff --git a/src/net/clientthread.h b/src/net/clientthread.h index 0d47d04b..2b8e65a8 100644 --- a/src/net/clientthread.h +++ b/src/net/clientthread.h @@ -206,6 +206,9 @@ protected: bool IsSynchronized() const; + void ReadSessionGuidFromFile(); + void WriteSessionGuidToFile() const; + private: boost::shared_ptr m_ioService; diff --git a/src/net/common/clientstate.cpp b/src/net/common/clientstate.cpp index 1d8eb468..f1318389 100644 --- a/src/net/common/clientstate.cpp +++ b/src/net/common/clientstate.cpp @@ -983,6 +983,14 @@ ClientStateWaitEnterLogin::TimerLoop(const boost::system::error_code& ec, boost: InitMessage_t *netInit = &init->GetMsg()->choice.initMessage; netInit->requestedVersion.major = NET_VERSION_MAJOR; netInit->requestedVersion.minor = NET_VERSION_MINOR; + if (!context.GetSessionGuid().empty()) + { + netInit->myLastSessionId = + OCTET_STRING_new_fromBuf( + &asn_DEF_OCTET_STRING, + context.GetSessionGuid().c_str(), + (int)context.GetSessionGuid().length()); + } context.SetPlayerName(loginData.userName); @@ -1177,8 +1185,11 @@ ClientStateWaitSession::InternalHandlePacket(boost::shared_ptr cli InitAckMessage_t *netInitAck = &tmpPacket->GetMsg()->choice.initAckMessage; client->SetGuiPlayerId(netInitAck->yourPlayerId); + client->GetContext().SetSessionGuid(STL_STRING_FROM_OCTET_STRING(netInitAck->yourSessionId)); client->SetSessionEstablished(true); client->GetCallback().SignalNetClientConnect(MSG_SOCK_SESSION_DONE); + if (netInitAck->rejoinGameId) + client->GetCallback().SignalNetClientRejoinPossible(*netInitAck->rejoinGameId); client->SetState(ClientStateWaitJoin::Instance()); } else if (tmpPacket->GetMsg()->present == PokerTHMessage_PR_avatarRequestMessage) { // Before letting us join the lobby, the server requests our avatar. diff --git a/src/net/common/clientthread.cpp b/src/net/common/clientthread.cpp index ccc33fbe..0b24a334 100644 --- a/src/net/common/clientthread.cpp +++ b/src/net/common/clientthread.cpp @@ -35,11 +35,14 @@ #include #include +#include #include #include #include -#define TEMP_AVATAR_FILENAME "avatar.tmp" +#define TEMP_AVATAR_FILENAME "avatar.tmp" +#define TEMP_GUID_FILENAME "guid.tmp" +#define CLIENT_GUID_SIZE 16 #define CLIENT_AVATAR_LOOP_MSEC 100 #define CLIENT_SEND_LOOP_MSEC 50 @@ -86,6 +89,8 @@ ClientThread::Init( context.SetPlayerName(playerName); context.SetAvatarFile(avatarFile); context.SetCacheDir(cacheDir); + + ReadSessionGuidFromFile(); } void @@ -567,6 +572,9 @@ ClientThread::ClearAuthContext() void ClientThread::InitGame() { + // Store current session guid, in case we need to rejoin the game. + WriteSessionGuidToFile(); + // EngineFactory erstellen boost::shared_ptr factory(new ClientEngineFactory); // LocalEngine erstellen @@ -1398,3 +1406,27 @@ ClientThread::IsSynchronized() const return m_playerInfoRequestList.empty(); } +void +ClientThread::ReadSessionGuidFromFile() +{ + string guidFileName(GetContext().GetCacheDir() + TEMP_GUID_FILENAME); + ifstream guidStream(guidFileName.c_str(), ios::in | ios::binary); + if (guidStream.good()) + { + std::vector tmpGuid(CLIENT_GUID_SIZE); + guidStream.read(&tmpGuid[0], CLIENT_GUID_SIZE); + GetContext().SetSessionGuid(string(tmpGuid.begin(), tmpGuid.end())); + } +} + +void +ClientThread::WriteSessionGuidToFile() const +{ + string guidFileName(GetContext().GetCacheDir() + TEMP_GUID_FILENAME); + ofstream guidStream(guidFileName.c_str(), ios::out | ios::trunc | ios::binary); + if (guidStream.good()) + { + guidStream.write(GetContext().GetSessionGuid().c_str(), GetContext().GetSessionGuid().size()); + } +} + diff --git a/src/net/common/servergame.cpp b/src/net/common/servergame.cpp index a3a9b3a6..a21c6199 100644 --- a/src/net/common/servergame.cpp +++ b/src/net/common/servergame.cpp @@ -562,6 +562,17 @@ ServerGame::IsClientAddressConnected(const std::string &clientAddress) const return GetSessionManager().IsClientAddressConnected(clientAddress); } +boost::shared_ptr +ServerGame::GetPlayerInterfaceFromGame(const std::string &playerName) +{ + boost::shared_ptr tmpPlayer; + if (m_game) + { + tmpPlayer = m_game->getPlayerByName(playerName); + } + return tmpPlayer; +} + bool ServerGame::IsRunning() const { diff --git a/src/net/common/serverlobbythread.cpp b/src/net/common/serverlobbythread.cpp index 7a247e67..a65edd53 100644 --- a/src/net/common/serverlobbythread.cpp +++ b/src/net/common/serverlobbythread.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -225,16 +226,6 @@ ServerLobbyThread::SignalTermination() void ServerLobbyThread::AddConnection(boost::shared_ptr sock) { - // Create a random session id. - // This id can be used to reconnect to the server if the connection was lost. - //unsigned sessionId; - - // TODO: use randomized method. - //if(!RAND_bytes((unsigned char *)&sessionId, sizeof(sessionId))) - //{ - // RAND_pseudo_bytes((unsigned char *)&sessionId, sizeof(sessionId)); - //} - // Create a new session. boost::shared_ptr sessionData(new SessionData(sock, m_curSessionId++, *m_internalServerCallback)); m_sessionManager.AddSession(sessionData); @@ -711,6 +702,26 @@ ServerLobbyThread::GetBanManager() return *m_banManager; } +u_int32_t +ServerLobbyThread::GetRejoinGameIdForPlayer(const std::string &playerName, const std::string &guid, unsigned &outPlayerUniqueId) +{ + u_int32_t retGameId = 0; + GameMap::iterator i = m_gameMap.begin(); + GameMap::iterator end = m_gameMap.end(); + while (i != end) { + boost::shared_ptr tmpGame = i->second; + boost::shared_ptr tmpPlayer = tmpGame->GetPlayerInterfaceFromGame(playerName); + if (tmpPlayer && tmpPlayer->getMyGuid() == guid) + { + retGameId = tmpGame->GetId(); + outPlayerUniqueId = tmpPlayer->getMyUniqueID(); + break; + } + ++i; + } + return retGameId; +} + u_int32_t ServerLobbyThread::GetNextUniquePlayerId() { @@ -1025,12 +1036,6 @@ ServerLobbyThread::HandleNetPacketInit(boost::shared_ptr session, c return; } - // Check whether this player is already connected. - if (IsPlayerConnected(playerName)) { - SessionError(session, ERR_NET_PLAYER_NAME_IN_USE); - return; - } - // Check whether the player name is banned. if (GetBanManager().IsPlayerBanned(playerName)) { SessionError(session, ERR_NET_PLAYER_BANNED); @@ -1047,6 +1052,10 @@ ServerLobbyThread::HandleNetPacketInit(boost::shared_ptr session, c new PlayerData(GetNextUniquePlayerId(), 0, PLAYER_TYPE_HUMAN, validGuest ? PLAYER_RIGHTS_GUEST : PLAYER_RIGHTS_NORMAL, false)); tmpPlayerData->SetName(playerName); tmpPlayerData->SetAvatarMD5(avatarMD5); + if (initMessage.myLastSessionId) + { + tmpPlayerData->SetGuid(STL_STRING_FROM_OCTET_STRING(*initMessage.myLastSessionId)); + } // Set player data for session. m_sessionManager.SetSessionPlayerData(session->GetId(), tmpPlayerData); @@ -1483,6 +1492,21 @@ ServerLobbyThread::EstablishSession(boost::shared_ptr session) if (!session->GetPlayerData()) throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_SESSION, 0); + u_int32_t rejoinPlayerId = 0; + u_int32_t rejoinGameId = GetRejoinGameIdForPlayer(session->GetPlayerData()->GetName(), session->GetPlayerData()->GetGuid(), rejoinPlayerId); + if (rejoinGameId != 0) + { + // Offer rejoin, and disconnect current player with the same name. + InternalRemovePlayer(rejoinPlayerId, ERR_NET_PLAYER_NAME_IN_USE); + } + + // Check whether this player is already connected. + // We need to enforce this here to prevent duplicates. + if (IsPlayerConnected(session->GetPlayerData()->GetName())) { + SessionError(session, ERR_NET_PLAYER_KICKED); + return; + } + // Run postlogin for DB string tmpAvatarHash; string tmpAvatarType; @@ -1494,16 +1518,24 @@ ServerLobbyThread::EstablishSession(boost::shared_ptr session) } m_database->PlayerPostLogin(session->GetPlayerData()->GetDBId(), tmpAvatarHash, tmpAvatarType); + // Generate a new GUID. + boost::uuids::uuid sessionGuid(m_sessionIdGenerator()); + session->GetPlayerData()->SetGuid(string((char *)&sessionGuid, boost::uuids::uuid::static_size())); + // Send ACK to client. boost::shared_ptr ack(new NetPacket(NetPacket::Alloc)); ack->GetMsg()->present = PokerTHMessage_PR_initAckMessage; InitAckMessage_t *netInitAck = &ack->GetMsg()->choice.initAckMessage; - boost::uuids::uuid sessionId(m_sessionIdGenerator()); OCTET_STRING_fromBuf( &netInitAck->yourSessionId, - (char *)&sessionId, - (int)boost::uuids::uuid::static_size()); + session->GetPlayerData()->GetGuid().c_str(), + session->GetPlayerData()->GetGuid().size()); netInitAck->yourPlayerId = session->GetPlayerData()->GetUniqueId(); + if (rejoinGameId != 0) + { + netInitAck->rejoinGameId = (NonZeroId_t *)calloc(1, sizeof(NonZeroId_t)); + *netInitAck->rejoinGameId = rejoinGameId; + } GetSender().Send(session, ack); // Send the connected players list to the client. @@ -1782,7 +1814,7 @@ ServerLobbyThread::InternalRemovePlayer(unsigned playerId, unsigned errorCode) while (i != end) { boost::shared_ptr tmpGame = i->second; - if (tmpGame->GetPlayerDataByUniqueId(playerId).get()) { + if (tmpGame->GetPlayerDataByUniqueId(playerId)) { tmpGame->RemovePlayer(playerId, errorCode); break; } diff --git a/src/net/servergame.h b/src/net/servergame.h index 7b14ca0f..75a000e1 100644 --- a/src/net/servergame.h +++ b/src/net/servergame.h @@ -33,6 +33,7 @@ class ServerLobbyThread; class ServerGameState; class ServerDBInterface; +class PlayerInterface; class ConfigFile; struct GameData; class Game; @@ -73,6 +74,7 @@ public: bool IsPlayerConnected(const std::string &name) const; bool IsPlayerConnected(unsigned playerId) const; bool IsClientAddressConnected(const std::string &clientAddress) const; + boost::shared_ptr GetPlayerInterfaceFromGame(const std::string &playerName); bool IsRunning() const; diff --git a/src/net/serverlobbythread.h b/src/net/serverlobbythread.h index 4bf62e79..633af9a5 100644 --- a/src/net/serverlobbythread.h +++ b/src/net/serverlobbythread.h @@ -110,6 +110,8 @@ public: boost::shared_ptr GetDatabase(); ServerBanManager &GetBanManager(); + u_int32_t GetRejoinGameIdForPlayer(const std::string &playerName, const std::string &guid, unsigned &outPlayerUniqueId); + protected: typedef std::deque > ConnectQueue; diff --git a/src/playerdata.cpp b/src/playerdata.cpp index 9abc38eb..d586a47b 100644 --- a/src/playerdata.cpp +++ b/src/playerdata.cpp @@ -26,7 +26,7 @@ PlayerData::PlayerData(unsigned uniqueId, int number, PlayerType type, PlayerRig } PlayerData::PlayerData(const PlayerData &other) - : m_uniqueId(other.GetUniqueId()), m_dbId(other.GetDBId()), m_number(other.GetNumber()), m_name(other.GetName()), + : m_uniqueId(other.GetUniqueId()), m_dbId(other.GetDBId()), m_number(other.GetNumber()), m_guid(other.GetGuid()), m_name(other.GetName()), m_password(), m_country(other.GetCountry()), m_avatarFile(other.GetAvatarFile()), m_avatarMD5(other.GetAvatarMD5()), m_type(other.GetType()), m_rights(other.GetRights()), m_isGameAdmin(other.IsGameAdmin()), m_netAvatarFile(), m_dataMutex() @@ -160,6 +160,20 @@ PlayerData::SetNumber(int number) m_number = number; } +std::string +PlayerData::GetGuid() const +{ + boost::mutex::scoped_lock lock(m_dataMutex); + return m_guid; +} + +void +PlayerData::SetGuid(const std::string &guid) +{ + boost::mutex::scoped_lock lock(m_dataMutex); + m_guid = guid; +} + DB_id PlayerData::GetDBId() const { diff --git a/src/playerdata.h b/src/playerdata.h index a70a4e20..8c2fe1fd 100644 --- a/src/playerdata.h +++ b/src/playerdata.h @@ -92,6 +92,8 @@ public: unsigned GetUniqueId() const; int GetNumber() const; void SetNumber(int number); + std::string GetGuid() const; + void SetGuid(const std::string &guid); DB_id GetDBId() const; void SetDBId(DB_id id); @@ -101,6 +103,7 @@ private: const unsigned m_uniqueId; DB_id m_dbId; int m_number; + std::string m_guid; std::string m_name; std::string m_password; std::string m_country; diff --git a/src/session.cpp b/src/session.cpp index c4b4383b..10652793 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -112,7 +112,9 @@ void Session::startLocalGame(const GameData &gameData, const StartData &startDat //PlayerData erzeugen // UniqueId = PlayerNumber for local games. - boost::shared_ptr playerData(new PlayerData(i, i, + boost::shared_ptr playerData(new PlayerData( + i, + i, i == 0 ? PLAYER_TYPE_HUMAN : PLAYER_TYPE_COMPUTER, PLAYER_RIGHTS_NORMAL, i == 0));