From 1c63cbf204b1d2c667ce9f821ccb197d0ffb0ac3 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Wed, 11 May 2011 14:48:17 +0000 Subject: Review of the main loop timer, converted timers from float to unsigned long, corrected a number of timing bugs, improved client framerate stability. --- src/core/Makefile.am | 2 -- src/core/application.cc | 7 ++-- src/core/application.h | 21 ++++++------ src/core/entity.cc | 10 +++--- src/core/entity.h | 11 ++++--- src/core/gameinterface.h | 17 ++++++---- src/core/gameserver.cc | 83 +++++++++++++++++++++++++++++++++++------------- src/core/gameserver.h | 15 +++++---- src/core/netserver.cc | 14 ++++++-- src/core/netserver.h | 3 ++ src/core/physics.cc | 14 ++------ src/core/physics.h | 5 +-- src/core/timer.cc | 50 ----------------------------- src/core/timer.h | 50 ----------------------------- 14 files changed, 124 insertions(+), 178 deletions(-) delete mode 100644 src/core/timer.cc delete mode 100644 src/core/timer.h (limited to 'src/core') diff --git a/src/core/Makefile.am b/src/core/Makefile.am index e6b98e5..c1f4632 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -33,7 +33,6 @@ noinst_HEADERS = \ range.h \ signals.h \ stats.h \ - timer.h \ zone.h libcore_la_SOURCES = \ @@ -63,7 +62,6 @@ libcore_la_SOURCES = \ player.cc \ signals.cc \ stats.cc \ - timer.cc \ zone.cc libcore_la_DEPENDENCIES = \ diff --git a/src/core/application.cc b/src/core/application.cc index ec7e89c..38c39a1 100644 --- a/src/core/application.cc +++ b/src/core/application.cc @@ -72,7 +72,6 @@ Application::Application() } application_instance = this; - application_timestamp = 0; application_game = 0; #ifndef _WIN32 @@ -321,10 +320,8 @@ void Application::disconnect() } } -void Application::frame(unsigned long timestamp) +void Application::frame() { - application_timestamp = timestamp; - // execute commands in the buffer CommandBuffer::exec(); @@ -332,7 +329,7 @@ void Application::frame(unsigned long timestamp) return; // run a game interface frame - application_game->frame(timestamp); + application_game->frame(application_timer.timestamp()); if (!application_game->running()) disconnect(); diff --git a/src/core/application.h b/src/core/application.h index 37b70c2..cdaef67 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -8,6 +8,7 @@ #define __INCLUDED_CORE_APPLICATION_H__ #include "sys/sys.h" +#include "sys/timer.h" #include "core/commandbuffer.h" #include "core/cvar.h" @@ -30,14 +31,16 @@ public: /*----- inspectors ----------------------------------------------- */ - /// the current application time, in microseconds + /// the current application time, in milliseconds inline unsigned long timestamp() const { - return application_timestamp; + return application_timer.timestamp(); } - - /// the current application time, in seconds - float time() const { - return ((float)(timestamp()) / 1000.0f); + + /** + * @brief return the current application time, in seconds. + * */ + inline float time() const { + return (float) application_timer.timestamp() / 1000.0f; } /// true if the core is connected to a running game interface @@ -99,7 +102,7 @@ public: protected: /// run a core frame - virtual void frame(unsigned long timestamp); + virtual void frame(); /// load cvar config void load_config(); @@ -114,8 +117,8 @@ protected: void load_commandline(int count, char **argments); private: - /// time the core has been running - unsigned long application_timestamp; + /// main loop timer + sys::Timer application_timer; GameInterface *application_game; diff --git a/src/core/entity.cc b/src/core/entity.cc index 915dafe..6ce94a7 100644 --- a/src/core/entity.cc +++ b/src/core/entity.cc @@ -488,7 +488,7 @@ void Entity::remove_menu(std::string const &label) } -void Entity::frame(float seconds) +void Entity::frame(const unsigned long elapsed) { if (entity_collision_child_shapes.size() > 0) { btCompoundShape *compoundshape = static_cast (entity_collision_shape); @@ -782,9 +782,9 @@ void EntityDynamic::reset() set_dirty(); } -void EntityDynamic::frame(float seconds) +void EntityDynamic::frame(const unsigned long elapsed) { - Entity::frame(seconds); + Entity::frame(elapsed); if (entity_state == Docked) { return; @@ -1251,9 +1251,9 @@ void EntityControlable::action(btScalar seconds) } // osirion game frame (runs at osirion server framerate) -void EntityControlable::frame(float seconds) +void EntityControlable::frame(const unsigned long elapsed) { - EntityDynamic::frame(seconds); + EntityDynamic::frame(elapsed); // update zone keepalive bounding box if (owner() && (owner()->control() == this) && zone()) { diff --git a/src/core/entity.h b/src/core/entity.h index 230ce1b..164f746 100644 --- a/src/core/entity.h +++ b/src/core/entity.h @@ -263,8 +263,9 @@ public: /** * @brief runs one game frame for the entity * The default implementation does nothing + * @param elapsed elepased time since previous frame, in milliseconds */ - virtual void frame(float seconds); + virtual void frame(const unsigned long elapsed); /** * @brief runs one upkeep frame for the entity @@ -588,12 +589,13 @@ public: /// set event state virtual void set_state(int state); - /// runs one game frame for the entity /** + * @brief runs one game frame for the entity * The default implementation will update the position() of the entity, * determined by its speed() and axis() + * @param elapsed elepased time since previous frame, in milliseconds */ - virtual void frame(float seconds); + virtual void frame(const unsigned long elapsed); /** * @brief reset the physics state @@ -743,8 +745,9 @@ public: * @brief runs one game frame for the entity * The default implementation will set direction() and thrust() to the desired targets * and calls its parent frame() funcion. + * @param elapsed elepased time since previous frame, in milliseconds */ - virtual void frame(float seconds); + virtual void frame(const unsigned long elapsed); /// current thrust float entity_thrust; diff --git a/src/core/gameinterface.h b/src/core/gameinterface.h index 369b828..d99903f 100644 --- a/src/core/gameinterface.h +++ b/src/core/gameinterface.h @@ -67,11 +67,13 @@ public: return game_interactive; } - /// return the current game time, in seconds + /** + * @brief return the current game time, in seconds. + * */ inline float time() const { - return ((float)(game_timestamp) / 1000.0f); + return (float) game_timestamp / 1000.0f; } - + /** * @brief return the current game time, in milliseconds. * 1000 milliseconds equals one second. @@ -99,7 +101,7 @@ public: /// run one game time frame /// @param timestamp current application time - virtual void frame(unsigned long timestamp) = 0; + virtual void frame(const unsigned long timestamp) = 0; protected: /// the local player @@ -108,16 +110,17 @@ protected: /// all the players Players game_players; - model::VertexArray *game_vertexarray; - /// timestamp of the time the playerlist was last changed unsigned long game_playerlist_timestamp; - void set_timestamp(unsigned long timestamp); + /// set the current game time + void set_timestamp(const unsigned long timestamp); void set_running(const bool running); void set_interactive(const bool interactive); + + model::VertexArray *game_vertexarray; private: bool game_running; diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index 85a956d..58367e5 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -39,17 +39,42 @@ void func_who(std::string const &args) void func_time(std::string const &args) { - using namespace std; + using namespace std; - // FIXME unify with dedicated server clock - int minutes = (int) floorf(server()->time() / 60.0f); - int seconds = (int) floorf(server()->time() - (float) minutes * 60.0f); - - int syshours, sysminutes, sysseconds; - sys::get_localtime(syshours, sysminutes, sysseconds); - - con_print << "Uptime " << minutes << ":" << setfill('0') << setw(2) << seconds << - " Local time " << setfill(' ') << setw(2) << syshours << ":" << setfill('0') << setw(2) << sysminutes << ":" << setw(2) << sysseconds << setfill(' ') << std::endl; + // system time + int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, milliseconds = 0; + sys::get_localtime(year, month, day, hour, min, sec, milliseconds); + + con_print << "Local time: " + << std::setw(4) << std::setfill('0') << year << "-" + << std::setw(2) << std::setfill('0') << month << "-" + << std::setw(2) << std::setfill('0') << day << " " + << std::setw(2) << hour << ":" + << std::setw(2) << min << ":" + << std::setw(2) << sec << " " + << std::setw(2) << " "; + + // uptime + float uptime = core::game()->time(); + + const int uptime_days = (int) floorf(uptime / (24.0f * 3600.0f)); + uptime -= uptime_days * 24.0f * 3600.0f; + + const int uptime_hours = (int) floorf(uptime / 3600.0f); + uptime -= uptime_hours * 3600.0f; + + const int uptime_minutes = (int) floorf(uptime / 60.0f); + uptime -= uptime_minutes * 60.0f; + + const int uptime_seconds = (int) floorf(uptime); + + con_print << " Uptime: "; + if (uptime_days > 0) { + con_print << uptime_days << " " << aux::plural("day", uptime_days); + } + con_print << std::setfill('0') << std::setw(2) << uptime_hours << ":" + << std::setfill('0') << std::setw(2) << uptime_minutes << ":" + << std::setfill('0') << std::setw(2) << uptime_seconds << std::endl; } void func_mute(std::string const &args) @@ -126,7 +151,6 @@ GameServer::GameServer() : GameInterface() server_network = 0; server_previoustime = 0; server_maxplayerid = 1; - server_startup = application()->timestamp(); // create the default infotype for entities Entity::set_infotype(new InfoType("entity")); @@ -196,7 +220,10 @@ GameServer::GameServer() : GameInterface() if (!Cvar::sv_dedicated->value()) { player_connect(localplayer()); } - + + set_timestamp(server_timer.timestamp()); + server_previoustime = timestamp(); + set_running(true); } @@ -471,7 +498,7 @@ void GameServer::player_connect(Player *player) // manage player list game_players.push_back(player); - set_playerlist_timestamp(timestamp()); + set_playerlist_timestamp(server_timer.timestamp()); } void GameServer::player_disconnect(Player *player) @@ -489,16 +516,16 @@ void GameServer::player_disconnect(Player *player) // manage player list std::list:: iterator it = game_players.begin(); - while (((*it)->id() != player->id()) && (it != game_players.end())) { + while ((it != game_players.end()) && ((*it)->id() != player->id())) { it++; } if (it != game_players.end()) { game_players.erase(it); } - set_playerlist_timestamp(timestamp()); + set_playerlist_timestamp(server_timer.timestamp()); } -void GameServer::frame(unsigned long timestamp) +void GameServer::frame(const unsigned long timestamp) { if (error()) return; @@ -519,24 +546,36 @@ void GameServer::frame(unsigned long timestamp) /*if (!Cvar::sv_dedicated->value()) { update_clientstate(); }*/ - - Physics::frame(timestamp); if ((Cvar::sv_dedicated->value() || Cvar::sv_private->value())) { if (core::Cvar::sv_framerate->value()) { - float fps = ::floorf(1000.0f / core::Cvar::sv_framerate->value()); - if (server_startup + this->timestamp() + (unsigned long) fps > timestamp) { + + const unsigned long server_framelength = (unsigned long) ::floorf(1000.0f / core::Cvar::sv_framerate->value()); + + if (server_timer.timestamp() < server_previoustime + server_framelength) { return; } } } server_previoustime = this->timestamp(); - set_timestamp(timestamp - server_startup); + set_timestamp(server_timer.timestamp()); + + if (server_network) { + server_network->transmit(); + + if (server_network->error()) { + abort(); + return; + } + } - const float elapsed = (float)(this->timestamp() - server_previoustime) / 1000.0f; + const unsigned long elapsed = (this->timestamp() - server_previoustime); const unsigned long keepalive_timeout = (Cvar::sv_keepalive ? 1000 * (unsigned long) Cvar::sv_keepalive->value() : (unsigned long) 0 ); + // run a physics frame + Physics::frame(elapsed); + // reset zone keepalive state for (Zone::Registry::iterator zit = Zone::registry().begin(); zit != Zone::registry().end(); zit++) { Zone *zone= (*zit).second; diff --git a/src/core/gameserver.h b/src/core/gameserver.h index ab2bf92..2e1aa2a 100644 --- a/src/core/gameserver.h +++ b/src/core/gameserver.h @@ -7,6 +7,7 @@ #ifndef __INCLUDED_CORE_GAMESERVER_H__ #define __INCLUDED_CORE_GAMESERVER_H__ +#include "sys/timer.h" #include "core/gameinterface.h" #include "core/message.h" #include "core/module.h" @@ -41,7 +42,12 @@ public: void player_disconnect(Player *player); /// run a game server time frame - void frame(unsigned long timestamp); + virtual void frame(const unsigned long timestamp); + + /// server timer + inline const sys::Timer & timer() { + return server_timer; + } /// a player sends a chat message to the global chat channel void shout(Player *player, std::string const &args); @@ -70,11 +76,6 @@ public: /// a player sends a command to the game server void exec(Player *player, std::string const &cmdline); - /// time the server was started - inline const unsigned long startup() const { - return server_startup; - } - /// request info record with id virtual Info *request_info(const unsigned int id); @@ -99,7 +100,7 @@ private: unsigned int server_maxplayerid; unsigned long server_previoustime; - unsigned long server_startup; + sys::Timer server_timer; }; inline GameServer *server() diff --git a/src/core/netserver.cc b/src/core/netserver.cc index 0bd6350..d9a5bed 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -359,7 +359,7 @@ void NetServer::client_frame(NetClient *client, unsigned long timestamp) } else { // send a server frame marker - send_frame_marker(client, timestamp); + send_frame_marker(client, server()->timer().timestamp()); // send updates for entities in the zone for (Entity::Registry::iterator it = Entity::registry().begin(); it != Entity::registry().end(); it++) { @@ -411,6 +411,16 @@ void NetServer::client_frame(NetClient *client, unsigned long timestamp) } } +// transmit pending packets to all clients +void NetServer::transmit() +{ + // send updates to each client + for (Clients::iterator it = clients.begin(); it != clients.end(); it++) { + NetClient *client = *it; + client->transmit(); + } +} + // run a network server frame, send updates to clients void NetServer::frame(unsigned long timestamp) { @@ -730,7 +740,7 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me } else if (command.compare("ping") == 0) { unsigned long timestamp; if (msgstream >> timestamp) { - client->player()->set_ping(application()->timestamp() - server()->startup() - timestamp); + client->player()->set_ping(server()->timer().timestamp() - timestamp); } return; } diff --git a/src/core/netserver.h b/src/core/netserver.h index 6eeb49b..d8635e4 100644 --- a/src/core/netserver.h +++ b/src/core/netserver.h @@ -57,6 +57,9 @@ public: /// run a network server frame void frame(unsigned long timestamp); + + /// transmit pending packets to all clients + void transmit(); /// receive data from clients void receive(); diff --git a/src/core/physics.cc b/src/core/physics.cc index 90fb184..9ffe13c 100644 --- a/src/core/physics.cc +++ b/src/core/physics.cc @@ -15,7 +15,6 @@ namespace core { btDefaultCollisionConfiguration *Physics::physics_configuration = 0; btCollisionDispatcher *Physics::physics_dispatcher = 0; btSequentialImpulseConstraintSolver *Physics::physics_solver = 0; -unsigned long Physics::physics_timestamp = 0; void Physics::init() { @@ -27,8 +26,6 @@ void Physics::init() physics_dispatcher = new btCollisionDispatcher(physics_configuration); btGImpactCollisionAlgorithm::registerAlgorithm(physics_dispatcher); physics_solver = new btSequentialImpulseConstraintSolver; - - physics_timestamp = 0; } void Physics::done() @@ -46,18 +43,13 @@ void Physics::done() physics_solver = 0; } -void Physics::frame(const unsigned long timestamp) +void Physics::frame(const unsigned long elapsed) { - if (!timestamp) - return; - - const float seconds = (float) (timestamp - physics_timestamp) / 1000.0f; + const float seconds = (float) elapsed / 1000.0f; for (core::Zone::Registry::iterator it = core::Zone::registry().begin(); it != core::Zone::registry().end(); it++) { - (*it).second->physics()->stepSimulation(seconds); + (*it).second->physics()->stepSimulation(seconds, 8); } - - physics_timestamp = timestamp; } } // namespace core diff --git a/src/core/physics.h b/src/core/physics.h index 3237c3a..1989410 100644 --- a/src/core/physics.h +++ b/src/core/physics.h @@ -28,7 +28,7 @@ public: static void done(); - static void frame(const unsigned long timestamp); + static void frame(const unsigned long elapsed); inline static btDefaultCollisionConfiguration *configuration() { return physics_configuration; @@ -46,9 +46,6 @@ private: static btDefaultCollisionConfiguration *physics_configuration; static btCollisionDispatcher *physics_dispatcher; static btSequentialImpulseConstraintSolver *physics_solver; - - static unsigned long physics_timestamp; - }; } // namespace core diff --git a/src/core/timer.cc b/src/core/timer.cc deleted file mode 100644 index 6f2e914..0000000 --- a/src/core/timer.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - core/timer.cc - This file is part of the Osirion project and is distributed under - the terms and conditions of the GNU General Public License version 2 -*/ - -#include "core/timer.h" - -#include -#include -#include - -namespace core -{ - -Timer::Timer() -{ - gettimeofday(&this->timer_tick, &this->timer_tz); - this->timer_elapsed = 0; -} - -Timer::~Timer() -{ -} - -void Timer::mark() -{ - gettimeofday(&timer_tick, &timer_tz); -} - -unsigned long Timer::timestamp() -{ - struct timeval tick; - struct timezone tick_tz; - - gettimeofday(&tick, &tick_tz); - - // calculate elapsed time in 10^-6 seconds - unsigned long delta = 0; - delta = tick.tv_sec * 1000 + tick.tv_usec / 1000; - delta -= timer_tick.tv_sec * 1000 + timer_tick.tv_usec / 1000; - return delta; -} - -float Timer::elapsed() -{ - return ((float) timestamp() / 1000.0f); -} - -} diff --git a/src/core/timer.h b/src/core/timer.h deleted file mode 100644 index 5157811..0000000 --- a/src/core/timer.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - core/timer.h - This file is part of the Osirion project and is distributed under - the terms and conditions of the GNU General Public License version 2 -*/ - -#ifndef __INCLUDED_CORE_TIMER_H__ -#define __INCLUDED_CORE_TIMER_H__ - -#include - -namespace core -{ - -/// a timer measures intervals in seconds -/*! A timer class measures the time elapsed -* between the last two calls to its mark() function. -*/ -class Timer -{ -public: - /// Constructor - Timer(); - /// Destructor - ~Timer(); - - /// mark the current time as zero - /*! Reset the timer, all subsequent calls too elapsed() will - * use the current timestamp as reference - */ - void mark(); - - /*! return the time elapsed since the last mark, in seconds - * @see mark() - */ - float elapsed(); - - /// return timestamp since last mark, in microseconds - unsigned long timestamp(); - -private: - float timer_elapsed; - struct timezone timer_tz; - struct timeval timer_tick; -}; // class Timer - -} // namespace core - -#endif // __INCLUDED_CORE_TIMER_H__ - -- cgit v1.2.3