Higher RemoteFunction/Event refresh rates

To my understanding they only refresh (send/recieve) at 20hz. That’s 0.05 seconds or about 3 frames (@60FPS) between each batch being sent/recieved. That’s pretty big for anything competitive.

If we could please have it upgraded to 30 (the old standard for FPS/competitive games) that would be great. If we could have it set to 45 (The Betafield 4 standard) that would be even better. If we could have it set to 60 (The Battlefield 1 standard) that would be absolutely amazing. I understand there are probably some performance reasons for having it lower but if we could have some sort of way to send/receive these faster we could make FPS games play smoother.

I’m sure other games that rely on frame-by-frame action would benefit as well. Things like racing games and other actiony genres.

15 Likes

Something like this I think would best work configurable. For example, FPS games like phantom forces could set it to 60hz, and a game like lumber tycoon, etc would only need 20 hz

2 Likes

Probably. I’m worried that it’s hooked up to the entire system and can’t be changed without changing the entire system.

If that’s the case we’d be incredibly lucky if they just gave us 30.

If it’s just a number somewhere then we’d be golden.

2 Likes

I imagine the limit on RemoteEvents/Functions is because there’s a limit on network traffic. ROBLOX networking works at 20 Hz, so you can’t go any faster than that with RemoteFunctions/Events. At RDC 2016 they said they were going to improve a lot of network-related stuff though (60 Hz instead of 20 Hz, better interpolating, etc), so when that happens I guess that’ll satisfy the feature request.

2 Likes

Maybe not necessarily a higher rate, but a variable rate, so that i.e. mobile can keep 20 and people with a desktop can have it at the 45/60 you’re pointing out. (based on device/network performance)

5 Likes

Delay is pretty much inevitable, so you need to work around it.

Usually, ROBLOX’s ping are usually around 100ms, and at the lowest 40ms, what you are asking for is a consistent 15ms ping, and that isn’t possible considering the distance variation between clients and the server.

If I were you, I would interpolate the characters on the client-side to predict the motion of the character, then predict this interpolation difference on the server when considering what the player is shooting at. Or… something like that at least, hopefully you get the idea lol.

3 Likes

That’s not at all what I’m asking for dude. I want more frequent updates so I can do things like extrapolate more accurately and know who fired bullets first more accurately

1 Like

The client can send as many packets as they want per second, they don’t have to wait until the server received and processed a request before they can send another.

1 Like

cc @zeuxcg

As someone who just finished a custom character replication system… I can say a 50% bump to 30hz would be heavenly. 45 would be an ABSOLUTE GODSEND.

Working on a game with spaceships and can confirm this would help a lot.

1 Like

Why is this not a thing yet? It’s 2024… I understand that mobile and lower-end devices might suffer because of this, but at least make it configurable, so the devs will have the final say about the drawbacks…

1 Like

If you’re sending a RemoteEvent that quickly, wouldn’t UnreliableRemoteEvents work for this?

1 Like

I was talking about the roblox replication refresh rate (the rate character’s positions are updated), the current one is 20Hz. It isn’t enough for roblox competitive games, and the only solution is to make a custom replication system, which is really tedious to make, especially if you already have a game in development.

1 Like

Are you sure it’s 20hz? From my own testing, it seems to be 60hz.

Code

Client:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")

local NetRateRem = ReplicatedStorage.NetRateTest
local ChangeRateRem = ReplicatedStorage.ChangeRate

local UI = script.Parent

local Label = UI.Calculated
local RateChangeBox = UI.NewRate

local CurrentFrameId = 0
local LastEventFrameId = 0
local EventsInSameFrame = 0

local TargetRate = 60

RateChangeBox.FocusLost:Connect(function()
	local NewRate = tonumber(RateChangeBox.Text) or 60
	
	-- so that the server script can just step multiple times with a very simple division
	if NewRate > 60 then
		NewRate = math.ceil(NewRate / 60) * 60
	end
	
	ChangeRateRem:FireServer(NewRate)
	
	RateChangeBox.Text = `Try to reach rate: {NewRate}`
	
	TargetRate = NewRate
end)

RunService.PreRender:Connect(function()
	CurrentFrameId += 1
	EventsInSameFrame = 0
end)

NetRateRem.OnClientEvent:Connect(function()
	if CurrentFrameId == LastEventFrameId then
		EventsInSameFrame += 1
	end
	
	LastEventFrameId = CurrentFrameId
end)

while true do
	if EventsInSameFrame > 0 then
		print(`Event fired multiple times in a single frame ({EventsInSameFrame} times), net rate is lower than the fire rate!`)
	end
	
	Label.Text = `Actual rate: {TargetRate/(EventsInSameFrame + 1)} (EISF: {EventsInSameFrame})`
	
	task.wait(1/60)
end

Server:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local NetRateTest = ReplicatedStorage.NetRateTest
local ChangeRate = ReplicatedStorage.ChangeRate

local Rate = 60

ChangeRate.OnServerEvent:Connect(function(_, NewRate)
	Rate = NewRate
	
	print(`The new rate is {Rate}`)
end)

wait(1)

while true do
	if Rate >= 60 then
		for _ = 1, Rate / 60 do
			NetRateTest:FireAllClients()
		end
		
		task.wait(1/60)
	else
		NetRateTest:FireAllClients()
		
		task.wait(1/Rate)
	end
end

The “Try to Reach Rate” (“Target Rate”) is how many times, per second, the Server will fire the Remote Event. The Actual Rate is how many times the event is received per second, divided by how many times the event fires in the same frame (also given as “EISF”). My reasoning for this is that Roblox should process packets as soon as they arrive, so if they manually add a little bit of delay to try to hide the network rate, this doesn’t work. If multiple events occur in the same frame, it indicates Roblox has accumulated them and sent them as a single packet due to the event being fired at a rate faster than the connections can actually be replicated, which can be shown by setting a ludicrously high Target like 4020 (:FireAllClients/second).

Unfortunately, I can’t record a video of it in action because the Recorder seems to cap my FPS at 60 regardless of what I set in the Menu through that new option, which, on my non-stellar internet connection, causes multiple packets to be handled per frame, which doesn’t happen when I’m running above 60 FPS.

With the Target set to 60, nothing interesting happens, the Actual Rate is just the Target Rate without any kind of “snapping” to a lower value, meaning the system can consistently send and receive that many packets per second without any of them being accumulated together.

If I set the Target Rate above 60, the Actual Rate will snap and flash between the Target and exactly 60, showing that the networking rate must be 60hz (excluding the aforementioned potential caveat) as that is the maximum rate the system can consistently hit, going above that means the Server is firing the event multiple times per Server frame, as the Server runs at 60 FPS. So, hypothetically, if the networking rate is below 60hz (like, for example, 20hz), then the same jitter should be observed even while the Target is below the Server FPS and above 20, which, it doesn’t.

Actual Rate = Target / (EISF + 1)

TL;DR:

  • Target Rate below or at 60 (and Client framerate above Target Rate): Actual Rate is Target Rate
  • Target Rate above 60 (eg: 4020): Actual Rate frequently snaps to 60, EISF is 66 (4020/(66+1) = 60)

So, above 60hz, multiple packets are processed per frame because the Server runs at 60 FPS, but just below or at 60hz, only one event per Server frame and one event per Client frame, which I have equated to a single network packet. If the networking rate was 20hz, we should expect to see the Client receive 3 events per frame, as 60 :FireAllClients per second / 20hz networking rate = 3 packets per frame, which doesn’t happen.

Unless I’ve done something wrong.