Why is it necessary? Extreme example: The game time is 59,9 seconds and the turn master orders the character to jump in oder to save him from the water which will increase in 0.1 seconds. It's important that the non turn master does not continue the game to the 60th game second, if it hasn't received the jump action yet. Otherwise the non turn master will see the character drown and no resurrection system can bring him back as the water has already risen.
How to detect that there are no further actions for the current physics frame? If there had been actions in a physics frame wormux will send now one "echo" action at the end of the physics frame. The non turn masters knows when receiving it that they can render all physics frames until that time. --- lib/wormux/include/WORMUX_action.h | 2 +- src/game/game.cpp | 1 + src/game/time.cpp | 35 +++++++++++++++++++++-------------- src/game/time.h | 4 ++-- src/include/action_handler.cpp | 30 ++++++++++++++++++++++++++++-- src/network/network.cpp | 21 +++++++++++++++++---- src/network/network.h | 11 ++++++----- src/network/network_local.cpp | 2 +- src/network/network_local.h | 2 +- 9 files changed, 78 insertions(+), 30 deletions(-) diff --git a/lib/wormux/include/WORMUX_action.h b/lib/wormux/include/WORMUX_action.h index 14238c9..4f9dc21 100644 --- a/lib/wormux/include/WORMUX_action.h +++ b/lib/wormux/include/WORMUX_action.h @@ -106,7 +106,7 @@ public: ACTION_NETWORK_RANDOM_INIT, ACTION_INFO_CLIENT_CONNECT, ACTION_INFO_CLIENT_DISCONNECT, - + ACTION_ECHO, // ######################################################## } Action_t; diff --git a/src/game/game.cpp b/src/game/game.cpp index d24bd99..70df1d8 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -552,6 +552,7 @@ void Game::MainLoop() } } #endif + Network::GetInstance()->SendEcho(); delay = time_of_next_phy_frame - Time::GetInstance()->ReadRealTime(); if (delay >= 0) diff --git a/src/game/time.cpp b/src/game/time.cpp index c345817..05ec829 100644 --- a/src/game/time.cpp +++ b/src/game/time.cpp @@ -19,7 +19,11 @@ * Handle the game time. The game can be paused. *****************************************************************************/ +#include "game/game.h" #include "game/time.h" +#include "network/network.h" +#include "team/team.h" +#include "team/teams_list.h" #include <SDL.h> #include <sstream> #include <iomanip> @@ -34,7 +38,7 @@ Time::Time() is_game_paused = false; delta_t = 20; current_time = 0; - //max_time = 0; + max_time_plus_one = 0; real_time_game_start = 0; real_time_pause_dt = 0; } @@ -42,6 +46,7 @@ Time::Time() void Time::Reset() { current_time = 0; + max_time_plus_one = 0; is_game_paused = false; real_time_game_start = SDL_GetTicks(); real_time_pause_dt = 0; @@ -55,19 +60,21 @@ uint Time::ReadRealTime() const void Time::Refresh() { - /* - TODO : Activate this condition later. - Refresh time condition : - - active team is Local - - current node is server and game loop is not in Playing state - - game don't use network - if((ActiveTeam().IsLocal() || ActiveTeam().IsLocalAI()) || - (Network::GetInstance()->IsServer() && Game::GetInstance()->ReadState() != Game::PLAYING) || - (!Network::GetInstance()->IsServer() && !Network::GetInstance()->IsClient()) || - current_time < max_time) - */ - current_time += delta_t; - //RefreshMaxTime(current_time); + uint newValue = current_time + delta_t; + bool activeTeamLocal = ActiveTeam().IsLocal() || ActiveTeam().IsLocalAI(); + bool serverButNotPlaying = Network::GetInstance()->IsServer() && Game::GetInstance()->ReadState() != Game::PLAYING; + bool noNetworkGame = !Network::GetInstance()->IsServer() && !Network::GetInstance()->IsClient(); + bool ignoreMaxTime = activeTeamLocal || serverButNotPlaying || noNetworkGame; + if (ignoreMaxTime || (newValue < max_time_plus_one)) + current_time = newValue; + else + real_time_pause_dt += delta_t; +} + +void Time::RefreshMaxTimePlusOne(uint updated_max_time_plus_one) +{ + if (updated_max_time_plus_one >= max_time_plus_one) + max_time_plus_one = updated_max_time_plus_one; } void Time::TogglePause() diff --git a/src/game/time.h b/src/game/time.h index 193c8e9..edb9109 100644 --- a/src/game/time.h +++ b/src/game/time.h @@ -31,7 +31,7 @@ class Time : public Singleton<Time> { private: uint current_time; - //uint max_time; + uint max_time_plus_one; uint delta_t; bool is_game_paused; @@ -59,7 +59,7 @@ public: uint ReadMin() const { return ReadSec() / 60; }; void Refresh(); uint GetDelta() const { return delta_t; }; - //void RefreshMaxTime(uint updated_max_time); + void RefreshMaxTimePlusOne(uint updated_max_time_plus_one); // Read the clock time uint ClockSec() const { return ReadSec() % 60; }; diff --git a/src/include/action_handler.cpp b/src/include/action_handler.cpp index df17a05..f22348d 100644 --- a/src/include/action_handler.cpp +++ b/src/include/action_handler.cpp @@ -825,6 +825,11 @@ static void Action_Network_Ping(Action */*a*/) { } +// Nothing to do here. The action handler will use the timestamp to set the maximal time. +static void Action_Echo(Action */*a*/) +{ +} + // ######################################################## static void _Info_ConnectHost(const std::string& hostname, const std::string& nicknames) @@ -1084,7 +1089,7 @@ void Action_Handler_Init() ActionHandler::GetInstance()->Register (Action::ACTION_NETWORK_RANDOM_INIT, "NETWORK_random_init", &Action_Network_RandomInit); ActionHandler::GetInstance()->Register (Action::ACTION_INFO_CLIENT_DISCONNECT, "INFO_client_disconnect", &Action_Info_ClientDisconnect); ActionHandler::GetInstance()->Register (Action::ACTION_INFO_CLIENT_CONNECT, "INFO_client_connect", &Action_Info_ClientConnect); - + ActionHandler::GetInstance()->Register (Action::ACTION_ECHO, "ACTION_echo", &Action_Echo); // ######################################################## ActionHandler::GetInstance()->UnLock(); } @@ -1106,11 +1111,32 @@ void ActionHandler::ExecActions() { Action * a; std::list<Action*>::iterator it; + Time * time = Time::GetInstance(); + + // The game can continue until the point at which all actions are known. + // If for example a non turn master receives: + // 5s game time: jump + // 6s game time: jump + // then the game can continue until the game time is (6s - smallest time unit). + // The physics frame for the 6th game second must not be calculated + // as more actions for the 6th game second might be received in future. + for (it = queue.begin(); it != queue.end() ;it++) + { + Lock(); + a = (*it); + // The action echo gets send at the end of a physics frame: + // No more physic related actions are expected for the current frame. + if (a->GetType() == Action::ACTION_ECHO) + time->RefreshMaxTimePlusOne(a->GetTimestamp() + 1); + else + time->RefreshMaxTimePlusOne(a->GetTimestamp()); + UnLock(); + } + for (it = queue.begin(); it != queue.end() ;) { Lock(); a = (*it); - //Time::GetInstance()->RefreshMaxTime((*it)->GetTimestamp()); // If action is in the future, wait for next refresh if (a->GetTimestamp() > Time::GetInstance()->Read()) { UnLock(); diff --git a/src/network/network.cpp b/src/network/network.cpp index da56cab..5652b5c 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -340,7 +340,7 @@ void Network::DisconnectNetwork() //----------------------------------------------------------------------------- // Send Messages -void Network::SendActionToAll(const Action& a) const +void Network::SendActionToAll(const Action& a) { MSG_DEBUG("network.traffic","Send action %s to all remote computers", ActionHandler::GetInstance()->GetActionName(a.GetType()).c_str()); @@ -348,7 +348,7 @@ void Network::SendActionToAll(const Action& a) const SendAction(a, NULL, false); } -void Network::SendActionToOne(const Action& a, DistantComputer* client) const +void Network::SendActionToOne(const Action& a, DistantComputer* client) { MSG_DEBUG("network.traffic","Send action %s to %s (%s)", ActionHandler::GetInstance()->GetActionName(a.GetType()).c_str(), @@ -357,7 +357,7 @@ void Network::SendActionToOne(const Action& a, DistantComputer* client) const SendAction(a, client, true); } -void Network::SendActionToAllExceptOne(const Action& a, DistantComputer* client) const +void Network::SendActionToAllExceptOne(const Action& a, DistantComputer* client) { MSG_DEBUG("network.traffic","Send action %s to all EXCEPT %s", ActionHandler::GetInstance()->GetActionName(a.GetType()).c_str(), @@ -369,11 +369,13 @@ void Network::SendActionToAllExceptOne(const Action& a, DistantComputer* client) // if (client == NULL) sending to every clients // if (clt_as_rcver) sending only to client 'client' // if (!clt_as_rcver) sending to all EXCEPT client 'client' -void Network::SendAction(const Action& a, DistantComputer* client, bool clt_as_rcver) const +void Network::SendAction(const Action& a, DistantComputer* client, bool clt_as_rcver) { char* packet; int size; + echo_outstanding = true; + a.WriteToPacket(packet, size); ASSERT(packet != NULL); @@ -405,6 +407,17 @@ void Network::SendAction(const Action& a, DistantComputer* client, bool clt_as_r free(packet); } +void Network::SendEcho() { + if (echo_outstanding) + { + Action a(Action::ACTION_ECHO); + SendActionToAll(a); + + // this must be set after SendActionToAll as SendActionToAll would set it to true otherwise. + echo_outstanding = false; + } +} + //----------------------------------------------------------------------------- // Static method diff --git a/src/network/network.h b/src/network/network.h index 1a1964b..a9bd1c2 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -85,7 +85,7 @@ private: Player player; bool turn_master_player; - + bool echo_outstanding; protected: bool game_master_player; WNet::net_game_state_t state; @@ -99,7 +99,7 @@ protected: int fin; #endif - virtual void SendAction(const Action& a, DistantComputer* client, bool clt_as_rcver) const; + virtual void SendAction(const Action& a, DistantComputer* client, bool clt_as_rcver); void DisconnectNetwork(); @@ -150,9 +150,10 @@ public: virtual void CloseConnection(std::list<DistantComputer*>::iterator closed) = 0; // Action handling - void SendActionToAll(const Action& action) const; - void SendActionToOne(const Action& action, DistantComputer* client) const; - void SendActionToAllExceptOne(const Action& action, DistantComputer* client) const; + void SendActionToAll(const Action& action); + void SendActionToOne(const Action& action, DistantComputer* client); + void SendActionToAllExceptOne(const Action& action, DistantComputer* client); + void SendEcho(); // Manage network state void SetState(WNet::net_game_state_t state); diff --git a/src/network/network_local.cpp b/src/network/network_local.cpp index 48c70e6..3e6cc14 100644 --- a/src/network/network_local.cpp +++ b/src/network/network_local.cpp @@ -25,7 +25,7 @@ NetworkLocal::NetworkLocal() : Network("-", "") {} NetworkLocal::~NetworkLocal() {} -void NetworkLocal::SendAction(const Action& /*a*/, DistantComputer* /*client*/, bool /*clt_as_rcver*/) const {} +void NetworkLocal::SendAction(const Action& /*a*/, DistantComputer* /*client*/, bool /*clt_as_rcver*/) {} void NetworkLocal::CloseConnection(std::list<DistantComputer*>::iterator /*closed*/) { diff --git a/src/network/network_local.h b/src/network/network_local.h index 6d29dbc..727a577 100644 --- a/src/network/network_local.h +++ b/src/network/network_local.h @@ -30,7 +30,7 @@ class NetworkLocal : public Network protected: virtual void HandleAction(Action* /*a*/, DistantComputer* /*sender*/) { ASSERT(false) }; virtual void WaitActionSleep() { ASSERT(false) }; - virtual void SendAction(const Action& a, DistantComputer* client, bool clt_as_rcver) const; + virtual void SendAction(const Action& a, DistantComputer* client, bool clt_as_rcver); public: NetworkLocal(); -- 1.6.0.4 _______________________________________________ Wormux-dev mailing list Wormux-dev@gna.org https://mail.gna.org/listinfo/wormux-dev