diff --git a/README.md b/README.md index 18aff57..53bbe51 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Upon its first run, LeviOptimize will generate configuration files in `mod/LeviO - **`optMovingBlock`**: Improves FPS and TPS by optimizing block entities' processing during piston movements. - **`fixChunkLeak`**: Addresses the issue of chunks remaining loaded after players leave. - **`fixPowerAssocMap`**:Fixes the memory leak issue in PowerAssociationMap. from [glibcxx/PowerAssociationMapLeakFix](https://github.com/glibcxx/PowerAssociationMapLeakFix) +- **`fixTimer`**: Fixes the accuracy issue of timer. from [glibcxx/TimerFix](https://github.com/glibcxx/TimerFix) - **`optSeenPercent`**: Caches "SeenPercent" values to improve TPS, with a note of caution regarding potential hash collisions. - **`optPacketSender`**: Optimizes the packet sending process to improve server performance. >Some mods rely on this feature to achieve multi-threaded sending packets, it is best to keep it enabled. diff --git a/README.zh.md b/README.zh.md index 7904dfd..d67161c 100644 --- a/README.zh.md +++ b/README.zh.md @@ -53,6 +53,7 @@ lip install github.com/LiteLDev/LeviOptimize - **`optMovingBlock`**:通过优化活塞运动过程中的方块实体处理来提高FPS和TPS。 - **`fixChunkLeak`**:修复玩家离开后区块未卸载的问题。 - **`fixPowerAssocMap`**:修复PowerAssociationMap的内存泄漏问题。from [glibcxx/PowerAssociationMapLeakFix](https://github.com/glibcxx/PowerAssociationMapLeakFix) +- **`fixTimer`**: 修复计时器的精度问题。 from [glibcxx/TimerFix](https://github.com/glibcxx/TimerFix) - **`optSeenPercent`**:缓存特定坐标及其相应边界框内的“SeenPercent”值,以提高TPS。由于存在哈希冲突可能导致性能下降的可能性,实际效果不确定。 - **`optPacketSender`**:优化数据包发送过程以提高服务器性能。 > 一些插件依赖此功能以实现多线程发包,最好保持启用。 diff --git a/src/levioptimize/Config.h b/src/levioptimize/Config.h index ce4c67b..48b1ff8 100644 --- a/src/levioptimize/Config.h +++ b/src/levioptimize/Config.h @@ -7,13 +7,14 @@ namespace lo { using ll::reflection::Dispatcher; struct Config { - int version = 12; + int version = 13; struct { Dispatcher optMovingBlock = true; Dispatcher optHopperItem = true; Dispatcher fixChunkLeak = true; Dispatcher fixPowerAssocMap = true; + Dispatcher fixTimer = true; Dispatcher optSeenPercent = true; Dispatcher packetSenderOpt = true; Dispatcher playerLookupOpt = true; diff --git a/src/levioptimize/features/PowerAssociationMapLeakFix.cpp b/src/levioptimize/features/PowerAssociationMapLeakFix.cpp index 86580ed..f75ea6f 100644 --- a/src/levioptimize/features/PowerAssociationMapLeakFix.cpp +++ b/src/levioptimize/features/PowerAssociationMapLeakFix.cpp @@ -16,15 +16,15 @@ LL_TYPE_INSTANCE_HOOK( &CircuitSceneGraph::removeStaleRelationships, void ) { - for (auto iterUpdate = this->mPendingUpdates.begin(); iterUpdate != this->mPendingUpdates.end(); iterUpdate++) { + for (auto iterUpdate = mPendingUpdates.begin(); iterUpdate != mPendingUpdates.end(); iterUpdate++) { BlockPos& posUpdate = *iterUpdate->second.mPos; - auto powerAssociationIter = this->mPowerAssociationMap.find(posUpdate); - if (powerAssociationIter != this->mPowerAssociationMap.end()) { + auto powerAssociationIter = mPowerAssociationMap.find(posUpdate); + if (powerAssociationIter != mPowerAssociationMap.end()) { CircuitComponentList& relationships = powerAssociationIter->second; for (auto perChunkIter = relationships.mComponents.begin(); perChunkIter != relationships.mComponents.end();) { perChunkIter = relationships.mComponents.erase(perChunkIter); - auto allListIter = this->mAllComponents.find(perChunkIter->mPos); + auto allListIter = mAllComponents.find(perChunkIter->mPos); if (allListIter != mAllComponents.end()) { allListIter->second->removeSource(posUpdate, iterUpdate->second.mRawComponentPtr); } diff --git a/src/levioptimize/features/TimerFix.cpp b/src/levioptimize/features/TimerFix.cpp new file mode 100644 index 0000000..992e38a --- /dev/null +++ b/src/levioptimize/features/TimerFix.cpp @@ -0,0 +1,103 @@ +#include "features.h" +#include "ll/api/memory/Hook.h" +#include "mc/util/Timer.h" + +namespace lo::timer_fix { +float inlineClamp(float value, float min, float max) { return (value < min) ? min : (value > max) ? max : value; } + +using namespace ll::literals; + +class FixedTimer : public Timer { +public: + FixedTimer( + float ticksPerSecond, + ::std::function getTimeMSCallback = []() -> int64 { return Timer::getMillisecondsSinceLaunch(); } + + ) + : Timer(ticksPerSecond, getTimeMSCallback) {} + + double mLastTimeSecondsFixed = mLastMs * 0.001; +}; + +LL_STATIC_HOOK( + TimerCtorHook, + ll::memory::HookPriority::Highest, + "??$make_unique@VTimer@@AEBH$0A@@std@@YA?AV?$unique_ptr@VTimer@@U?$default_delete@VTimer@@@std@@@0@AEBH@Z"_sym, + std::unique_ptr, + int const& ticksPerSecond +) { + return std::make_unique(ticksPerSecond); +} + +LL_TYPE_INSTANCE_HOOK( + TimerUpdateHook, + ll::memory::HookPriority::Highest, + FixedTimer, + &Timer::advanceTime, + void, + float preferredFrameStep +) { + if (mSteppingTick >= 0) { + if (mSteppingTick) { + mTicks = 1; + --mSteppingTick; + } else { + mTicks = 0; + mAlpha = 0.0f; + } + } else { + int64 nowMs = mGetTimeMSCallback.get()(); + int64 passedMs = nowMs - mLastMs; + if (passedMs > 1000) { + int64 passedMsSysTime = nowMs - mLastMsSysTime; + if (passedMsSysTime == 0) { + passedMsSysTime = 1; + passedMs = 1; + } + double adjustTimeT = (double)passedMs / (double)passedMsSysTime; + mAdjustTime += (adjustTimeT - mAdjustTime) * 0.2; + mLastMs = nowMs; + mLastMsSysTime = nowMs; + } + if (passedMs < 0) { + mLastMs = nowMs; + mLastMsSysTime = nowMs; + } + double passedSeconds = (nowMs * 0.001 - mLastTimeSecondsFixed) * mAdjustTime; // the key modification + mLastTimeSeconds = mLastTimeSecondsFixed = nowMs * 0.001; + if (preferredFrameStep > 0.0f) { + float newFrameStepAlignmentRemainder = inlineClamp( + mFrameStepAlignmentRemainder + preferredFrameStep - passedSeconds, + 0.0f, + 4.0f * preferredFrameStep + ); + passedSeconds -= mFrameStepAlignmentRemainder - newFrameStepAlignmentRemainder; + mFrameStepAlignmentRemainder = newFrameStepAlignmentRemainder; + } + if (passedSeconds < 0.0) passedSeconds = 0.0; + if (passedSeconds > 0.1) passedSeconds = 0.1; + mLastTimestep = passedSeconds; + mPassedTime += passedSeconds * mTimeScale * mTicksPerSecond; + mTicks = mPassedTime; + mPassedTime -= mTicks; + if (mTicks > 10) mTicks = 10; + mAlpha = mPassedTime; + } +} + +struct TimerFix::Impl { + ll::memory::HookRegistrar r; +}; + +void TimerFix::call(bool enable) { + if (enable) { + if (!impl) impl = std::make_unique(); + } else { + impl.reset(); + } +} + +TimerFix::TimerFix() = default; +TimerFix::~TimerFix() = default; + +} // namespace lo::timer_fix \ No newline at end of file diff --git a/src/levioptimize/features/features.h b/src/levioptimize/features/features.h index db40923..2b19172 100644 --- a/src/levioptimize/features/features.h +++ b/src/levioptimize/features/features.h @@ -46,6 +46,18 @@ struct PowerAssociationMapLeakFix { ~PowerAssociationMapLeakFix(); }; } // namespace power_association_map_leak_fix + +namespace timer_fix { +struct TimerFix { + struct Impl; + std::unique_ptr impl; + + void call(bool); + TimerFix(); + ~TimerFix(); +}; +} // namespace chunk_leak_fix + namespace push_entity_opt { struct Config { bool enable = true;