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:
lotodore
2011-08-28 17:14:03 +00:00
parent 0a49783889
commit 32ba77709e
18 changed files with 195 additions and 24 deletions
+9
View File
@@ -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
+3
View File
@@ -206,6 +206,9 @@ protected:
bool IsSynchronized() const;
void ReadSessionGuidFromFile();
void WriteSessionGuidToFile() const;
private:
boost::shared_ptr<boost::asio::io_service> m_ioService;
+11
View File
@@ -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.
+33 -1
View File
@@ -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());
}
}
+11
View File
@@ -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
{
+52 -20
View File
@@ -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;
}
+2
View File
@@ -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;
+2
View File
@@ -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;