ByteNet Max | Upgraded networking library w/ buffer serialisation, strict Luau and RemoteFunction support | v0.1.8

ByteNet Max

An upgraded buffer-based networking system

GitHub | Wally | Roblox Creator Store

ByteNet Max is an upgraded version of @ffrostfall’s ByteNet which relies on strict type-checking to serialise your data into buffers before deserialising it on the other end, feeding it back to your Luau code. But what makes ByteNet Max different from ByteNet? ByteNet Max supports queries (RemoteFunctions) which are client to server requests for data. This brings you an extremely optimised experience for RemoteFunctions, using minimal data to increase send and receive speeds. ByteNet Max lives up to ByteNet’s idea of making networking simple, easy and quick. The API is simple and minimalistic, helping you grasp the concepts of ByteNet Max pretty quickly!

Installation

Get ByteNet Max on the Roblox Creator Store, or on Wally (WARNING: v1.0.0 is the FIRST version of ByteNet Max, due to an error I made. The latest version is always the one shown on the title of this post)!

Performance

ByteNet Max lives up to the standards of ByteNet, performing incredibly well compared to other networking libraries such as BridgeNet2. The conversion to a buffer reduces memory usage significantly, helping optimise and speed up data transfer.

Documentation

ByteNet Max follows the same architecture as ByteNet, hence the documentation for RemoteEvents (packets) is the exact same and can be found here: Documentation.

However, it adds a new system named queries. This is the ByteNet equivalent of a RemoteFunction. To begin, you can create a ModuleScript to define a namespace under which your packets and queries will be held:

local ByteNetMax = require(path.to.ByteNetMax)

return ByteNetMax.defineNamespace("PlayerData", function()
    return {
       packets = {}, -- not necessary to include this table if there's no values in it.
       queries = {
			GetCoins = ByteNetMax.defineQuery({
				request = ByteNetMax.struct({
					message = ByteNetMax.string
				}),
				response = ByteNetMax.struct({
					coins = ByteNetMax.uint8
				})
			})
		},		
    }
end)

Then, in a local script, you can invoke the query like so:

local QueryModule = require(path.to.QueryModule)

local Coins = QueryModule.queries.GetCoins.invoke({
	message = "Can I please get the coins value?"
})

print(Coins)

In a server script, you can receive the query and return the appropriate information, like so:

local QueryModule = require(path.to.QueryModule)

QueryModule.queries.GetCoins.listen(function(data, player)	
    print(data.message) -- prints "Can I please get the coins value?"
	return {coins = player.leaderstats.Coins.Value}
end)

If you want to disconnect the listener in the future, you can assign the function to a variable:

local QueryModule = require(path.to.QueryModule)
local Listener 

Listener = QueryModule.queries.GetCoins.listen(function(data, player)	
    print(data.message) -- prints "Can I please get the coins value?"
	return {coins = player.leaderstats.Coins.Value}
end)

Listener() -- disconnects listener

The above function works with packets too!

It’s that simple!

Packets & Queries can co-exist under the same namespace, just make sure you define the packets and queries table in defineNamespace. If you don’t require packets, you can leave it out and just define the queries table, and vice versa.

IMPORTANT: You must require the ModuleScript you created on both the server and client! This is to initialise server side & client side dependencies for a secure network.

Some extra functions

ByteNet Max also adds extra functions for both packets & queries for better control over your code. You can now use .listenOnce() and .disconnectAll() to call a function once or disable all callbacks connected to a packet/query (equivalent to the :Disconnect() and :Once() functions from Roblox)

Using the example above, .listenOnce() is used the same way as .listen() :

local QueryModule = require(path.to.QueryModule)

QueryModule.queries.GetCoins.listenOnce(function(data, player)	 -- this callback only runs once, before disconnecting.
    print(data.message) -- prints "Can I please get the coins value?"
	return {coins = player.leaderstats.Coins.Value}
end)

The .disconnectAll() function can be used to completely erase every callback created through .listen():

local QueryModule = require(path.to.QueryModule)

QueryModule.queries.GetCoins.disconnectAll() -- disconnects all callbacks

Contact

Contact me on Twitter or Discord (username: elitriare), or just on this thread to report bugs or request features. I haven’t fully tested this across different types of experiences, so your feedback is extremely useful!

This project is open-source.

16 Likes

Version 0.1.1

Fixes

  • Added a separate queries and packets table to defineNamespace to segregate them, and prevent errors.
1 Like

why are you using return here
Extra chars

1 Like

It’s a module script, so it returns the namespace table so other scripts can use the packets & queries.

1 Like

I shall try it ! Remote Functions here I come !

2 Likes

but you didn’t define it to a variable to use it
why return ByteNetMax.defineNamespace("PlayerData", function()
instead of
local namespace = return ByteNetMax.defineNamespace("PlayerData", function()

1 Like

Have u confused “ByteNet” and “ByteNetMax”

2 Likes

good point lol, i fixed it! thanks for the alert

1 Like

there’s no such rule, the only requirement for a module script is to return a table. also you cannot define a variable to return smth

local namespace = return ...

is invalid

1 Like

added a github repository for those who want it. i hope i did it right :sweat_smile:

1 Like

Little unrelated but why does ByteNet use namespaces? They don’t seem to do anything

1 Like

hey does this have any performance benefit from bytenet, especially the remotefunction thing?

1 Like

i tried to implement the module to one of my stuff in a game and for some reason its not sending it. i made sure everything was coded properly; it sends 3 stuffs. one is alot of numbers like more than 100 to make a low res “picture” of my screen using http service. the others where x and y with the canvas size. im sending it from server to localscript.

heres my module:

local ByteNetMax = require(script.Parent.ByteNetMax)

return ByteNetMax.defineNamespace("rgb", function()
	return {
		queries = {
			rgbpixels = ByteNetMax.defineQuery({
				request = ByteNetMax.struct({
					x = ByteNetMax.unknown,
					y = ByteNetMax.unknown,
					rgbcolors = ByteNetMax.unknown
				}),
				response = ByteNetMax.struct({
					msg = ByteNetMax.string
				})
			})
		},		
	}
end)

heres the send bit in the server side:

packet.queries.rgbpixels.invoke({rgbcolors = rgbVal ,x = screenshotSize.x, y = screenshotSize.y },game.Players.zak_isla)

heres the bit of localscript:

event.queries.rgbpixels.listen(function(data,player)
	print(player)
	local ResX, ResY, updatedRGBValues = data.x, data.y, data.rgbcolors
	print(data.rgbcolors)
	print(data.x, data.y)
	updatePixels(data.rgbcolors)
	countfps(0.4)
	txt.Text = "res:" .. ResX .. "x" .. ResY .. 'p'
end)

correct me if i was wrong

1 Like

They are used to organise & initialise packets & queries. Without a namespace, the packets are not initialised and won’t work.

1 Like

It has the same performance efficiency as ByteNet for RemoteEvents. However, for RemoteFunctions, ByteNet Max queries are up to 1.52x memory efficient than traditional Roblox RemoteFunctions!

Here’s a test sending a string & number 200 times per frame using traditional Roblox RemoteFunctions:
Screenshot 2024-11-22 at 4.36.22 pm

The same thing but with ByteNet Max queries (uint8 & string):
Screenshot 2024-11-22 at 4.34.58 pm

2 Likes

ByteNet Max does not support server → client remote functions. Only client → server!

1 Like

oh ok, in your next update, are you gonna add that functionality from server to client?

1 Like

No, I don’t plan on adding that function since Roblox recommends not to use server → client remotefunctions.

1 Like

ok, i have scripts that use remotefunctions to send from server to client but ok ill find a another way.

2 Likes

hey, this works mostly great, one significant issue though, if you send multiple queries at the same time, they seem to get scrambled up i guess?


in the video shown above, i send a few different types of queries, with each following different formats that i can’t exactly interchange.

when i click on a unit, i end up firing 4 queries at the same time, and while the last query returns the correct result, the previous 3 return results from other queries than their own, despite what should happen.

i hope this gets fixed soon, but until then, i think ill just avoid queries altogether.