Ideas on how to make a Server Event Listener

table.pack creates a table from all the given parameters, and table.unpack will “unpack” a table.

So, for example:

local function testFunc(...: any?): nil
    --we don't know what's contained in ...
    --let's gather it into a table
    local extraData = {...}

    --[[now we need to run our callback
    we use table.unpack so each variable in our table gets distributed to a variable in the parameters
    so if it takes 3 parameters and our table has 3 elements each of the parameters receives one of the elements

    but we also need to store the results. We don't know how many results we are going to get,
    so we can store them in a table using table.pack and assign it to results
    ]]

    local results = table.pack(
        self.callback(
            table.unpack(extraData)
        )
    )
end

These functions are useful for within a bindable as it allows for one generic method. We don’t know how many parameters we are going to receive, or how many we are going to get. We could just do return callback(table.unpack(extraData)), but that’s harder to read and understand. Therefore, we send all the parameters needed for the callback to to the callback, and return all the parameters the callback returns.

We only use one callback for this, just like a BindableFunction. It wouldn’t work very well otherwise.

What do you mean by this part? Do you mean I don’t put the brackets after the function name? If I did that, it would run the function and pass the return result of the function, instead of the function itself.

1 Like

I think you need to include all the fields of the custom event type in strict mode. I didn’t because I didn’t make it for strict mode.

type CustomEvent = typeof(setmetatable({}:: {Callback: (any?) -> (any?),Invoke: (Event,any?) -> (any?),Connect: (Event,any?) -> (any?),Invoked: boolean},{}:: Event))


local CustomEvent = {}
CustomEvent.__index = CustomEvent

-- All Events will be stored here.
CustomEvent.Events = {}




-- Class Constructor

function CustomEvent.new(name: string): Event
	local self = {}
	setmetatable(self, CustomEvent)

	self.Callback = nil -->> Any Functions/Arguments to be executed/passed!
	self.Invoke = self:Invoke() -- These doesn't work. lol
	self.Connect = self:Connect() 
	self.Invoked = false -->> Tells us if this Event has fired.

	CustomEvent.Events[name] = self 

	return self 
end




--waiting for a signal to exist
function CustomEvent:WaitForInvoke(EventName: string): Event
	local Signal: Event
	local attempt = 0 --Times waited for a Signal.

	repeat
		Signal = self.Events[EventName] -->> Signal is equal to the Event given.
		attempt += 1
		task.wait(1)

		if attempt == 5 then -->> If we have waited for a Signal for more than 5 seconds then warn.
			warn("Yielded for Event: "..EventName.."5+".."seconds")
		end
	until
	Signal or attempt == 60

	return Signal
end



--Replicating RBXScriptSignal's :Connect Function
function CustomEvent:Connect(callback: (any?) -> (any?)): any? --The given Callback/Function should not return a value, stated by the -> (nil)
	-- Add an Asynchronous version of the given Callback to be ran.
	self.Callback = callback
	return self.Callback
end



--Fires Event
function CustomEvent:Invoke(...: any?): any?
	local Arguments = table.unpack({...})
	
	local results = self.Callback(Arguments) -->> Calls Callback Function, and unpacks all Arguments and sends to it.
	return results or table.unpack(results) --Returns any results returned by the given Callback.
end


export type Event = {
	["Connect"]: (self: Event, any?) -> (any?), -->> Connect Function, will pass in the Event/self and any other arguments. and will not return a value.
	["Invoke"]: (self: Event, any?) -> (any?), -->> Fire Function, will pass in the Event/self and any other arguments. and will not return a value.
	["Callback"]: (any?) -> (any?), -->> Callbacks, so basically Arguments? Functions?
	["Invoked"]: boolean --When Signal/Event is Fired this will go true. Making any Receiving Scripts with :WaitForEvent execute their code.
}

return CustomEvent

What i made for my Bindable Functions

What do you mean by function itself?
You mean this?

function Something()
       return Something() -- this can be anything, just any result in general
end

Didn’t know Bindable Functions only used one callback

This part is wrong. Use table.pack to gather ..., use table.pack to store the results of the callback. Use table.unpack on ... only when invoking the callback, and return table.unpack(results). Otherwise, you will only get one of your variables.

sorry bro but i am not reading all that, why are you doing type annotation like that anyway? I thought it’d be easier to type annotate using a table of each element, like you did at the bottom.



This code will cause a script timeout. You said

I think I misunderstood.

We pass functions as parameters/arguments to the :Connect function. These functions are then run with parameters/arguments passed upon :Fire(). We don’t include the brackets when calling them, otherwise that would run them and pass the return value. The function name holds a reference to the function itself, which is also why you can pass an anonymous function to :Connect and it will still run.

I just put that there to see if the script would recognize something, but it didn’t do anything and i just forgot to remove it before sending the script.

meant this:
image

But yeah it’s a bad idea

yeah, assigning a member of self to the return value of it’s function is not a great idea and would certainly cause a logic error; would be undetectable without monitoring behaviour of the object, though.

Made this new one:

function CustomEvent:Invoke(...: any?): any?
	local Arguments = table.pack({...})
	
	local results = self.Callback(table.unpack(Arguments)) -->> Calls Callback Function, and unpacks all Arguments and sends to it.
	return results or table.unpack(results) --Returns any results returned by the given Callback.
end

Another thing is, how could i improve this base system u gave me In the future? with additions that would be good for it, i could research some stuff and add them but i feel like asking u would be better right now.

hm, what do u think would be good then?

The new code still isn’t quite right. Here’s fixed code:

function CustomEvent:Invoke(...: any?): any?
    local extraArgs = {...}

    local results = table.pack(self.Callback(table.unpack(extraArgs))
    return table.unpack(results)
end

The best way to improve it would be to add more methods of RBXScriptSignal and then optimise it. I made that example in a few minutes, but I then spent the time making my own module for it and optimising it (won’t send here sorry, it’s quite important to my game) and it works really well now. Type annotation is very important especially for the function preview when calling methods from this class, it serves as a good reminder of what is needed. I type annotated pretty much everything in mine.

Note that table.pack adds a field with the key n. This is the median of number values within the table.

Interesting, but why table.pack the self.Callback plus the rest? of the arguments?

From what i’ve noticed on yours u don’t add results or table.unpack(results).

Because on mine the output would either be a table or just 1 argument.

table.packing im assuming is just putting every returned value into a table to then be returned with table.unpack?
Theres no variables separating anything so it’s quite hard to digest.

And to add more methods i would just see what methods RBXScriptSignal has and put it in the Module?
And how would i optimise something like this, would be nice if i had something to have reference of about the optimisation part.

What i did for mine:

function CustomEvent:Invoke(...: any?): any? -->> ... is all arguments passed into :Invoke.
	local Arguments = {...}
	local ReturnedValues: any? = self.Callback(table.unpack(Arguments))

	local results: any = table.pack(ReturnedValues)
	return table.unpack(results)
end

Would have to rename some variables but this feels just better to look at lol

From the documentation the first 4 methods i will try to implement are these, i have to see if theres any other methods:

When you call a function, the return results are stored in assigned variables. Unstored values are discarded. For example:

local function getNumbers()
    return math.random(1, 10), math.random(1, 10)
end

local rand = getNumbers() --> this would store one of the numbers. The other one is not stored and is discarded.
local rand, rand2 = getNumbers() --> both generated numbers are stored
local rand, rand2, rand3 = getNumbers() --> rand3 is nil because there was not a third number returned

When you store the return results, you are only storing the first one. However, if we pass the return results straight to table.pack(), none of them will get discarded and it can all be stored as a table with only one variable holding a reference to it. We can then re-distribute the return results to the variables that requested them by using return table.unpack(results). This will return all the results, the same way all of the results were passed to table.pack(). This allows us to account for any amount of extra parameters through one function.

Oh i think i get it now:

   --Invoke
   local Result = TestEvent:Invoke(1,2,3)
   print(Result) -->> Would print 3
   --Receive

   TestEvent:Connect(function(1,2) -->> no mention of 3
             return 1 + 2
    end)

This wouldn’t discard any variables but since i didn’t mention them in this function, tthey will be “discarded” kind of.
even though they are still passed but just not mentioned.

1 Like

Bro how could i reference my Functions so my script doesn’t warn me in the .New function?

Do you mean you want to assign the callback in the .new function?

Just add an extra and pass the callback to that, and assign it there.

kind of but i also wanna mention the connect function and the other ones so the scrip doesn’t warn me

You could just initialise each value with nil or some other placeholder if it still happens.