diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp index 7578a2af7..ba77d7a7f 100644 --- a/src/server/apps/worldserver/Main.cpp +++ b/src/server/apps/worldserver/Main.cpp @@ -72,13 +72,15 @@ char serviceDescription[] = "AzerothCore World of Warcraft emulator world servic * 2 - paused */ int m_ServiceStatus = -1; + +#include +#include #endif #ifndef _ACORE_CORE_CONFIG #define _ACORE_CORE_CONFIG "worldserver.conf" #endif -#define WORLD_SLEEP_CONST 10 using namespace boost::program_options; namespace fs = std::filesystem; @@ -137,6 +139,44 @@ int main(int argc, char** argv) return WinServiceUninstall() == true ? 0 : 1; else if (configService.compare("run") == 0) WinServiceRun(); + + Optional newTimerResolution; + boost::system::error_code dllError; + std::shared_ptr winmm(new boost::dll::shared_library("winmm.dll", dllError, boost::dll::load_mode::search_system_folders), [&](boost::dll::shared_library* lib) + { + try + { + if (newTimerResolution) + lib->get("timeEndPeriod")(*newTimerResolution); + } + catch (std::exception const&) + { + // ignore + } + + delete lib; + }); + + if (winmm->is_loaded()) + { + try + { + auto timeGetDevCapsPtr = winmm->get("timeGetDevCaps"); + // setup timer resolution + TIMECAPS timeResolutionLimits; + if (timeGetDevCapsPtr(&timeResolutionLimits, sizeof(TIMECAPS)) == TIMERR_NOERROR) + { + auto timeBeginPeriodPtr = winmm->get("timeBeginPeriod"); + newTimerResolution = std::min(std::max(timeResolutionLimits.wPeriodMin, 1u), timeResolutionLimits.wPeriodMax); + timeBeginPeriodPtr(*newTimerResolution); + } + } + catch (std::exception const& e) + { + printf("Failed to initialize timer resolution: %s\n", e.what()); + } + } + #endif // Add file and args in config @@ -522,9 +562,15 @@ void ShutdownCLIThread(std::thread* cliThread) void WorldUpdateLoop() { + uint32 minUpdateDiff = uint32(sConfigMgr->GetOption("MinWorldUpdateTime", 1)); uint32 realCurrTime = 0; uint32 realPrevTime = getMSTime(); + uint32 maxCoreStuckTime = uint32(sConfigMgr->GetOption("MaxCoreStuckTime", 60)) * 1000; + uint32 halfMaxCoreStuckTime = maxCoreStuckTime / 2; + if (!halfMaxCoreStuckTime) + halfMaxCoreStuckTime = std::numeric_limits::max(); + LoginDatabase.WarnAboutSyncQueries(true); CharacterDatabase.WarnAboutSyncQueries(true); WorldDatabase.WarnAboutSyncQueries(true); @@ -536,18 +582,19 @@ void WorldUpdateLoop() realCurrTime = getMSTime(); uint32 diff = getMSTimeDiff(realPrevTime, realCurrTime); + if (diff < minUpdateDiff) + { + uint32 sleepTime = minUpdateDiff - diff; + if (sleepTime >= halfMaxCoreStuckTime) + LOG_ERROR("server.worldserver", "WorldUpdateLoop() waiting for {} ms with MaxCoreStuckTime set to {} ms", sleepTime, maxCoreStuckTime); + // sleep until enough time passes that we can update all timers + std::this_thread::sleep_for(Milliseconds(sleepTime)); + continue; + } sWorld->Update(diff); realPrevTime = realCurrTime; - uint32 executionTimeDiff = getMSTimeDiff(realCurrTime, getMSTime()); - - // we know exactly how long it took to update the world, if the update took less than WORLD_SLEEP_CONST, sleep for WORLD_SLEEP_CONST - world update time - if (executionTimeDiff < WORLD_SLEEP_CONST) - { - std::this_thread::sleep_for(Milliseconds(WORLD_SLEEP_CONST - executionTimeDiff)); - } - #ifdef _WIN32 if (m_ServiceStatus == 0) World::StopNow(SHUTDOWN_EXIT_CODE); @@ -583,10 +630,14 @@ void FreezeDetector::Handler(std::weak_ptr freezeDetectorRef, bo freezeDetector->_worldLoopCounter = worldLoopCounter; } // possible freeze - else if (getMSTimeDiff(freezeDetector->_lastChangeMsTime, curtime) > freezeDetector->_maxCoreStuckTimeInMs) + else { - LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!"); - ABORT(); + uint32 msTimeDiff = getMSTimeDiff(freezeDetector->_lastChangeMsTime, curtime); + if (msTimeDiff > freezeDetector->_maxCoreStuckTimeInMs) + { + LOG_ERROR("server.worldserver", "World Thread hangs for {} ms, forcing a crash!", msTimeDiff); + ABORT("World Thread hangs for {} ms, forcing a crash!", msTimeDiff); + } } freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(1)); diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 57bd54b48..99dfc9cc0 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -339,12 +339,19 @@ SocketTimeOutTimeActive = 60000 SessionAddDelay = 10000 +# +# MinWorldUpdateTime +# Description: Minimum time (milliseconds) between world update ticks (for mostly idle servers). +# Default: 1 - (0.001 second) + +MinWorldUpdateTime = 1 + # # MapUpdateInterval # Description: Time (milliseconds) for map update interval. -# Default: 100 - (0.1 second) +# Default: 10 - (0.01 second) -MapUpdateInterval = 100 +MapUpdateInterval = 10 # # ChangeWeatherInterval diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index fe3124aa5..cc4577f29 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -39,7 +39,7 @@ class ObjectGuid; #define CENTER_GRID_OFFSET (SIZE_OF_GRIDS/2) #define MIN_GRID_DELAY (MINUTE*IN_MILLISECONDS) -#define MIN_MAP_UPDATE_DELAY 10 +#define MIN_MAP_UPDATE_DELAY 1 #define SIZE_OF_GRID_CELL (SIZE_OF_GRIDS/MAX_NUMBER_OF_CELLS) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 59936d0a2..ca0b3c206 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -666,7 +666,7 @@ void World::LoadConfigSettings(bool reload) _int_configs[CONFIG_MIN_LEVEL_STAT_SAVE] = 0; } - _int_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfigMgr->GetOption("MapUpdateInterval", 100); + _int_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfigMgr->GetOption("MapUpdateInterval", 10); if (_int_configs[CONFIG_INTERVAL_MAPUPDATE] < MIN_MAP_UPDATE_DELAY) { LOG_ERROR("server.loading", "MapUpdateInterval ({}) must be greater {}. Use this minimal value.", _int_configs[CONFIG_INTERVAL_MAPUPDATE], MIN_MAP_UPDATE_DELAY); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index bb1ec8b84..f8e0de9b8 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -141,8 +141,6 @@ enum WorldStates WS_DAILY_CALENDAR_DELETION_OLD_EVENTS_TIME = 20008 // Next daily calendar deletions of old events time }; -#define WORLD_SLEEP_CONST 10 - // xinef: petitions storage struct PetitionData { diff --git a/src/server/shared/Network/NetworkThread.h b/src/server/shared/Network/NetworkThread.h index abe0c0e27..aec20a16a 100644 --- a/src/server/shared/Network/NetworkThread.h +++ b/src/server/shared/Network/NetworkThread.h @@ -121,8 +121,8 @@ protected: { LOG_DEBUG("misc", "Network Thread Starting"); - _updateTimer.expires_from_now(boost::posix_time::milliseconds(10)); - _updateTimer.async_wait(std::bind(&NetworkThread::Update, this)); + _updateTimer.expires_from_now(boost::posix_time::milliseconds(1)); + _updateTimer.async_wait([this](boost::system::error_code const&) { Update(); }); _ioContext.run(); LOG_DEBUG("misc", "Network Thread exits"); @@ -135,8 +135,8 @@ protected: if (_stopped) return; - _updateTimer.expires_from_now(boost::posix_time::milliseconds(10)); - _updateTimer.async_wait(std::bind(&NetworkThread::Update, this)); + _updateTimer.expires_from_now(boost::posix_time::milliseconds(1)); + _updateTimer.async_wait([this](boost::system::error_code const&) { Update(); }); AddNewSockets();