#pragma once
#include "Network/GameSession.h"
#include "Network/NetworkGameActions.h"
class SessionWatchDog : public GameClass
{
public:
SessionWatchDog(std::shared_ptr<GameSession> a_session, std::string a_name)
: GameClass(a_session)
, m_name(a_name)
{
SessionRegisterListener(HandlePing);
SessionRegisterListener(HandlePong);
SessionRegisterListener(HandleConnected);
SessionRegisterListener(HandleDisconnected);
SessionRegisterListener(HandleInDanger);
SessionRegisterListener(HandleOutOfDanger);
SessionRegisterListener(HandleBandwidthProbeRequest);
SessionRegisterListener(HandleBandwidthProbeResponse);
m_probe.m_probeStr = "BandwidthProbeString";
}
~SessionWatchDog()
{
}
void SetTimeOuts(std::chrono::milliseconds a_inDangerTimeOut, std::chrono::milliseconds a_abandonedConnectionThreshold, std::chrono::milliseconds a_timeBetweenPings)
{
m_timeoutTillInDanger = a_inDangerTimeOut; // Amount of time until considered in-danger
m_abandonedConnectionThreshold = a_abandonedConnectionThreshold;
m_timeBetweenPings = a_timeBetweenPings; // Frequency to send pings
}
void HandlePing(const PingAction& a_ping)
{
PongAction pong;
pong.SetRespondingToAction(a_ping);
SessionQueueAction(pong);
}
void HandlePong(const PongAction& a_pong)
{
bool okay = a_pong.IsResponseToAction(m_currentPing);
if (!okay)
{
VerboseDebugging("Got an INVALID Pong");
}
m_havePong |= okay;
if (okay && m_isInDanger)
{
SessionQueueEvent(ConnectionOutOfDanger());
}
}
void HandleBandwidthProbeRequest(const BandwidthProbeRequest& a_probe)
{
VerboseDebugging("Got a BandwidthProbe Request");
BandwidthProbeResponse resp;
resp.m_probeStr = a_probe.m_probeStr;
resp.SetRespondingToAction(a_probe);
SessionQueueAction(resp);
}
void HandleBandwidthProbeResponse(const BandwidthProbeResponse& a_probe)
{
bool okay = a_probe.IsResponseToAction(m_probe) && (a_probe.m_probeStr.size() == m_probe.m_probeStr.size());
VerboseDebugging("Got a %s BandwidthProbe Response", okay ? "valid" : "INVALID");
}
void HandleConnected(const ConnectionEstablished& a_connect)
{
m_isConnected = true;
m_isInDanger = false;
m_elapsedTimeSinceLastSentPing = std::chrono::milliseconds(0);
VerboseDebugging("Got a Connection Established");
}
void HandleDisconnected(const ConnectionLost& a_lost)
{
m_isConnected = false;
VerboseDebugging("Got a Connection Lost");
}
void HandleInDanger(const ConnectionInDanger& a_danger)
{
m_isInDanger = true;
VerboseDebugging("Got a Connection In-Danger");
}
void HandleOutOfDanger(const ConnectionOutOfDanger& a_danger)
{
m_isInDanger = false;
VerboseDebugging("Got a Connection Out-Of-Danger");
}
bool UpdateWatchDog(std::chrono::milliseconds a_timeElapsed)
{
if (!m_isConnected)
{
return false;
}
m_elapsedTimeSinceLastSentPing += a_timeElapsed;
if (!m_isInDanger && m_elapsedTimeSinceLastSentPing > m_timeoutTillInDanger)
{
m_missedPongs++;
SessionQueueEvent(ConnectionInDanger());
}
if (m_isInDanger && m_elapsedTimeSinceLastSentPing > m_abandonedConnectionThreshold)
{
SessionQueueEvent(ConnectionLost());
return false;
}
if (m_havePong && m_elapsedTimeSinceLastSentPing > m_timeBetweenPings)
{
PingAction newPing;
m_currentPing = newPing;
SessionQueueAction(m_currentPing);
m_elapsedTimeSinceLastSentPing = std::chrono::milliseconds(0);
m_havePong = false;
/*
// TODO: This is just test code to try out the re-connection path by forcably closing it periodically (should remove this when done testing the re-connection code)
static int testRetry = 0;
if (m_name == "Server") // Test when it is the server doing the disconnecting (try both cases, client or server initiated disconnections)
{
testRetry++;
if (testRetry == 3) // every 10th ping, force disconnect to test the re-connecting code
{
// Only needed for debugging/testing
GetGameContext()->Disconnect();
testRetry = 0;
}
}
*/
}
return true;
}
private:
std::chrono::milliseconds m_timeoutTillInDanger = std::chrono::seconds(2); // Amount of time until considered in-danger
std::chrono::milliseconds m_abandonedConnectionThreshold = std::chrono::seconds(5); // Amount of time until consider the connection abandoned
std::chrono::milliseconds m_timeBetweenPings = std::chrono::seconds(1); // Frequency to send pings
std::string m_name;
PingAction m_currentPing;
int m_missedPongs = 0;
bool m_havePong = true;
std::chrono::milliseconds m_elapsedTimeSinceLastSentPing = std::chrono::seconds(0);
bool m_isInDanger = false;
bool m_isConnected = false;
BandwidthProbeRequest m_probe;
protected:
template <typename ...Args>
void VerboseDebugging(const char* fmt, Args ...args)
{
#if 0 // Enable to debug the client<->server handshake
printf("%s: ", m_name.c_str());
printf(fmt, args...);
printf("\n");
#endif
}
};