I see you removed WaitForBridge. What can I use instead? This breaks a ton of code. I wrote around that function existing.
You should theoretically be able to use CreateBridge.
Hello @ffrostfall,
I am really impressed with everything youāve done here - the github page, the documentation, etc. Itās all very well organized. However, something stood out to me that and is your ping measurements. I wondered: how can something that uses RemoteEvents to send over data be faster than the RemoteEvents themselves? Itās somewhat a paradox.
I had a theory, but to test it, I created an empty baseplate. I would measure ping by doing the following with both Robloxās remote event API and your own API:
- Client stores tick in a variable and calls :FireServer with no parameters
- Server receives the event and does :FireClient immediately with no parameters
- Once the client receives the event, it subtracts the current tick from the stored one to get the elapsed time.
This is a very simple but rigorous method to test the ping of a specific network event. I tested 100 requests after waiting 5 seconds on the client for any more lag due to replication/first joining to settle. The environment is exactly the same. After putting it to the test, there were the results:
Results
BridgeNet consistently gets ~60ms, while roblox gets ~40ms. Although Robloxās time is far more spread out, the overall difference between the two makes it negligent.
I do have an explanation for this and why it is different from your results:
I believe the way your system queues the results and sends them once per frame slowly allows the packet size to be smaller overall and allows roblox to more quickly send packets that are used to measure ping, however this is at the cost of the speed in which the server/client receives data. Although Robloxās stats show the ping to be low, that is not a measurement of how long it takes for the data going through the BridgeNet to reach the server, its a measurement that ROBLOX takes internally. The actual time it takes for the server to receive the event, particularly when done multiple times per frame, takes tremendous amounts of time, (>1000ms).
Personally, Iām almost certain that ROBLOX already handles serialization of instances and compression on the backend for anything sent over the network. In fact, Iām certain most multiplayer games do; compression algorithms have been one of the most researched and developed algorithms I can think of. I totally agree with your last statement though. ROBLOX should have an alternative API for handling data that is loss-tolerable (UDP).
TL;DR: My main complaint is that your original post describes it as if there are no cons. Overall, this system is great if the delay between when the packet is sent and received isnāt important; however, this isnāt always the case (for example, when a player hits an enemy and the client requests the enemy to be damaged. Speed is very important here!)
I have attached the Google Sheets file with the data along with the place file with the test scripts of both ROBLOX and BridgeNet.
Testing game (90.4 KB)
Spreadsheet download (35.1 KB)
I see. Could you update the documentation? Itās very out of date and I find it hard to keep up with the frequent breaking changes. Fortunately, I can find/replace all bridgenet calls to a wrapper module (which may be the done way anyway?)
Youāre fundamentally misinterpreting what BridgeNet does. āhow can something that uses RemoteEvents to send over data be faster than the RemoteEvents themselves?ā I never claim to be faster as in less ping, I claim to use less data, which is true.
āhowever this is at the cost of the speed in which the server/client receives data.ā Actually, this is a bug with 2.0.0. I donāt think youāll see this sort of behavior in earlier versions- and if you do, please report it. 2.0.0-rc3 is short for 2.0.0 release-candidate 3, which means that itās an unstable version that isnāt ready for production usage. I introduced variable replication rates (replicating some remotes at 20hz), and in that I suppose I must have code that accidentally waits one more frame per remote call. Iāll definitely be investigating and fixing this.
āPersonally, Iām almost certain that ROBLOX already handles serialization of instances and compression on the backend for anything sent over the network.ā No, they donāt unfortunately.
Blank remote call: ~9 bytes
string (len 0): 2 bytes
string (len 1): 4 bytes
string (len 2): 8 bytes
string (len 3): 9 bytes
string (len 4): 10 bytes
string (len 5): 11 bytes
string (len 6): 12 bytes
string (len 8): 14 bytes
string (len 16): 22 bytes
string (len 32): 36 bytes
boolean: 2 bytes
number: 9 bytes
table (empty): 2 bytes
table (array with 4 numbers): 38 bytes
EnumItem: 4 bytes
Vector3: 13 bytes
CFrame (axis aligned): 14 bytes
CFrame (random rotation): 20 bytes
Source listed here: In-Depth Explanation of Roblox's RemoteEvents, Instance Replication, and Physics Replication (w/ sources!)
Your post does actually remind me of another critical flaw of Roblox though- we donāt have the ability to tap into when networksteps happen, and do behavior on those steps. Feature request is here, if you want to support it! RunService.NetworkStepped Event
I actually really appreciate this post because it brought attention to a bug in 2.0.0, which is an unstable version. Thank you! Oh, and one more thing: Any test you do is going to be fundamentally flawed, because network receive is done at the beginning of the frame, however code that reacts to it will not take effect until later stages, so the ping there is mostly irrelevant.
Iāll be updating the documentation when 2.0.0 is fully ready- as shown by the above post, it is not ready at the moment.
iām scared of writing all the documentation though not gonna lie
Even incremental updates would be nice! I have no idea how to use new bridgenet and canāt figure out how to modify my code to support it. I might just use regular network events until then.
this is a must have, i am definetely going to use this for my current and future projects.
Iām confused;
Roblox Remotes
Sent: 426.82 KB/s
Received: 541.99 KB/s
BridgeNet
Sent: 496.77 KB/s
Received 496.07 KB/s
With bridgenet youāve received 0.70 KB/s less than you sent,
doesnāt that imply the amount of data sent wasnāt fully received? ie; data loss?
Itās separate numbers afaik, itās not passing the value through and then back again.
Also- thereās variance, you shouldnāt rely on exact numbers like that because Roblox has net code that gets logged in there too.
I really like this module but how can I invoke from server to client and why you havenāt add remote function APIs
Invoking the client is an anti-pattern. Itās an exploit vulnerability, pretty much.
However, the client can invoke the server through InvokeServerAsync
Hi,
When I run the test below it does not show the correct values and returns a table for Val1 and nil for Val2 and 3.
What am I doing wrong?
23:06:25.493 plr CanterCrow - Server - BNet-Client-To-Server-SS:7
23:06:25.493 Val1 table: 0x986ee00f34d665e4 - Server - BNet-Client-To-Server-SS:8
23:06:25.493 Val2 nil - Server - BNet-Client-To-Server-SS:9
23:06:25.493 Val3 nil - Server - BNet-Client-To-Server-SS:10
āLocal Script
local BridgeNet = require(game.ReplicatedStorage.Modules.BridgeNet)
BridgeNet.Start({})
local ClientRemoteLS = BridgeNet.CreateBridge(āRemoteā)
while true do
ClientRemoteLS:Fire(āVal1ā, āVal2ā, āVal3ā)
task.wait(1)
end
āRemote Script
local BridgeNet = require(game.ReplicatedStorage.Modules.BridgeNet)
BridgeNet.Start({})
local ClientRemoteSS = BridgeNet.CreateBridge(āRemoteā)
ClientRemoteSS:Connect(function(plr, Val1, Val2, Val3)
print("plr " ā¦ tostring(plr))
print("Val1 " ā¦ tostring(Val1))
print("Val2 " ā¦ tostring(Val2))
print("Val3 " ā¦ tostring(Val3))
end)
Apologies for this- this is due to an oversight I missed in testing. This will be fixed in 2.0.0-rc4, which should come out soon-ish (I would say within 2 days or so). Iāve been working pretty hard on it.
This is very interesting and cool, but is there a tutorial on this? Sorry, Iām not that great at scripting and donāt know a lot about this stuff.
Will there be some way of deciding the data type in Declare? Apparently you can get way better results doing stuff like encoding ints from 0 to 255 as bytes, as well as big savings from obscure data types like Vector3int16. It would be great if there was functionality for this.
Thereās functionality planned, however this is separate to RemoteEvents- I plan on creating an abstraction for string.pack
Iām working pretty hard on 2.0 which is already jam-packed with improvements, so this likely wonāt come soon.
2.0.0-rc4
Reminder- rc stands for release candidate. This version, and this module, are unstable. Itās available on the Roblox marketplace, and on Wally under ffrostflame/bridgenet@2.0.0-rc4
. I will be updating the roblox-ts port when 2.0.0 fully releases.
- Unpacked arguments on server receive (Thank you @MELON-Om4r)
- Fixed numerous queue-related bugs
- Invoke UUIDs are now packed for less network usage (34 bytes ā 18 bytes)
- The Identifiers function is a closure again
- Added outbound middleware
- Added middleware to the client
- Middleware now passes in the
plr
argument on the server - Overall middleware improvements
- Client-sided improvements w/ connections
- Added .GetQueue() for debugging purposes
- General improvements to client receive
- Temporarily removed warning signals until I can figure out a better way to add them, theyāre kind of a mess right now.
- Removed config symbols
- Removed logging features- it turns out I forgot to fully implement them, plus nobody used them.
- Removed BridgeNet.Start(), the module now runs when you require it for the first time.
- Removed :InvokeServer()
- Removed both dependencies
Iām very happy with the current state of BridgeNet. This update should help accessibility and usability across architectures, and it also significantly speeds up development. I will be releasing 2.0.0 likely soon.
I keep getting this error everytime I try to run my code:
14:26:38.266 Infinite yield possible on 'ReplicatedStorage:WaitForChild("AutoSerde")' - Studio
14:26:38.266 Stack Begin - Studio
14:26:38.266 Script 'ReplicatedStorage.Utils.BridgeNet.SerdesLayer', Line 38 - function _start - Studio - SerdesLayer:38
14:26:38.267 Script 'ReplicatedStorage.Utils.BridgeNet', Line 73 - Studio - BridgeNet:73
14:26:38.267 Stack End - Studio
The code:
Client:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local BridgeNet = require(ReplicatedStorage.Utils.BridgeNet)
local Remote = BridgeNet.CreateBridge("Remote")
Remote:Connect(function(stringA, stringB)
print(stringA .. stringB) -- Prints
-- Hello, world!
-- Hello, someone!
end)
Server:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local BridgeNet = require(ReplicatedStorage.Utils.BridgeNet)
local Remote = BridgeNet.CreateBridge("Remote")
while true do
Remote:FireAll("Hello, ", "world!") -- Fires to everyone
Remote:FireTo(game.Players.Someone, "Hello, ", "someone!") -- Fires to a specific player
task.wait(1)
end
It seems the roblox model that you can get on the marketplace is currently broken, I downloaded the latest relase from the github and that worked. One last problem though, it only starts working when you call bridgenet.Start(), I thought it does that automatically when you require the module for the first time? I made sure Iām using the 2.0.0 release btw.