First test version with network timeout. Only activity timeout and global timeout, no game admin timeout yet. Set to test values (will disconnect rapidly).
Modified method for init timeout (if session cannot be established) - now using one non-modifiable timer for each session, and an additional timer which is reset whenever session data is received.
This commit is contained in:
@@ -39,6 +39,7 @@ public:
|
||||
virtual void SignalNetClientError(int errorID, int osErrorID) = 0;
|
||||
virtual void SignalNetClientNotification(int notificationId) = 0;
|
||||
virtual void SignalNetClientStatsUpdate(const ServerStats &stats) = 0;
|
||||
virtual void SignalNetClientShowTimeoutDialog(NetTimeoutReason reason, unsigned remainingSec) = 0;
|
||||
virtual void SignalNetClientRemovedFromGame(int notificationId) = 0;
|
||||
|
||||
virtual void SignalNetClientGameListNew(unsigned gameId) = 0;
|
||||
@@ -57,6 +58,6 @@ public:
|
||||
|
||||
virtual void SignalNetClientChatMsg(const std::string &playerName, const std::string &msg) = 0;
|
||||
virtual void SignalNetClientWaitDialog() = 0;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
void SendJoinFirstGame(const std::string &password);
|
||||
void SendJoinGame(unsigned gameId, const std::string &password);
|
||||
void SendCreateGame(const GameData &gameData, const std::string &name, const std::string &password);
|
||||
void SendResetTimeout();
|
||||
|
||||
GameInfo GetGameInfo(unsigned gameId) const;
|
||||
PlayerInfo GetPlayerInfo(unsigned playerId) const;
|
||||
|
||||
@@ -417,6 +417,12 @@ AbstractClientStateReceiving::Process(ClientThread &client)
|
||||
client.GetCallback().SignalNetClientRemovedFromGame(removedData.removeReason);
|
||||
client.SetState(ClientStateWaitJoin::Instance());
|
||||
}
|
||||
else if (tmpPacket->ToNetPacketTimeoutWarning())
|
||||
{
|
||||
NetPacketTimeoutWarning::Data warningData;
|
||||
tmpPacket->ToNetPacketTimeoutWarning()->GetData(warningData);
|
||||
client.GetCallback().SignalNetClientShowTimeoutDialog(warningData.timeoutReason, warningData.remainingSeconds);
|
||||
}
|
||||
else if (tmpPacket->ToNetPacketChatText())
|
||||
{
|
||||
// Chat message - display it in the GUI.
|
||||
|
||||
@@ -241,6 +241,14 @@ ClientThread::SendCreateGame(const GameData &gameData, const std::string &name,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClientThread::SendResetTimeout()
|
||||
{
|
||||
boost::shared_ptr<NetPacket> reset(new NetPacketResetTimeout);
|
||||
boost::mutex::scoped_lock lock(m_outPacketListMutex);
|
||||
m_outPacketList.push_back(reset);
|
||||
}
|
||||
|
||||
GameInfo
|
||||
ClientThread::GetGameInfo(unsigned gameId) const
|
||||
{
|
||||
|
||||
@@ -4354,6 +4354,7 @@ NetPacketTimeoutWarning::SetData(const NetPacketTimeoutWarning::Data &inData)
|
||||
tmpData->timeoutReason = htons(NET_TIMEOUT_OTHER_REASON);
|
||||
break;
|
||||
}
|
||||
tmpData->remainingSeconds = htons(inData.remainingSeconds);
|
||||
|
||||
// Check the packet - just in case.
|
||||
Check(GetRawData());
|
||||
@@ -4374,6 +4375,7 @@ NetPacketTimeoutWarning::GetData(NetPacketTimeoutWarning::Data &outData) const
|
||||
outData.timeoutReason = NETWORK_TIMEOUT_GENERIC;
|
||||
break;
|
||||
}
|
||||
outData.remainingSeconds = ntohs(tmpData->remainingSeconds);
|
||||
}
|
||||
|
||||
const NetPacketTimeoutWarning *
|
||||
|
||||
@@ -175,6 +175,8 @@ AbstractServerGameStateReceiving::Process(ServerGameThread &server)
|
||||
// Delegate to Lobby.
|
||||
server.GetLobbyThread().HandleGameRetrieveAvatar(session, *packet->ToNetPacketRetrieveAvatar());
|
||||
}
|
||||
else if (packet->ToNetPacketResetTimeout())
|
||||
{}
|
||||
else if (packet->ToNetPacketLeaveCurrentGame())
|
||||
{
|
||||
server.MoveSessionToLobby(session, NTF_NET_REMOVED_ON_REQUEST);
|
||||
|
||||
@@ -34,11 +34,15 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#define SERVER_MAX_NUM_SESSIONS 512 // Maximum number of idle users in lobby.
|
||||
#define SERVER_CACHE_CLEANUP_INTERVAL_SEC 86400 // 1 day
|
||||
#define SERVER_SAVE_STATISTICS_INTERVAL_SEC 60
|
||||
#define SERVER_INIT_SESSION_TIMEOUT_SEC 20
|
||||
#define SERVER_INIT_AVATAR_CLIENT_LOCK_SEC 30 // Forbid a client to send an additional avatar.
|
||||
#define SERVER_MAX_NUM_SESSIONS 512 // Maximum number of idle users in lobby.
|
||||
#define SERVER_CACHE_CLEANUP_INTERVAL_SEC 86400 // 1 day
|
||||
#define SERVER_SAVE_STATISTICS_INTERVAL_SEC 60
|
||||
#define SERVER_INIT_AVATAR_CLIENT_LOCK_SEC 30 // Forbid a client to send an additional avatar.
|
||||
|
||||
#define SERVER_INIT_SESSION_TIMEOUT_SEC 20
|
||||
#define SERVER_TIMEOUT_WARNING_REMAINING_SEC 60
|
||||
#define SERVER_SESSION_ACTIVITY_TIMEOUT_SEC 90 // MUST be > SERVER_TIMEOUT_WARNING_REMAINING_SEC
|
||||
#define SERVER_SESSION_FORCED_TIMEOUT_SEC 300 // Should be quite large.
|
||||
|
||||
#define SERVER_STATISTICS_FILE_NAME "server_statistics.log"
|
||||
#define SERVER_STATISTICS_STR_TOTAL_PLAYERS "TotalNumPlayersLoggedIn"
|
||||
@@ -142,10 +146,6 @@ ServerLobbyThread::RemoveSessionFromGame(SessionWrapper session)
|
||||
void
|
||||
ServerLobbyThread::CloseSession(SessionWrapper session)
|
||||
{
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
|
||||
m_initTimerSessionMap.erase(session.sessionData->GetId());
|
||||
}
|
||||
m_sessionManager.RemoveSession(session.sessionData->GetId());
|
||||
m_gameSessionManager.RemoveSession(session.sessionData->GetId());
|
||||
|
||||
@@ -308,12 +308,13 @@ ServerLobbyThread::Main()
|
||||
NewSessionLoop();
|
||||
// Main loop.
|
||||
ProcessLoop();
|
||||
// Close sessions.
|
||||
CloseSessionLoop();
|
||||
// Remove games.
|
||||
RemoveGameLoop();
|
||||
// Kick players.
|
||||
KickPlayerLoop();
|
||||
// Check session timeouts.
|
||||
m_sessionManager.ForEachRemoveIf(boost::bind(&ServerLobbyThread::CheckSessionTimeouts, boost::ref(*this), _1));
|
||||
m_gameSessionManager.ForEachRemoveIf(boost::bind(&ServerLobbyThread::CheckSessionTimeouts, boost::ref(*this), _1));
|
||||
// Update avatar limitation lock.
|
||||
UpdateAvatarClientTimerLoop();
|
||||
// Cleanup cache.
|
||||
@@ -383,6 +384,8 @@ ServerLobbyThread::ProcessLoop()
|
||||
HandleNetPacketRetrievePlayerInfo(session, *packet->ToNetPacketRetrievePlayerInfo());
|
||||
else if (packet->ToNetPacketRetrieveAvatar())
|
||||
HandleNetPacketRetrieveAvatar(session, *packet->ToNetPacketRetrieveAvatar());
|
||||
else if (packet->ToNetPacketResetTimeout())
|
||||
{}
|
||||
else if (packet->ToNetPacketCreateGame())
|
||||
HandleNetPacketCreateGame(session, *packet->ToNetPacketCreateGame());
|
||||
else if (packet->ToNetPacketJoinGame())
|
||||
@@ -697,10 +700,6 @@ ServerLobbyThread::EstablishSession(SessionWrapper session)
|
||||
SendGameList(session.sessionData);
|
||||
|
||||
// Session is now established.
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
|
||||
m_initTimerSessionMap.erase(session.sessionData->GetId());
|
||||
}
|
||||
session.sessionData->SetState(SessionData::Established);
|
||||
|
||||
{
|
||||
@@ -764,27 +763,6 @@ ServerLobbyThread::NewSessionLoop()
|
||||
HandleReAddedSession(tmpSession);
|
||||
}
|
||||
|
||||
void
|
||||
ServerLobbyThread::CloseSessionLoop()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
|
||||
TimerSessionMap::iterator i = m_initTimerSessionMap.begin();
|
||||
TimerSessionMap::iterator end = m_initTimerSessionMap.end();
|
||||
|
||||
// Remove sessions if they do not initialize within a certain period.
|
||||
while (i != end)
|
||||
{
|
||||
TimerSessionMap::iterator next = i;
|
||||
++next;
|
||||
if (i->second.elapsed().total_seconds() > SERVER_INIT_SESSION_TIMEOUT_SEC)
|
||||
{
|
||||
m_sessionManager.RemoveSession(i->first);
|
||||
m_initTimerSessionMap.erase(i);
|
||||
}
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServerLobbyThread::RemoveGameLoop()
|
||||
{
|
||||
@@ -956,8 +934,6 @@ ServerLobbyThread::HandleNewConnection(boost::shared_ptr<ConnectData> connData)
|
||||
tmpAddress[sizeof(tmpAddress) - 1] = 0; // paranoia
|
||||
sessionData->SetClientAddr(tmpAddress);
|
||||
}
|
||||
boost::mutex::scoped_lock lock(m_initTimerSessionMapMutex);
|
||||
m_initTimerSessionMap[sessionData->GetId()] = boost::timers::portable::microsec_timer();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -987,6 +963,39 @@ ServerLobbyThread::HandleReAddedSession(SessionWrapper session)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ServerLobbyThread::CheckSessionTimeouts(SessionWrapper session)
|
||||
{
|
||||
bool retVal = false;
|
||||
if (session.sessionData.get())
|
||||
{
|
||||
if (session.sessionData->GetState() == SessionData::Init && session.sessionData->GetAutoDisconnectTimerElapsedSec() >= SERVER_INIT_SESSION_TIMEOUT_SEC)
|
||||
retVal = true;
|
||||
else if (session.sessionData->GetActivityTimerElapsedSec() >= SERVER_SESSION_ACTIVITY_TIMEOUT_SEC - SERVER_TIMEOUT_WARNING_REMAINING_SEC
|
||||
&& !session.sessionData->HasActivityNoticeBeenSent())
|
||||
{
|
||||
session.sessionData->MarkActivityNotice();
|
||||
boost::shared_ptr<NetPacket> packet(new NetPacketTimeoutWarning);
|
||||
NetPacketTimeoutWarning::Data warningData;
|
||||
warningData.timeoutReason = NETWORK_TIMEOUT_GENERIC;
|
||||
warningData.remainingSeconds = SERVER_TIMEOUT_WARNING_REMAINING_SEC;
|
||||
static_cast<NetPacketTimeoutWarning *>(packet.get())->SetData(warningData);
|
||||
GetSender().Send(session.sessionData, packet);
|
||||
}
|
||||
else if (session.sessionData->GetActivityTimerElapsedSec() >= SERVER_SESSION_ACTIVITY_TIMEOUT_SEC)
|
||||
{
|
||||
// TODO SendError(session.sessionData, errorCode);
|
||||
retVal = true;
|
||||
}
|
||||
else if (session.sessionData->GetAutoDisconnectTimerElapsedSec() >= SERVER_SESSION_FORCED_TIMEOUT_SEC)
|
||||
{
|
||||
// TODO SendError(session.sessionData, errorCode);
|
||||
retVal = true;
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void
|
||||
ServerLobbyThread::CleanupConnectQueue()
|
||||
{
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
#include <net/sessiondata.h>
|
||||
|
||||
SessionData::SessionData(SOCKET sockfd, SessionId id)
|
||||
: m_sockfd(sockfd), m_id(id), m_state(SessionData::Init), m_readyFlag(false)
|
||||
: m_sockfd(sockfd), m_id(id), m_state(SessionData::Init), m_readyFlag(false),
|
||||
m_activityTimeoutNoticeSent(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -100,3 +101,40 @@ SessionData::GetReceiveBuffer()
|
||||
return m_receiveBuffer;
|
||||
}
|
||||
|
||||
void
|
||||
SessionData::ResetActivityTimer()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_dataMutex);
|
||||
m_activityTimeoutNoticeSent = false;
|
||||
m_activityTimer.reset();
|
||||
m_activityTimer.start();
|
||||
}
|
||||
|
||||
unsigned
|
||||
SessionData::GetActivityTimerElapsedSec() const
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_dataMutex);
|
||||
return m_activityTimer.elapsed().total_seconds();
|
||||
}
|
||||
|
||||
bool
|
||||
SessionData::HasActivityNoticeBeenSent() const
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_dataMutex);
|
||||
return m_activityTimeoutNoticeSent;
|
||||
}
|
||||
|
||||
void
|
||||
SessionData::MarkActivityNotice()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_dataMutex);
|
||||
m_activityTimeoutNoticeSent = true;
|
||||
}
|
||||
|
||||
unsigned
|
||||
SessionData::GetAutoDisconnectTimerElapsedSec() const
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_dataMutex);
|
||||
return m_autoDisconnectTimer.elapsed().total_seconds();
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,8 @@ SessionManager::Select(unsigned timeoutMsec)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (retSession.sessionData.get())
|
||||
retSession.sessionData->ResetActivityTimer();
|
||||
return retSession;
|
||||
}
|
||||
|
||||
@@ -294,6 +296,24 @@ SessionManager::ForEach(boost::function<void (SessionWrapper)> func)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::ForEachRemoveIf(boost::function<bool (SessionWrapper)> func)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(m_sessionMapMutex);
|
||||
|
||||
SessionMap::iterator i = m_sessionMap.begin();
|
||||
SessionMap::iterator end = m_sessionMap.end();
|
||||
|
||||
while (i != end)
|
||||
{
|
||||
SessionMap::iterator next = i;
|
||||
next++;
|
||||
if (func((*i).second))
|
||||
m_sessionMap.erase(i);
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
SessionManager::CountReadySessions() const
|
||||
{
|
||||
|
||||
@@ -113,7 +113,6 @@ protected:
|
||||
void RequestPlayerAvatar(SessionWrapper session);
|
||||
void NewConnectionLoop();
|
||||
void NewSessionLoop();
|
||||
void CloseSessionLoop();
|
||||
void RemoveGameLoop();
|
||||
void KickPlayerLoop();
|
||||
void UpdateAvatarClientTimerLoop();
|
||||
@@ -128,7 +127,7 @@ protected:
|
||||
void HandleNewConnection(boost::shared_ptr<ConnectData> connData);
|
||||
void HandleReAddedSession(SessionWrapper session);
|
||||
|
||||
SOCKET Select();
|
||||
bool CheckSessionTimeouts(SessionWrapper session);
|
||||
|
||||
void CleanupConnectQueue();
|
||||
void CleanupSessionMap();
|
||||
@@ -166,9 +165,6 @@ private:
|
||||
SessionManager m_sessionManager;
|
||||
SessionManager m_gameSessionManager;
|
||||
|
||||
TimerSessionMap m_initTimerSessionMap;
|
||||
mutable boost::mutex m_initTimerSessionMapMutex;
|
||||
|
||||
TimerClientAddressMap m_timerAvatarClientAddressMap;
|
||||
mutable boost::mutex m_timerAvatarClientAddressMapMutex;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <net/receivebuffer.h>
|
||||
#include <string>
|
||||
#include <boost/thread.hpp>
|
||||
#include <core/boost/timers.hpp>
|
||||
|
||||
#define INVALID_SESSION 0
|
||||
#define SESSION_ID_INIT INVALID_SESSION
|
||||
@@ -60,6 +61,12 @@ public:
|
||||
|
||||
ReceiveBuffer &GetReceiveBuffer();
|
||||
|
||||
void ResetActivityTimer();
|
||||
unsigned GetActivityTimerElapsedSec() const;
|
||||
bool HasActivityNoticeBeenSent() const;
|
||||
void MarkActivityNotice();
|
||||
unsigned GetAutoDisconnectTimerElapsedSec() const;
|
||||
|
||||
private:
|
||||
SOCKET m_sockfd;
|
||||
const SessionId m_id;
|
||||
@@ -67,6 +74,9 @@ private:
|
||||
std::string m_clientAddr;
|
||||
ReceiveBuffer m_receiveBuffer;
|
||||
bool m_readyFlag;
|
||||
boost::timers::portable::microsec_timer m_activityTimer;
|
||||
bool m_activityTimeoutNoticeSent;
|
||||
boost::timers::portable::microsec_timer m_autoDisconnectTimer;
|
||||
|
||||
mutable boost::mutex m_dataMutex;
|
||||
};
|
||||
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
bool IsPlayerConnected(unsigned uniqueId) const;
|
||||
|
||||
void ForEach(boost::function<void (SessionWrapper)> func);
|
||||
void ForEachRemoveIf(boost::function<bool (SessionWrapper)> func);
|
||||
|
||||
unsigned CountReadySessions() const;
|
||||
void ResetAllReadyFlags();
|
||||
|
||||
Reference in New Issue
Block a user