diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 88f978f1e..05a7e7d9e 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -195,6 +195,7 @@ SET(common_headers say_link.h seperator.h serialize_buffer.h + server_stats.h serverinfo.h servertalk.h shareddb.h diff --git a/common/net/eqstream_concurrent.cpp b/common/net/eqstream_concurrent.cpp index fcf7153fc..00d3d63e9 100644 --- a/common/net/eqstream_concurrent.cpp +++ b/common/net/eqstream_concurrent.cpp @@ -6,6 +6,7 @@ #include "../opcodemgr.h" #include "../eqemu_logsys.h" #include "../eqemu_logsys_fmt.h" +#include "../server_stats.h" #include "daybreak_connection.h" #include #include @@ -85,20 +86,27 @@ void EQ::Net::ConcurrentEQStreamManager::_BackgroundThread() { _impl->background_update_stats_timer.reset(new EQ::Timer(&loop, 500, true, std::bind(&ConcurrentEQStreamManager::_BackgroundUpdateStatsTimer, this, std::placeholders::_1))); + int sleep_time = 16; while (true == _impl->background_running) { + auto now = std::chrono::high_resolution_clock::now(); + EQ::ServerStats::Get().BeginNetworkFrame(); loop.Process(); switch (_impl->priority) { case EQStreamPriority::Low: - Sleep(10); + sleep_time = 200; break; case EQStreamPriority::Normal: - Sleep(5); + sleep_time = 100; break; case EQStreamPriority::High: - Sleep(1); + sleep_time = 16; break; } + + EQ::ServerStats::Get().EndNetworkFrame(); + auto end = now + std::chrono::milliseconds(sleep_time); + std::this_thread::sleep_until(end); } _impl->background_loop_timer.release(); diff --git a/common/server_stats.h b/common/server_stats.h new file mode 100644 index 000000000..be300f77c --- /dev/null +++ b/common/server_stats.h @@ -0,0 +1,164 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace EQ +{ + struct ResourceUsageStats + { + double user_cpu_time; + double system_cpu_time; + }; + + struct OSInfoStats + { + std::string system_name; + std::string release; + std::string version; + std::string machine; + }; + + class ServerStats + { + typedef std::chrono::high_resolution_clock clock; + typedef std::chrono::high_resolution_clock::time_point timestamp; + typedef std::chrono::duration seconds; + public: + ~ServerStats() { + } + + static ServerStats& Get() { + static ServerStats inst; + return inst; + } + + void BeginFrame() + { + auto now = clock::now(); + auto inactive = now - m_last_end; + m_inactive_time = std::chrono::duration_cast(inactive).count(); + m_last_begin = now; + } + + void EndFrame() { + auto now = clock::now(); + auto active = now - m_last_begin; + m_active_time = std::chrono::duration_cast(active).count(); + m_last_end = now; + } + + double InactiveFrameTime() const { + return m_inactive_time; + } + + double ActiveFrameTime() const { + return m_active_time; + } + + double FrameTime() const { + return m_inactive_time + m_active_time; + } + + void BeginNetworkFrame() + { + std::lock_guard lock(m_network_lock); + auto now = clock::now(); + auto inactive = now - m_network_last_end; + m_network_inactive_time = std::chrono::duration_cast(inactive).count(); + m_network_last_begin = now; + } + + void EndNetworkFrame() + { + std::lock_guard lock(m_network_lock); + auto now = clock::now(); + auto active = now - m_network_last_begin; + m_network_active_time = std::chrono::duration_cast(active).count(); + m_network_last_end = now; + } + + double NetworkInactiveFrameTime() { + std::lock_guard lock(m_network_lock); + return m_network_inactive_time; + } + + double NetworkActiveFrameTime() { + std::lock_guard lock(m_network_lock); + return m_network_active_time; + } + + double NetworkFrameTime() { + std::lock_guard lock(m_network_lock); + return m_network_inactive_time + m_network_active_time; + } + + //OS Related Stuff + int PID() { + return uv_os_getpid(); + } + + int ParentPID() { + return uv_os_getppid(); + } + + ResourceUsageStats ResourceUsage() { + uv_rusage_t usage; + if(0 != uv_getrusage(&usage)) { + return ResourceUsageStats(); + } + + ResourceUsageStats ret; + ret.user_cpu_time = static_cast(usage.ru_utime.tv_sec) + static_cast(usage.ru_utime.tv_usec / 1000000.0); + ret.system_cpu_time = static_cast(usage.ru_stime.tv_sec) + static_cast(usage.ru_stime.tv_usec / 1000000.0); + return ret; + } + + OSInfoStats OSInfo() { + uv_utsname_t osn; + + if (0 != uv_os_uname(&osn)) { + return OSInfoStats(); + } + + OSInfoStats ret; + ret.system_name = osn.sysname; + ret.release = osn.release; + ret.version = osn.version; + ret.machine = osn.machine; + return ret; + } + + //Compile related things + bool IsDebug() { +#ifdef NDEBUG + return false; +#else + return true; +#endif + } + private: + ServerStats() { + m_last_begin = clock::now(); + m_last_end = clock::now(); + m_network_last_begin = clock::now(); + m_network_last_end = clock::now(); + } + + //Main thread + timestamp m_last_begin; + timestamp m_last_end; + double m_inactive_time; + double m_active_time; + + //Network thread + std::mutex m_network_lock; + timestamp m_network_last_begin; + timestamp m_network_last_end; + double m_network_inactive_time; + double m_network_active_time; + }; +} diff --git a/zone/command.cpp b/zone/command.cpp index 491be25da..079ff1201 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -56,6 +56,7 @@ #include "../common/eqemu_logsys.h" #include "../common/profanity_manager.h" #include "../common/net/eqstream.h" +#include "../common/server_stats.h" #include "data_bucket.h" #include "command.h" @@ -351,7 +352,7 @@ int command_init(void) command_add("scribespells", "[max level] [min level] - Scribe all spells for you or your player target that are usable by them, up to level specified. (may freeze client for a few seconds)", 150, command_scribespells) || command_add("sendzonespawns", "- Refresh spawn list for all clients in zone", 150, command_sendzonespawns) || command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", 0, command_sensetrap) || - command_add("serverinfo", "- Get OS info about server host", 200, command_serverinfo) || + command_add("serverinfo", "- Various info about the current running instance.", 200, command_serverinfo) || command_add("serverrules", "- Read this server's rules", 0, command_serverrules) || command_add("setaapts", "[value] - Set your or your player target's available AA points", 100, command_setaapts) || command_add("setaaxp", "[value] - Set your or your player target's AA experience", 100, command_setaaxp) || @@ -843,22 +844,6 @@ void command_setanim(Client *c, const Seperator *sep) } } -void command_serverinfo(Client *c, const Seperator *sep) -{ -#ifdef _WINDOWS - char intbuffer [sizeof(unsigned long)]; - c->Message(0, "Operating system information."); - c->Message(0, " %s", Ver_name); - c->Message(0, " Build number: %s", ultoa(Ver_build, intbuffer, 10)); - c->Message(0, " Minor version: %s", ultoa(Ver_min, intbuffer, 10)); - c->Message(0, " Major version: %s", ultoa(Ver_maj, intbuffer, 10)); - c->Message(0, " Platform Id: %s", ultoa(Ver_pid, intbuffer, 10)); -#else -char buffer[255]; - c->Message(0, "Operating system information: %s", GetOS(buffer)); -#endif -} - void command_getvariable(Client *c, const Seperator *sep) { std::string tmp; @@ -12371,6 +12356,35 @@ void command_network(Client *c, const Seperator *sep) } } +void command_serverinfo(Client *c, const Seperator *sep) +{ + auto &stats = EQ::ServerStats::Get(); + auto rusage = stats.ResourceUsage(); + auto osn = stats.OSInfo(); + + c->Message(0, "------------------------------------------------"); + c->Message(0, "Process"); + c->Message(0, "------------------------------------------------"); + c->Message(0, "PID: %u", stats.PID()); + c->Message(0, "DEBUG: %s", stats.IsDebug() ? "ON" : "OFF"); + c->Message(0, "%s (%s %s %s)", osn.version.c_str(), osn.system_name.c_str(), osn.release.c_str(), osn.machine.c_str()); + + c->Message(0, "------------------------------------------------"); + c->Message(0, "CPU"); + c->Message(0, "------------------------------------------------"); + c->Message(0, "Usage: User %.2f sec, System %.2f sec", rusage.user_cpu_time, rusage.system_cpu_time); + c->Message(0, "Frame Time (Main): %.2f ms (active: %.2f ms) (inactive: %.2f ms) (%.2f fps)", + stats.FrameTime() * 1000.0, + stats.ActiveFrameTime() * 1000.0, + stats.InactiveFrameTime() * 1000.0, + 1.0 / stats.FrameTime()); + c->Message(0, "Frame Time (Network): %.2f ms (active: %.2f ms) (inactive: %.2f ms) (%.2f fps)", + stats.NetworkFrameTime() * 1000.0, + stats.NetworkActiveFrameTime() * 1000.0, + stats.NetworkInactiveFrameTime() * 1000.0, + 1.0 / stats.NetworkFrameTime()); +} + // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. #ifdef BOTS #include "bot_command.h" diff --git a/zone/mob_movement_manager.cpp b/zone/mob_movement_manager.cpp index cd47270d8..a9180d0d9 100644 --- a/zone/mob_movement_manager.cpp +++ b/zone/mob_movement_manager.cpp @@ -7,13 +7,13 @@ #include "../common/eq_packet_structs.h" #include "../common/misc_functions.h" #include "../common/data_verification.h" +#include "../common/server_stats.h" #include #include #include #include -extern double frame_time; extern Zone *zone; class IMovementCommand @@ -69,7 +69,7 @@ public: } } - auto td = rotate_to_speed * 19.0 * frame_time; + auto td = rotate_to_speed * 19.0 * EQ::ServerStats::Get().FrameTime(); if (td >= dist) { m->SetHeading(to); @@ -186,7 +186,7 @@ public: glm::vec2 dir = tar - pos; glm::vec2 ndir = glm::normalize(dir); - double distance_moved = frame_time * current_speed * 0.4f * 1.45f; + double distance_moved = EQ::ServerStats::Get().FrameTime() * current_speed * 0.4f * 1.45f; if (distance_moved > len) { if (m->IsNPC()) { @@ -313,7 +313,7 @@ public: glm::vec2 dir = tar - pos; glm::vec2 ndir = glm::normalize(dir); - double distance_moved = frame_time * current_speed * 0.4f * 1.45f; + double distance_moved = EQ::ServerStats::Get().FrameTime() * current_speed * 0.4f * 1.45f; if (distance_moved > len) { if (m->IsNPC()) { diff --git a/zone/net.cpp b/zone/net.cpp index dc28a7dbf..28542b9ac 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -71,6 +71,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/event/timer.h" #include "../common/net/eqstream.h" #include "../common/net/servertalk_server.h" +#include "../common/server_stats.h" #include #include @@ -118,7 +119,6 @@ EQEmuLogSys LogSys; const SPDat_Spell_Struct* spells; int32 SPDAT_RECORDS = -1; const ZoneConfig *Config; -double frame_time = 0.0; void Shutdown(); extern void MapOpcodes(); @@ -466,16 +466,24 @@ int main(int argc, char** argv) { std::chrono::time_point frame_prev = std::chrono::system_clock::now(); std::unique_ptr console; - auto loop_fn = [&](EQ::Timer* t) { - //Advance the timer to our current point in time - Timer::SetCurrentTime(); + int sleep_time = 16; + while (RunLoops) { + auto now = std::chrono::high_resolution_clock::now(); - /** - * Calculate frame time - */ - std::chrono::time_point frame_now = std::chrono::system_clock::now(); - frame_time = std::chrono::duration_cast>(frame_now - frame_prev).count(); - frame_prev = frame_now; + EQ::ServerStats::Get().BeginFrame(); + bool previous_loaded = is_zone_loaded && numclients > 0; + Timer::SetCurrentTime(); + EQ::EventLoop::GetDefault().Process(); + bool current_loaded = is_zone_loaded && numclients > 0; + + if (previous_loaded && !current_loaded) { + sleep_time = 100; + eqsm->SetPriority(EQStreamPriority::Low); + } + else if (!previous_loaded && current_loaded) { + sleep_time = 16; + eqsm->SetPriority(EQStreamPriority::High); + } /** * Telnet server @@ -512,7 +520,7 @@ int main(int argc, char** argv) { eqsm->OnNewConnection([&stream_identifier](std::shared_ptr stream) { stream_identifier.AddStream(stream); - LogF(Logs::Detail, Logs::World_Server, "New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); + LogF(Logs::Detail, Logs::World_Server, "New connection from IP {0}:{1}", stream->GetRemoteAddr(), ntohs(stream->GetRemotePort())); }); } @@ -581,33 +589,10 @@ int main(int argc, char** argv) { database.ping(); entity_list.UpdateWho(); } - }; - EQ::Timer process_timer(loop_fn); - process_timer.Start(1000, true); - - while (RunLoops) { - bool previous_loaded = is_zone_loaded && numclients > 0; - EQ::EventLoop::GetDefault().Process(); - - bool current_loaded = is_zone_loaded && numclients > 0; - if (previous_loaded && !current_loaded) { - process_timer.Stop(); - process_timer.Start(1000, true); - eqsm->SetPriority(EQStreamPriority::Low); - } - else if (!previous_loaded && current_loaded) { - process_timer.Stop(); - process_timer.Start(32, true); - eqsm->SetPriority(EQStreamPriority::High); - } - - if (current_loaded) { - Sleep(1); - } - else { - Sleep(10); - } + EQ::ServerStats::Get().EndFrame(); + auto end = now + std::chrono::milliseconds(sleep_time); + std::this_thread::sleep_until(end); } entity_list.Clear(); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 1e38b481b..cddf71846 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -18,6 +18,7 @@ #include "../common/rulesys.h" #include "../common/string_util.h" +#include "../common/server_stats.h" #include "client.h" #include "entity.h" @@ -27,8 +28,6 @@ #include -extern double frame_time; - int Mob::GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target) { int base = EQEmu::skills::GetBaseDamage(skill); @@ -1033,7 +1032,7 @@ void Mob::ProjectileAttack() ProjectileAtk[i].skill = 0; ProjectileAtk[i].speed_mod = 0.0f; } else { - ProjectileAtk[i].increment += 1000 * frame_time; + ProjectileAtk[i].increment += 1000 * EQ::ServerStats::Get().FrameTime(); } }