Introducing UnreliableRemoteEvents

An average is near useless when you don’t know how good a connection will be in advance. Simply don’t make any assumptions about packet loss frequency.

What are you sending once per second?

Also, please read an earlier response of mine.

3 Likes

Seems sort of pointless but cool

1 Like

im gonna have to say the obvious here but i think this is pretty unreliable

3 Likes

So, when you use these and you don’t care if an event is dropped, but you don’t really want to process them out of order, do you use some sort of tick timestamp to disregard processing an ‘older’ event after a newer one has been received ?

Yes.

Awesome minimum characters!

1 Like

In a game I work on we have the orientation of someone’s head, neck and torso based on the angle of their camera. Let’s call this “HeadRotation”

It sends these three CFrames to the server each 8 heartbeats. (IF these have changed, that is)

The server stores whatever the clients send over and sends the entire list to all clients each 8 heartbeats. (I am aware that it is not synced up with the heartbeats on the clients)

Now this feels like an opportunity, but how many bytes do CFrames need?

I think I’ll go for one unreliable request to the server and an unreliable request to all clients for each user’s HeadRotation separately.

Will that work? Will that be better? I have no insight on how the network exactly behaves.

If CFrames take too much bytes, do I have to encode them with a tiny quality loss?

I honestly don’t know, feedback is appreciated.

I would use a RemoteFunction, it will yield until the server has processed the request.

(Firing the RemoteEvent once is already more than enough to cause the server to process the event; the only thing you need is to wait for the server to finish processing the request. Which is something RemoteFunctions do amazingly.)

1 Like

I would rather use a normal RemoteEvent in your case unless you are only comparing then with previously sent packets.
You would actually have to wait for the future packets to arrive if you wanted to compare them to the future packets.
Most you can do is compare it to previously received packets or else you might be waiting for nothing since some of the future packets might be dropped.

Generally I’d not use them if:

  • you don’t want dropped packets;
  • order matters a lot; or
  • you can replace the functionality with something that runs entirely on the client.

Wow, thanks! They actually worked perfectly in my case.

1 Like

It’s an ideal use case for unreliable remotes since it (presumably) purely visual.

CFrames are compressed into up to 20 bytes by the engine if you send them over the network so would be up to 5 kb/s plus some additional overhead for each time a remote event is used (so on the server you should ideally use only a single remote event call to send all head rotations which you already seem to be doing) if you send 8 of them per second for 32 players.

For optimization you probably shouldn’t be using CFrames but use Vector3s instead since all you probably need to send is the look vector of the CFrame/the direction the character is looking at which would bring that down to 12 bytes.

Since that look vector is always a unit vector you could even further compress that to avoid sending redundant information over the network by projecting the coordinates of the sphere that make up all possible unit vectors onto a 2D plane in which case you should be able to compress it down to 4 bytes if you want it to be reasonably precise or even just 2 bytes if you don’t mind a maximum error of 1-2 degrees, though this probably isn’t really necessary for you.

1 Like

Replication =/= remote

Replication event is past tense.

Thanks for the tip!

Encoding HeadRotation
I’ve decided to only grab the X and Y of the LookVector as I’d only need these for all of the custom player models to calculate the three CFrames, next to that I’m now sending the head rotation to the server at either each change of at least 0.01 on the LookVector.X or LookVector.Y OR if 300 heartbeats have passed.

Next to that I’ve encoded the LookVector.X and LookVector.Y into one number ranging 0-999999 allowing me up to a precision of 0.002 ranging from -1 to 0.998 for both LookVector.X and LookVector.Y.

On the server I’ve decided to make all nearby players replicate to the target player each time, but far away players only replicate per <x> heartbeats, variable per distance.

The server then sends an array of {UserId, Encoded LookVector, UserId, Encoded LookVector, etc.} for all the selected players to replicate to the target user.

The target user then decodes this LookVector and calculates the CFrames for the head rotation, this happens in parallel scripting.
Then in synchronised scripting it applies all the CFrames.

Further encoding + Use of buffers
I think that would take about 18 bytes per player, but I can make it smaller by removing the need for a userid.
I could give all users in the server a separate attribute instead of an id, and re-use ids that are no longer used; having around 45 “ids” as attributes, which still fit in the same 8 bytes number.
Even smaller would be possible if Roblox releases buffers I think;
I could store it as:
1 8-bit unsigned int for the attribute “id”
1 8-bit unsigned int for LookVector.X with precision 1/256th
1 8-bit unsigned int for LookVector.Y with precision 1/256th
Theoretically letting me use 3*playerquantity bytes.

Feedback?
I wonder if that’s the most I can do and if my optimisations are good, but right now this should be sufficient for a full server (45 players for the target experience) through one single packet. Correct me if I’m wrong

Another case
Next to that I decided to make analytics CLICK-EVENT calls (I store analytics data to see what buttons are clicked the most) unreliable too. These events fire each click on a gui button and aren’t too important, a little unreliability is fine on these I think. Though I can still encode these events to a number for each type of event. And I could queue them, sending multiple of them together and using buffers.

Ps: Sorry for going slightly offtopic, as encoding with buffers should fall under the other topic

Presumably you are calculating LookVector.Z from X and Y since it is a unit vector but this won’t work without also sending the sign of Z, a better way is to use a spherical coordinate system, calculate the polar and azimuthal angles for a given unit vector and send those instead which will also give you better maximum precision.

In Lua it should look something like this:

local function unitVector3ToAngles(v3 : Vector3) : (number, number)
	local theta = math.acos(v3.Y)
	local XZLength = (v3.X^2 + v3.Z^2)^0.5 
	local phi = XZLength < 0.00001 and 0 or (v3.Z < 0 and -1 or 1) * math.acos(v3.X / XZLength)
	return theta, phi
end

local function unitVector3FromAngles(theta : number, phi : number) : Vector3
	return Vector3.new(math.sin(theta) * math.cos(phi), math.cos(theta), math.sin(theta) * math.sin(phi))
end

With theta being a number between 0 and math.pi and phi being a number between -math.pi and math.pi

2 Likes

I dont understand the use cases for this event. If the event can be dropped, then it cannot be used for any critical aspect of gameplay, such as rendering bullet traces and/or hit detection events.

While it could be used for sounds and effects, gun sounds, vehicle sounds, etc are all incredibly important in the sound design of any shooter or driving game, so dropping events will cause confusion and make sound unreliable for gameplay.

Maybe at some point, however, we can have some sort of unreliable remote system that instead of manually sending events, sends events automatically to update values as modified by client/server for client-server communication of information like engine pitch, ammo counts, health / hit points, so on.

Just a note, roblox supports ternary operators. Should replace uses of condition and value or alternative with if condition then value else alternative. Cooperates better with type hints / Luau, and makes full use of short circuit eval

Just because you don’t understand the use cases, it doesn’t mean they aren’t there. As they said, it is not meant for critical aspects of gameplay.

Unreliable remote events are for constant data flow and low priority information. Think a more accurate character position or effects. This is because, although unreliable, an incredibly small amount of data will be lost depending on how efficient you use it and the clients internet connection.

A gun for example – if you fire a single bullet, if the single bullet doesn’t render it’s going to look awful and confuse the player so it’s best to use reliable remote events. If you’re firing 20 bullets per second, they’re not going to notice one trail missing, so it’s best to use unreliable events so you don’t clog up the reliable remote queue.

3 Likes

Just for clarification, there is no 9 byte overhead and additional 5 byte client-to-server overhead?

Should I use this when having a uncontrol event, which is client → server, which indicates that whatever object the player is holding, will be dropped. It has 0 arguements, but I just want to make sure!

Please refer to one of my earlier responses.

If it is important for the server to know that the client wants to drop said object, you shouldn’t use unreliable remote events at all.

I was not under the impression remote events were reliable to begin with, considering I had usecases where sending a fresh spawned part as an argument in a remote event lead to the argument being nil. lol