I’ve been measuring latency of remoteevents in Play Solo and I have recorded three values that pop up: ~0ms, ~17ms, ~34ms. These 3 values do not deviate beyond 1 millisecond, and these are the only values that would pop up; this suggests that there is 0 latency of the remote actually firing (which makes sense because it’s in Play Solo, so both the client and server are run locally) and that the task scheduler is choosing to wait one or two cycles before actually firing the signal.
I verified this phenomena in a game server by measuring the same delay and comparing most frequent values. I found that there were two extremely common deltas that were consistently printed out, and after doing the math, I found that they were separated by 17 ms (which lines up). I found a third, much rarer value which was again separated by another 17 ms, further backing up this idea of the task manager waiting 0-2 frames before firing the remote.
Replication Send Jobs:
Outgoing property updates and event firings are sent. Does not happen every frame.
I find this total lack of control over this behavior to be frustrating, because I am only trying to send a signal once a second, so being consistently throttled for such a low network load is insulting.
Because ROBLOX decided that’s how it wants to do things
But also, network comms are unreliable anyways, and you shouldn’t rely on remotes being sent exactly on time, since they won’t be received on time in any case.
Well, packet loss and inconsistencies stemming from that are understandable. But, these shifts caused by the task scheduler changing its mind about when it wants to fire the remote is just unnecessary flux and I want to eliminate it. This is ridiculous.
It’s because Roblox enforces a reliable-ordered communication channel, i.e. they make sure that all messages are received in the correct order.
This necessitates waiting some amount of time before firing the event handler, because you need to know that the other side knows that you’ve received the message and that there weren’t any events that should fire first but arrived later.
Roblox chose to do this synchronously with Heartbeat.
I mean, it makes sense. You have some point in time every frame where all Lua tasks are handled, which makes a lot of things a lot less complicated. However, this is really bad for anything where timing is critical, such as replicating movement updates (or worse, physics that require interaction between parts simulated locally on the clients).