ByteNet | Advanced networking library w/ buffer serialization, strict Luau, absurd optimization, and rbxts support. | 0.4.3

On structure you need to use datatype like

message = ByteNet.string etc

Also you can just definePacket in a module script maybe in ReplicatedStorage

ReplicatedStorage/test.lua

local ByteNet = require(path.to.ByteNet)

return {
   RGB = ByteNet.definePacket({
      reliabilityType = 'reliable'
      structure = { 
         R = ByteNet.int8,
         G = ByteNet.int8,
         B = ByteNet.int8
      }
   }),
   …
}

on main.server.lua

local testPacket = require(path.to.Packet)

testPacket.RGB:sendToAll({ R = R, G = G, B = B})

on main.client.lua

local testPacket = require(path.to.Packet)

testPacket.RGB:listen(function(rgb)
   print(rgb.R, rgb.G, rgb.B)
end)

Or you can also use dataTypes array

1 Like

Your client code is not correct.

2 Likes

What’s the correct one then? Charr

1 Like

Yes, that’s planned as a struct data type.

2 Likes

Take a look one more time at your client code you posted, and then check the structure given.

1 Like

Oh yea, should be rgb.R, … char char

Structs and namespaces are now finished, just doing docs.

On wally under 0.4.0-dev2

1 Like

When i first looked at ByteNet i thought it was a cool little project with no real use case, now I realize that it quite literally pushes previous limitations in terms of networking.

For my game we have to replicate 100’s of arrays containing usually 100 vector3 waypoints from the server to the client frequently. Quickly saw that I was getting 50-60 kb receiving, remembered ByteNet, less than 5 minutes to implement, 5-6 kb receiving.

This module is literally a game changer.

6 Likes

This module looks very interesting, I’ll make sure to implement it in all of my future games since the results of other people look very good. I do wanna know what impact this would have on the exploiters, like if its still possible to spoof the args of the remote events?

It’ll make using RemoteSpy near impossible.

Exploiters will still be able to spoof data.

How does this handle instances?

it doesnt, you need to pass specific types. Roblox doesn’t actually send instances over the network but just a reference to it, but you can just create your own way of referencing parts using unique id’s.

2 Likes

It’s great, the only thing is missing is a way to pass instances thru the network but other than that I use it a lot

Hi
I’m having some issues with this module. My usecase is to send positions of NPCs from the server to clients as efficiently as possible.

I’m using the following packet structure (ByteNetPackets module):

local ByteNet = require(game:GetService("ReplicatedStorage"):WaitForChild("ByteNet"))

return {
	NPC_ControllerPos = ByteNet.definePacket({
		-- This structure field is very important!
		structure = {
			id = ByteNet.uint16,	-- values from 0 to 65536-1 (more than enough for NPCs)
			pos = ByteNet.vec3,
		},
		--reliabilityType = "unreliable",
	})
}

The ID field is required to identify which NPC the movement is for.

I’m sending the position every 0.1s for each NPC on the server:

-- Send updated position to all clients that we're replicating to (this is only called if we're actually moving)
-- Don't send orientation to save network replication bandwidth
for _, v in pairs(self._playersReplicatedTo) do
	ByteNetPackets.NPC_ControllerPos:sendTo({ id = self._id, pos = self._currentCFrame.Position }, v)
	--movementRemote:FireClient(v, tostring(self._id), self._currentCFrame.Position)
end

Unfortunately I’m noticing that the client network recv is pretty much the same (even slightly higher!) compared to the unreliable remote event I was using before. That was simply sending the id as a string, and position as a Vector3, as you can see commented out.

Also I can’t use ‘unreliable’ mode without packets being dropped (size limit of 900KB). ‘movementRemote’ is unreliable and that worked perfectly before, so I’m not sure why it doesn’t work anymore. Perhaps the module internally puts packets together in one bigger packet?

What can I do to optimize the send, and be able to use unreliable remotes?

One thing I tried, was sending the x, y and z components as float32s, but your module already does that behind the scenes so that didn’t improve much.

Thanks

P.S.
Minor QoL issue, but the ByteNetPackets.NPC_ControllerPos:sendTo({ id = self._id, pos = self._currentCFrame.Position }, v) line is being linted as an error in Roblox Studio. No error related to it in-game though.

How many of these are you sending every frame?

Stress test of 200 NPCs, each at 0.1s update rate.
So 2k of those packets per second.
Ideally I want it to work for any number of NPCs.

What does the error say?
If self._id isn’t a number that might be apart of why.

Key ‘sendTo’ not found in table ‘Packet<{| id: any, pos: any |}>’.

sendToAll and send don’t give the ‘error’.

Only shows it in studio’s code editor. No issue during test session.

I think it’s because sendTo isn’t added to types.luau for autocomplete yet.

1 Like

This is likely because of the 1 byte overhead for the packet IDs, so if you want to send 200 NPC position updates separately in a for loop, the library would end up writing 200 bytes of packet IDs in total. To optimize this, you could change your packet structure to utilize a map, with keys as NPC IDs and mapped values the Vector3 positions. This would almost entirely remove the ID overhead, replacing it with 2 bytes (?) overhead for the number of key-value pairs in the map and only 1 byte for the packet ID.

However, overall network receive wouldn’t go down by too much. Roblox, by default, serializes Vector3’s into three float32’s, and writing 3 float32’s into a buffer (12 bytes) only saves 1 byte compared to sending the vector raw (13 bytes).

To optimize your network receive, consider whether most of the NPC updates you’re making are necessary. This depends on the type and scale of the game you’re making, but generally, you should not send position updates for NPCs that aren’t moving, or send at lower rates for NPCs far away from the player.

Yes, this is how ByteNet works internally. To work around the unreliable remote event payload limit, you would have to modify the send process to check if adding data would increase the current buffer queue size beyond the 908 byte limit. I can imagine this could get very annoying, as the actual number of bytes sent through the network is not equal to the number of bytes written.

I can’t really think of a more simple solution, but you could write a separate method that serializes the data and sends the resulting buffer immediately without attempting to merge with other data. Then, distribute your NPC position updates among several maps so that the maps, when serialized, are each under the payload limit (likely have to trail and error this).

1 Like