Red - A simple, fast, and powerful networking library

Red and BridgeNet2 have the same bandwidth usage, performance in terms of compute time are undocumented for both libraries (they are likely similar though). The big differences are in the intention and API. BridgeNet is what I would call “bleeding-edge”, it strives for the most optimal performance in all situations. Red strives to have good performance, but will not gain performance at the cost of DX. BridgeNet has an object-oriented API very similar to those found in other networking libraries (like knit’s rbxcomm). Red has a unique take on a networking API not found in any other libraries (afaik).

It’s difficult to choose which library you’ll use, but in general I’d say just look at the API and choose which one you like more. There aren’t any huge performance differences between the two.

What’s happening here is a bit complex, so I’ll walk through it in steps.

  1. The server starts with no players.
  2. FireAll is called, which loops through all players calling Fire on them. As there are no players, not a single Fire call occurs.
  3. The player joins, and attempts to listen to an event which has never been fired (as there were no players when FireAll was called) and cannot load the event identifier.

This isn’t intended behavior, but I’m also not going to add more code to explicitly support calling FireAll with no players as this case is extremely rare. If you can give me a usecase for what you’re doing I’ll see what I can do.

Interesting, I am using this method as ROBLOX standard firing method since I need to call FireAll() for a Server Notification when the round ends. I know there may be a better way of doing so but I just thought maybe Red would kind of work the same way as a Standard ROBLOX event. But when I also tried Identifying the Player(s) and then calling the Event I still wasn’t getting anything back from the server, not even a simple print method would work.

I tested this, and running this when player is added doesn’t have this issue. Running this on round end should work just fine with no issues. This problem only happens when calling FireAll with no players in the server.

It’s weird cause I have fired it even after I had loaded the player in and nothing would print I’ll try it again.

EDIT: Okay so I have messed around with it and got it to work now. Thank you for explaining it more to me and keep up the good work on the module it’s very handy :call_me_hand:

1 Like

Love the way this library is structured, and the documentation is fantastic. My only issue is it generates these warnings in the output:

They don’t seem to affect anything, its just inconvenient. It seems like the attributes on the remote event are not getting created until the corresponding event is fired.

1 Like

The warnings can be incredibly useful for troubleshooting, however I’ll make them opt-in in the next release. This isn’t an important enough issue to warrant an immediate release.

1 Like

Finally a networking library that is so underrated that also has it own Promise module with typecheck so I can use it easily without any problems :wink:

1 Like

Have been using it in my (developing in-progress) game, and if I tell ya, its just a no no from me. The best services I’ve gotten from a networking library. It works really fast and literally has stuff that I already want to make like Clock. It’s amazing. Good job on the module (so far)!

image … … … … … …69

1 Like

great module. Namespace feature is slightly strange to me though, just adds more lines and more work. Couldn’t you just refer to each event like a path instead if you wanted to keep a namespace convention?

Example:

local Red = require(Path.To.Red)

local Net = Red.Client()

Net:Fire("NamespaceName/MessageAll", "This is pretty red!")

Also why do we need to specify Red.client/server(). Shouldn’t it just return different methods/logic on require? Unless its for autocomplete and strict typing purposes, it just adds another layer of boilerplate like code.

it would also be nice to have some sort of bindable event logic too, for client → client and server → server communication.

1 Like

Namespaces are objects instead of paths because with paths you would be required to repeat yourself. Internally when networked, they are just paths.

The client and server functions exist for two reasons, its the only viable way to separate the different methods that the client and server have, and it makes it more clear what side of the client server boundary the script is on.

Bindable logic does exist! Red.Signal provides a signal class which has this behavior.

1 Like

gotcha, thank you. True that it can be done with a signal but I wanted the convience of not needing to store the signal somewhere and calling upon it, just like how we use networking modules to avoid cluttered remote instances. like maybe if your module had a wrapper class for signals where I can do Signal:On(‘SiganlPath1’, …) Signal:Fire(‘SignalPath1’, …) which matches the pattern of the remote logic in red.

there might be a bug with promises, unless im using them wrong. Here are the steps to repo:

I have a server script and a module script

this is the code for the server script

require(script.ModuleScript)
print('yield done')

this is the code for the module script

local Red = require(game.ReplicatedStorage.RS.Modules.Red)
local promise = Red.Promise.new(function(Resolve)
	task.wait(3)
	return Resolve(1)
end)
promise:Await()

return 1

in this example, print(‘yield done’) never runs.

unless I’m doing something wrong this is really bad because I’m trying to :Call() within a module script on the toplevel and yield the module return until its done, but yet there’s an infintite yield despite the server returning a value to the client

Can you wrap it as a pcall I tested and it will print

local Red = require(game.ReplicatedStorage.RS.Modules.Red)
local promise = Red.Promise.new(function(Resolve)
	task.wait(3)
	return Resolve(1)
end)
local success: boolean, result = pcall(function()
	return promise:Await()
end)
print(success, result)
return 1

I’ve looked at this and I’m not quite sure where the problem is, or how to fix it. If you find a solution please let me know.

I can’t find a solution either… its a very strange bug. Even though it reaches the bottom of a module script, it just doesn’t return and inf yields. It might be an engine bug with relation to coroutines/task library usage in your promise module.

In my specific use case. I’m invoking a function on the toplevel of a client module, or in Red, client:Call() and server:On and then using Await() on the promise that client:call returnsl. This is a blocker so I’m resorting to using RemoteFunctions and using Red for normal remote events.

1 Like

Hi @jackdotink. When will you allow us to remove Namespaces & Events from Red? Since it isn’t get garbage collected.

Namespaces and events should be statically defined, they should never be removed.

This library seems to be both interesting and promising, I’ll definitely be incorporating it into my project. I do have a question regarding the implementation of ‘RemoteFunction-like’ callbacks; is it possible to yield callbacks on the server (possible with RemoteFunctions) like in the example below? If yes, how come example B doesn’t work?

-- // server

local Red = ...
local Net = Red.Server("Test", {"Event"})

-- example A
Net:On("Event", function(player: Player, x: number, y: number)
    return x + y
end)

-- example B
Net:On("Event", function(player: Player, x: number, y: number)
    task.wait(2)
    return x + y
end)

-- // client

local Red = ...
local Net = Red.Client("Test")

local startTime: number = os.clock()

print("calling server and waiting...")
local result: number = Net:Call("Event", 1, 1):Await()
local timeTaken: number = os.clock()-startTime
print(string.format("server returned result [%f] in %f seconds", result, timeTaken))
-- output for example A
> calling server and waiting...
> server returned result [2.000000] in 0.160957 seconds

-- output for example B
> calling server and waiting...

There are currently some issues with function-like events, these will be resolved in the next update as I plan on rewriting that whole system. You should be able to yield in callback code, for some reason right now it’s not working.

1 Like