RevenantX/LiteNetLib

Calling TriggerUpdate() with a loop that's faster than 1ms causes timeouts to happen much earlier

TomTheFurry opened this issue · 1 comments

The TriggerUpdate() method is added to (I presume?) resolve the issue where in Windows, the Thread.sleep(...) method has quite a large minimal sleep time. Therefore, the doc as said here suggests using TriggerUpdate() for faster update time.

However, the underlying code that keep tracks of time elapsed uses millisecond integer, with the delta time between each actual update set with the minimum of 1 ms, seen here:

ProcessDelayedPackets();
int elapsed = (int)stopwatch.ElapsedMilliseconds;
elapsed = elapsed <= 0 ? 1 : elapsed;
stopwatch.Restart();

Additionally, the use of casting stopwatch milliseconds into integer caps the accuracy of the stopwatch to 1 millisecond, even if the underlying stopwatch implementation supports higher accuracy.

This wasn't (much) of an issue normally, where the default update time is set to 15 ms. (Note however even with default time, the timeout would still drift a bit and not be that accurate. But that's not really a real problem.) However, when TriggerUpdate() is added, user of the lib can now drive the update loop to as fast as it can goes, bypassing the previous bottleneck of Thread.sleep(). This includes driving the loop to faster than 1 ms, by a lot.

In our team's use case, we have an issue where tons of packets are sent (to localhost server), and the lib cannot keep up with processing all the packets. To 'patch' this issue, I changed from using the update time parameter to directly calling TriggerUpdate() on the main uncapped loop, with speed sometimes up to 100th of a millisecond. We however noticed that seemingly on random, the server kicked the client for timeout, even though around 5 packets are sent every 50 millisecond, and that our timeout setting is 5000, or 5 seconds.

After some investigation, I discovered that because the server is calling the TriggerUpdate() as fast as possible, the timeout is reached in much less than a millisecond, due to the above mentioned issue. This causes erroneous timeouts of the client.

Library version: 1.0.0-rc3, but can confirm issue still exists with latest commit (47d5721).

Framework: C# .NET with custom in-house game engine

OS: Tested on Windows

Suggestion: The proper fix is to change all time variable to 'float ms' or 'long ns'. However, if the library does not wish to support high-frequency updates, either proper documentation should be added to TriggerUpdate() warning against less-than 1 ms calls, or that the function should itself skips updating if the elapsed <= 0.

@TomTheFurry TriggerUpdate() is mostly used to send packets after game logic generated them (so it's mostly called like at 30-60 FPS rate), that's why this situation isn't checked. I will look what i can do with this