Make Bindable Events/Functions Sane

BindableEvent/BindableFunction vs. RemoteEvent/RemoteFunction dichotomy makes programming a complicated multiplayer game with logic split between client and server hell. :evil:

I want to use the same RPC mechanism between scripts regardless if the underlying communication is client-client or server-client or client-server. :smiley:

Right now for every function in a server script that I want to be callable from anywhere, I have to add both a client and a server side binding object. This is tediousaf. :X

While we are at it, can we fix the naming convention. The only call I should need is Invoke. It is never ambiguous where I want the code to be Invoked from or to. This criticism only applies to RemoteEvent/Function. I have to look up every time whether RemoveFunction:InvokeServer means “invoke on the server” or “invoke from the server”. I hate thinking about this. It’s especially stupid because right now it only works client-server or server-client. Imagine a world where it is RemoteFunction:Invoke everywhere. :imagine:

Needing to filter where you invoke is gross and leads to bad design regardless. Filtered invokes are a special case and I can always build this mechanism myself.

TL;DR -

All you need to do is:

  1. Deprecate BindableFunction/Event
  2. Deprecate RemoteFunction/Event:InvokeServer/InvokeClient
  3. Add RemoteFunction:Invoke and Event:Fire and make them work across network boundaries

You have made programming a multiplayer game in ROBLOX 20% easier in 30 minutes of work. :swag:

5 Likes

This might cause problems for Play Solo

But, what if you do not want the events/functions to replicate?

He’s saying to fire for anyone who wants to listen, client or server side, so even in play solo, the scripts listening are properly client/server separated already, so it won’t cause issues

[quote] But, what if you do not want the events/functions to replicate? [/quote] Then you don’t hook into them?

Elaborate?

I think the proposed behavior would eliminate edge cases, including those related to Play Solo. How does the current system even work in Play Solo? Does RemoteFunction:InvokeServer from a local to a server script work when there is no server? Should it? As a new developer I now have to learn about this weird interaction that is not intuitive.

have you ever heard of serverstorage or serverscriptservice

If no script is subscribed to the Event, the engine should be smart enough to not replicate the invoke.

If scripts are subscribed to the Event and you want to filter which scripts act on it, you could either add an argument to the event or create 2 separate events (probably better).

1 Like

How do you deal with the player and the arguments?
In the case of clients invoking the server, a player argument is passed through, what about if a server also invokes it, do you pass nil as the player?
Likewise, what if you have both a client and server subscribed to an event, which one fires, maybe you want them to function differently, like with a chat event for example.

[quote] How do you deal with the player and the arguments?
In the case of clients invoking the server, a player argument is passed through, what about if a server also invokes it, do you pass nil as the player?
[/quote]

Sure. Most remoting systems I’ve used have a tuple argument for Events {Sender, EventsObject}

[quote]
Likewise, what if you have both a client and server subscribed to an event, which one fires, maybe you want them to function differently, like with a chat event for example. [/quote]

Sounds like a good design here would use at least two Events. One fires client-side when a Player types something into chat, which only the server subscribes to. The server can process this input (run it through a whitelist, parse commands, figure out if it is alltalk or team chat, etc). The server then fires Player.Chatted (in the case of alltalk), which all the clients subscribe to and print to the HUD.

In a bad design, the team chat goes to all of the attached clients. Now you are wasting bandwidth and hackers can intercept messages they should not be privy to. I don’t see how you can avoid this if you are just broadcasting from a client whenever anyone says anything.

1 Like

I agree wholeheartedly that Bindable/Remote Functions/Events are not as good as they could be. In the event that we did need to check whether it was client-client, server-client, client-server, or server-server, I guess we could just check:

server = invoker == nil
client = invoker ~= nil

so I guess it wouldn’t be too much of an issue, but I don’t think just packaging them all into one and having to put “assert(invoker, “cannot be called from server”)” at the top of every function/event you only want to be called from the client, and likewise in similar situations would be optimal. In fact, creating an object for each and every remote/bindable function/event is tedious – moreso than creating two objects for unrestricted access from both the clients and server imo. They’re messy, hard to keep track of, and you end up having to parent them all to a messy folder for remote events/functions in ReplicatedStorage located away from the relevant script in ServerScriptService so that the client can see it and fire it. There needs to be an alternative to mess, physical objects and imo something like this would work well:

(I assume the engineers at ROBLOX can come up with a better name than eventfunctionservice)

eventfunctionservice CreateRemoteEvent (eventName) returns new remote event
eventfunctionservice CreateRemoteFunction(functionName) returns new remote function
eventfunctionservice CreateEvent (eventName) returns new event (only callable from same client / server that initialized it)
eventfunctionservice CreateFunction(functionName) returns new function

eventfunctionservice FireServer(eventName) fires event created through eventfunctionservice – pretty much port FireServer/Client, InvokerServer/Client, Invoke, and Fire over to eventfunctionservice

The reason eventfunctionservice still uses the long FireServer/InvokerClient names instead of just Fire/Invoke is because you could potentially create events with the same name on the client and server and you’d need a way to differentiate between them since there isn’t a physical object anymore.

server script:[code]
local eventfunctionservice = game:GetService(“EventFunctionService”)

eventfunctionservice:CreateRemoteEvent(“RequestTeleport”).OnEvent:connect(function(invoker, placeId)
game:service(“TeleportService”):Teleport(placeId, invoker)
end)
[/code]

local script:[code]
local eventfunctionservice = game:GetService(“EventFunctionService”)

button.MouseButton1Click:connect(function()
eventfunctionservice:FireServer(“RequestTeleport”, 111369)
end)
[/code]

Though as with the name, I’m sure the engineers at ROBLOX can come up with a better API than this.

Elaborate?

I think the proposed behavior would eliminate edge cases, including those related to Play Solo. How does the current system even work in Play Solo? Does RemoteFunction:InvokeServer from a local to a server script work when there is no server? Should it? As a new developer I now have to learn about this weird interaction that is not intuitive.[/quote]

Agree 99%
Without :InvokeServer() and :InvokeClient(), the calling script’s source in Play Solo is ambiguous.
It should be possible to put something in the environment of a running function that states if it was created by a Script or a LocalScript. There would be a gray area with _G/shared, and ModuleScripts, because they can be required by either. A solution could be to cache moduleScripts by both independently and potentially run twice, which is not ideal. Idk

If no script is subscribed to the Event, the engine should be smart enough to not replicate the invoke.

If scripts are subscribed to the Event and you want to filter which scripts act on it, you could either add an argument to the event or create 2 separate events (probably better).[/quote]

How long does it take for the script to replicate that it’s subscribed?

This. :X :X :X :X :X :X

1 Like

[quote]
How long does it take for the script to replicate that it’s subscribed? [/quote]

Is see what you are getting at. There is a potential for race conditions - i.e. you subscribe to an events, but in the networking the event arrives before the subscription.

I believe this happens in the current scheme as well as my proposed scheme and is part and parcel to the asynchronous programming model involving multiple actors that pass messages to each other.

1 Like

Invokes get queued and executed whenever something connects.

I don’t think this is a good idea. The objects are similar in that they send data but they’re used for entirely different purposes and have zero overlap.