Fun fact, this isn’t actually possible to include!
The only way to ensure reliable communications is the recipient sending an acknowledgement back to the sender when they get the message. If the sender gets no such acknowledgement they will eventually try resending the message. The longer the sender waits without getting an acknowledgement the more certain they become that the message was dropped, but there’s no particularly good point in time to say “now I know it was lost” and fire the event.
What makes it useful for visual things such as particle emitters?
Secondly, what determines the bytes it carries, is it the arguments you send via the remote? i.e., if I’m sending a character model as an argument compared to a string as an argument?
Right now everyone are kind of just guessing what each datatype costs on a remote because they are not documented at all. Whether Roblox puts more effort on the documentation or gives us a tool to introspect remotes or whatnot. We just need something to help us catch bugs involving hitting the 900 bytes limit so
these things do not sneak into production.
For now you can see the exact payload size by intentionally going over the limit.
The current exact maximum payload size is 908 bytes and a hacky way to see the size of a payload is by intentionally doing over the limit causing a warning like this:
function printPayloadSize(...)
UnreliableRemoteEvent:FireAllClients(string.rep("i",905), ...)
end
printPayloadSize(1,2,3)
This also tells you how many bytes each datatype costs:
Numbers use up 9 bytes (8 bytes of data since they are sent as FP64s and an additional byte of overhead)
Strings use up 1 byte per character (obviously) plus 2 bytes of overhead if the length is less than 128, 3 bytes if they are longer up to the limit you would actually be able to send through an UnreliableRemoteEvent
Nil uses up 1 byte
Booleans use up 2 bytes
Vector3s use up 13 bytes (3 * 4 bytes since they the individual components are sent as FP32s and an additional byte of overhead)
CFrames use up 14 - 20 bytes depending on the rotation (usually 20 bytes if the rotation isn’t aligned with the 3 axis)
References to instances use up 6 bytes
Arrays like strings create an overhead based on the length of the array, 2 if it has less that 128 elements, 3 if more
If you want to make sure you are within the limit you should be doing the data serialization and compression yourself for example using the new buffer type which gets rid of much of the overhead and gives you greater control over the size of the payload.
Right these numbers do line up with what is commonly understood but this is still not documented and therefore I feel as though it is hard to completely trust them and I want to rely on Roblox to provide some actual information. I do appreciate you trying to help though. Going over the limit is what I intended on doing anyways to test but nonetheless what I am looking for is a more reasonable solution in the future.
You didn’t read at all of what I said: “Also, if a packet gets lost in TCP, basically everything is frozen until that lost packet is resent.” This is a huge issue that UDP solves. UDP is also faster and lighter weight than TCP because it does not require the handshake. Doing a handshake yourself with UDP is going to cost slightly more resources, yes, but you shouldn’t really be using this for that, this is mainly for rapid firing unimportant data.
Finally something useful from Roblox for once. This update, and the inclusion of the buffer class, will change Roblox games forever. The performance optimisation opportunities will be another level. Even though these new features have some issues, like a buffer being able to skyrocket memory usage to over 23gb, it’s still great what they have done this day.
Just checked luau source; the max buffer size a buffer can have is 1073741824 bytes, thats around 1 gb, so unless you have multiple buffers its impossible to skyrocket it to 23gb.
Most of these have been calculated in my packet size counter module, however for some values this becomes hard. This is especially so for buffer types, that are compressed past a certain size, with an unknown limit using a library that isn’t available in lua (zstd). Implementing some method (maybe in HttpService?) to measure the cost would be greatly beneficial, as well as futureproof for eventually new datatypes.
Everything that changed between frames. I’m currently only sending over the values that changed (compressed), but that’d almost certainly go over 900 bytes for sending over the initial game’s state & for things such as loading new maps
What method do regular remotes use for this then? I’m looking for regular remotes w/o the guaranteed ordering to avoid network pausing, but would still like for them to eventually be guaranteed to reach the client
Server decides A may have been lost, and sends again as A2
Client gets A2 again and Ack(A2)
If you fired an event saying that A was lost… that wouldn’t be accurate. A wasn’t lost, the client got it just fine the first time, it was the Ack(A) that was lost.
Normally there are some interesting techniques used to combine together the acks and messages in an optimal way that gives the best user experience. For example, if the Client above also had to send e message to the server it might send message B + Ack(A) in the same packet. There’s also decisions to make on whether to prioritize a resend over additional new sends.
Long story short, each individual message can’t be taken in isolation, the connection between client and server is a continuous stream and decisions on what to resend / when aren’t as simple as “resent message X after Y”.