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.
This commit is contained in:
+1
-1
@@ -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 *
|
||||
|
||||
@@ -78,6 +78,7 @@ Game::Game(GuiInterface* gui, boost::shared_ptr<EngineFactory> 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<EngineFactory> 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<PlayerInterface> 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<PlayerInterface> Game::getCurrentPlayer()
|
||||
return tmpPlayer;
|
||||
}
|
||||
|
||||
boost::shared_ptr<PlayerInterface> Game::getPlayerByName(const std::string &name)
|
||||
{
|
||||
boost::shared_ptr<PlayerInterface> 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()
|
||||
{
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ public:
|
||||
}
|
||||
|
||||
boost::shared_ptr<PlayerInterface> getPlayerByUniqueId(unsigned id);
|
||||
boost::shared_ptr<PlayerInterface> getPlayerByName(const std::string &name);
|
||||
boost::shared_ptr<PlayerInterface> getCurrentPlayer();
|
||||
|
||||
void raiseBlinds();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<SessionData> m_sessionData;
|
||||
boost::shared_ptr<boost::asio::ip::tcp::resolver> m_resolver;
|
||||
@@ -137,6 +145,7 @@ private:
|
||||
std::string m_cacheDir;
|
||||
bool m_hasSubscribedLobbyMsg;
|
||||
ReceiveBuffer m_receiveBuffer;
|
||||
std::string m_sessionGuid;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -206,6 +206,9 @@ protected:
|
||||
|
||||
bool IsSynchronized() const;
|
||||
|
||||
void ReadSessionGuidFromFile();
|
||||
void WriteSessionGuidToFile() const;
|
||||
|
||||
private:
|
||||
|
||||
boost::shared_ptr<boost::asio::io_service> m_ioService;
|
||||
|
||||
@@ -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<ClientThread> 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.
|
||||
|
||||
@@ -35,11 +35,14 @@
|
||||
|
||||
#include <boost/lambda/lambda.hpp>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <gsasl.h>
|
||||
|
||||
#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<EngineFactory> 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<char> 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -562,6 +562,17 @@ ServerGame::IsClientAddressConnected(const std::string &clientAddress) const
|
||||
return GetSessionManager().IsClientAddressConnected(clientAddress);
|
||||
}
|
||||
|
||||
boost::shared_ptr<PlayerInterface>
|
||||
ServerGame::GetPlayerInterfaceFromGame(const std::string &playerName)
|
||||
{
|
||||
boost::shared_ptr<PlayerInterface> tmpPlayer;
|
||||
if (m_game)
|
||||
{
|
||||
tmpPlayer = m_game->getPlayerByName(playerName);
|
||||
}
|
||||
return tmpPlayer;
|
||||
}
|
||||
|
||||
bool
|
||||
ServerGame::IsRunning() const
|
||||
{
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <core/loghelper.h>
|
||||
#include <core/openssl_wrapper.h>
|
||||
#include <configfile.h>
|
||||
#include <playerinterface.h>
|
||||
#include <log.h>
|
||||
|
||||
#include <fstream>
|
||||
@@ -225,16 +226,6 @@ ServerLobbyThread::SignalTermination()
|
||||
void
|
||||
ServerLobbyThread::AddConnection(boost::shared_ptr<tcp::socket> 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> 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<ServerGame> tmpGame = i->second;
|
||||
boost::shared_ptr<PlayerInterface> 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<SessionData> 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<SessionData> 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<SessionData> 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<SessionData> 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<NetPacket> 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<ServerGame> tmpGame = i->second;
|
||||
if (tmpGame->GetPlayerDataByUniqueId(playerId).get()) {
|
||||
if (tmpGame->GetPlayerDataByUniqueId(playerId)) {
|
||||
tmpGame->RemovePlayer(playerId, errorCode);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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<PlayerInterface> GetPlayerInterfaceFromGame(const std::string &playerName);
|
||||
|
||||
bool IsRunning() const;
|
||||
|
||||
|
||||
@@ -110,6 +110,8 @@ public:
|
||||
boost::shared_ptr<ServerDBInterface> GetDatabase();
|
||||
ServerBanManager &GetBanManager();
|
||||
|
||||
u_int32_t GetRejoinGameIdForPlayer(const std::string &playerName, const std::string &guid, unsigned &outPlayerUniqueId);
|
||||
|
||||
protected:
|
||||
|
||||
typedef std::deque<boost::shared_ptr<boost::asio::ip::tcp::socket> > ConnectQueue;
|
||||
|
||||
+15
-1
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
+3
-1
@@ -112,7 +112,9 @@ void Session::startLocalGame(const GameData &gameData, const StartData &startDat
|
||||
|
||||
//PlayerData erzeugen
|
||||
// UniqueId = PlayerNumber for local games.
|
||||
boost::shared_ptr<PlayerData> playerData(new PlayerData(i, i,
|
||||
boost::shared_ptr<PlayerData> playerData(new PlayerData(
|
||||
i,
|
||||
i,
|
||||
i == 0 ? PLAYER_TYPE_HUMAN : PLAYER_TYPE_COMPUTER,
|
||||
PLAYER_RIGHTS_NORMAL,
|
||||
i == 0));
|
||||
|
||||
Reference in New Issue
Block a user