Hi there,
I am currently working on a custom networking system for my game and I need help implementing such a feature.
- What do you want to achieve?
I want to create a networking system that eliminates the need for remote events/functions stored in replicated storage. Scripts on both the client and server should be able to both listen to events and also fire events with given arguments and a unique event name. This system should be reliable, such that when a script on the client or server fires an event, the respective listener for it will always receive this signal and process it – which is essential as the server may load before the client or vice versa. So essentially, this proposed system will behave exactly like the common practice of storing remotes in replicated storage, and connecting to and firing these in any scripts, but instead eliminating the need for storing all these remotes in replicated storage objects though using bindable events/functions in this custom networking module.
Let me show you a code example of the whole system: let’s say the server wants to send the client their inventory data and the client will process this and display the players inventory on the screen via a GUI.
-- Server script
local CustomNetworking = require(...)
-- in a game player added event
-- first argument is the event identifier, next the relevant player and the rest is just the arguments for handling the event
CustomNetworking:FireClient('DisplayInventory', player, inventoryData)
-- Client script
local CustomNetworking = require(...)
-- in some sort of constructor
CustomNetworking:Connect('DisplayInventory', function(inventoryData)
-- display the data on gui
end)
With this networking module, the event called from the server should always be received by the client, even if the client has not yet loaded in – such logic I am not sure how to implement. And importantly, I do not need to create a remote event for this as the module will handle these events via bindable events.
- What is the issue?
-
The issue, which I just briefly mentioned, is that i’m not sure how to ensure that the events fired or invoked by the client and server will always be processed by the respective script. This is an issue as i know that either the server or client can load in before each other is ready, and so scripts may execute in the wrong order. I need to make sure that if a script uses the module to connect to an event specifier, then any events triggered by other scripts will be processed even if that event specific connection was created after the event was fired.
-
The other main issue is handling remote functions that cannot be processed straight away. This issue is explained in the solution where I tried implementing event buffers which is in the following section, as this is where the issue arises.
- What solutions have you tried so far?
-
I know that I can just have a script which contains all the event connections and the scripts that they should use, and then the networking module can just call these connections directly when an event is fired, but I would rather that these connections are not hardcoded in a single file, and instead any script can connect to any event.
-
I have tried using event buffers which hold events and their arguments if the event specifier was not found in the existing ones (a script has not connected to the event yet), and then when the matching event connection is created, it also immediately executes all the ones stored in the buffer for it – this same logic would be implemented on the server side too. However, a problem I found was with remote functions, where if a remote function was added to the buffer, there is no way to return the result back to the original place it was called once the connection is created and the event can be processed. For instance:
-- Client script
local CustomNetworking = require(...)
-- Assume that 'TestingEvent' has not been connected on the server side
local result = CustomNetworking:InvokeServer('TestingEvent')
print(result) -- nil as the module just put the event in the buffer and nothing was returned
-- Custom networking module
function CustomeNetworking:InvokeServer(action, ...)
if self._ServerInitialized then return self._RemoteFunction:InvokeServer(action, ...)
else table.insert(self._ServerOutboundFunctionQueue, {action, ...}) end
end
- I have also tried implementing a sort of RTS system where a handshake should occur between the client and the server so that once all event connections are made, the client and the server can start firing events and also one’s stored in buffers from before the handshake was made. But this proved difficult as I was not sure what to do in the case that the server or client never gets this signal, meaning the handshake can never occur.
Summary:
Sorry if this post is a bit long-winded or confusing, I found it really hard to explain exactly what the problem was and what I wanted help with. Please let me know if any of the things I have mentioned are not needed or can be changed. Also, If you can help me with this system then please do comment.
Just to let you know I am not asking you to create this whole system for me from scratch, just some guidance and code snippets would be greatly appreciated