NetSignal Event/Signal Creator Module (v3)(Bug Fix 4)

Have you ever made an object-oriented module, and thought: “Hmmm, an event or two would be really nice to make this module easier to use.”

Well, worry not! For I have completed my NetSignal Event Creator module which can do exactly that!

Linkie:

You may be wondering: “But Athar, other people have made this!” This is true.
But one problem i’ve noticed is that event signals do not replicate across the client-server boundary, which is a huge problem when you’re trying to make it so that events fire for both server and client!

Usually, the only way to fix this is to fire the event both on the server and client seperately.
But, with NetSignal, there is a new method called :Remote(…) which does cross the client-server boundary!

Another problem ive noticed with other modules is that they have unfamiliar syntax which is unusual to get used to.

Well, worry not! NetSignal has custom function autocomplete and also extremely familiar syntax, almost exactly the same as vanilla Roblox RBXScriptSignals.

  • CONSTRUCTOR

    Now, you may be wondering, “How do I create a new Event using this module?” Well, first of all you need ANOTHER module. This is because NetSignal is only to be used within other modules.

    Once you have the module you want to create an event for, the next step is just to call :Create(…) and set the returned Event as a variable in your module.

    The first argument of :Create(…) is simply just the name of your event. This is a string.

    (Note that this name cannot be randomly generated every time, as both the Server and Client need the correct name as to be synchronous.)

    The second argument is the type of function you want to set as the first argument of :Connect(func) for that event. This is to aid autocomplete with the parameters of your event.

    The third argument is the type of function you want to set as the :Fire(…) function for your event. This is to aid autocomplete with the parameters of your event when you call :Fire(…). It is simply the second argument with an additional ‘self’ parameter.

    The fourth argument is additionalInfo, where you specify any additional options youd like to configure within the event, such as useBuffers, sendTick and maxRequests.

    CODE SAMPLE:

    local netSignal = require(NetSignalPath) -- pseudocode
    
    myModule = {}
    
    myModule.Event = netSignal:Create(
      "MyEvent"
      function(
          Foo: number, Bar: string)
      end
      function(
          self: netSignal.RBXScriptSignal,
          Foo: number, Bar: string)
      end
      {
      	maxRequests = 5
      	useBuffers = true
      	sendTick = false
      }
    )
    
    return myModule
    

And we’re done! This is all you need to do to create a new Event using NetSignal.
Now let’s discuss all the methods that you can call on this Event!

  • METHODS

    • Event:Connect(func)

      Have you ever used :Connect(func) on a regular Roblox event, such as BasePart.Touched:Connect(function(HitPart))? Well, this is literally the same thing. Any functions you connect to this event will activate when Event:Fire(…) or Event:Remote(…) is called.

      CODE SAMPLE (using the previous myModule module):

      local myModule = require(MyModulePath) --pseudocode
      
      myModule.Event:Connect(function(Foo, Bar) -- number, string
        print(Foo, Bar)
      end
      
    • Event:Once(func)

      Essentially the same thing as Event:Connect(func), but after the Event is fired once this connection is instantly disconnected.

    • Event:Wait(timeOut: number?)

      This method yields the current thread until :Fire(…) or :Remote(…) is called, then it resumes the thread and returns all of the passed arguments as a tuple. Similarly to RBXScriptSignal:Wait().

      If the timeOut parameter is a number then it will wait that amount of seconds, and if a :Fire(…) or :Remote(…) call is not done within that time frame it will resume the thread anyway and return nil.

    • Event:Fire(…)

      This method calls all connected funcs of the event within the same client/server, passing the arguments as a tuple.

      CODE SAMPLE (using the previous myModule module.);

      local myModule = require(MyModulePath) --pseudocode
      
      myModule:Fire(10, "Bar")
      
    • Event:Remote(…)

      Now the moment you’ve all been waiting for, the Remote method! This essentially does the same thing as :Fire(…), but instead of firing all connected funcs on the same client/server, it instead fires on the opposite side of the client-server boundary.

      For example, if you called :Remote(Player, args) on the server, it would fire all connected funcs of that specific event on the specified Client.

      If you instead called :Remote(args) on the server, then it would fire all connected funcs of that specific event on all clients,

      The same thing on the client. For example, calling :Remote(args) on the client would fire all connected funcs of that specific event on the server.

      CODE SAMPLE (using the previous myModule module):

      -- Server
      local myModule = require(MyModulePath) --pseudocode
      
      while task.wait(0.1) do
        myModule.Event:Remote(10, "Bar")
      end
      
      -- Client
      local myModule = require(MyModulePath) --pseudocode
      
      myModule.Event:Connect(function(Foo, Bar)
        print(Foo, Bar) -- 10, Bar
      end
      

      Important to mention that if you call :Remote(…) on the client, it has a rate limit. You can turn this off by setting the maxRequests argument in the additionalInfo argument of :Create(…) to -1, but you can set it to any positive integer.

You also unfortunately cannot send Instances through :Remote(…) yet, but I will be adding a serializer for that soon.

By now you may be wondering how performant the :Remote(…) method is, since it uses buffers it’s actually pretty performant!

I performed a test where every 100 milliseconds the server calls :Remote(math.random(0, 123182), math.random(0, 1293192)), and the Client performs a square root operation on the first and second variable, then prints it, everytime a signal is fired from the server.

The result was an average of around 30 fps, which is pretty good (I think)!

Anyways, that’s basically it! Enjoy! (please report any bugs here, ty!)

  • Did you find this useful?
  • Yes
  • No

0 voters

3 Likes

so… remote functions in a module?
why not just use remote functions?


Honestly u could, but imo this module is faster and easier to use. Plus, its prob more secure too

what would make it more secure?

Go see for yourself, I’m not gonna sit here and argue :V

I’m not trying to argue with you, I’m just trying to see why I should use this module

It has typechecking for parameters, specifically for :Fire(…), :Connect(func) and :Once(func). Im also planning on adding typechecking for :Remote(…), alongside that it also uses buffers to send data, similarly to Bytenet (I dont use their namespace kinda stuff though), the main downside is that you cant send instances with :Remote(…) but im planning on adding that functionality soon.

The reason why I cited security is that it has a rate limiting system for signals sent from the client (can be disabled if needed), and also the reason why :Fire(…) and :Remote(…) are seperated is so that you can send some information to only the same side, and some information across, adding more security if needed.

This essentially makes it a remote event + bindable event, but without creating any new instances.

If you dont understand, please understand because English is not my first langauge :slight_smile:

Okay, so i tested the module, and without it i got 100mb of memory average. While testing with ONE event and no other scripts my memory shot up to 200mb, just use a RemoteFunction, having the instance and saving 100mb of memory is very worth it.

Thank you for the feedback, will be recreating this and seeing if I can fix it. This is one of my first community resources made so please dont judge :slightly_smiling_face:

1 Like

Not judging at all, just here to provide feedback.

2 Likes

I figured out the problem, I was sending the tick every time you call :Remote(…) which is a huge number, which is why you were noticing such big memory. I have updated the module to perform more compression and this time :Create(…) has an additionalInfo argument you can specify to use buffers, the maxRequests of that event and determine wether or not you want to sendTick.

And, I have 1 more security feature id like to implement. I have heard of a getgc() exploit which allows exploiters to see object youve created in the game. I plan to attempt to find a solution to this with the module in the future :slightly_smiling_face:

NEW UPDATE: V3!

With this new update, I have added:

Instance Serialization
Script Object Serialization (Except RBXScriptConnections and RBXScriptSignals)

And even the ability to send functions over :Remote(…)! (only server to client and not vice versa, for security.)

Linkie:

Code samples:

--module
local Events = {}

local Signal = require(game.ReplicatedStorage.NetSignal)

Events.MyEvent = Signal:Create(
	"MyEvent",
	function(Func)end,
	function(self: Signal.RBXScriptSignal, Func)end,
	{
		maxRequests = 5,
		maxSize = 100,
		
		remoteEnabled = true,
		useBuffers = true,
		serializeEnabled = true
	}
)

return Events
--server
local Events = require(game.ReplicatedStorage.Events)

while task.wait(1) do
	Events.MyEvent:Remote(
		function(Player: Player, Foo: number) -- The functiont to be sent, has Player as first argument, always
			if type(Foo) ~= "number" then return end

			print(Foo) -- 10
		end
	)
end
--client
local Events = require(game.ReplicatedStorage.Events)

Events.MyEvent:Connect(function(call)
	call(10)
end)

The only downside right now is that any functions sent over :Remote(…) have no autocomplete, and also no amount of serialization (basically a normal RemoteFunction.), will be trying to add this in the future (if possible).

POTENTIAL ISSUES:

Since instance serialization currently relies on names, it wont work correctly if you have duplicates. Keep this in mind when sending instances over :Remote(…)

Since the function sending functionality of :Remote(…) has a time window for the function to be called or else it gets garbage collected, you can configure this by going to NetSignal → GlobalConfigs → WrapperDefaultTimer and increasing it.

You can also just set it to -1 which disables the time window completely

I have updated the :Remote(…) handling part of NetSignal to be strictly typed, thereby giving more security when in use.