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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user