Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- SignalNet
- Version: 1.0.0
- Author: dodo12w
- License: MIT
- A robust signal messaging system built on top of Sleitnick's Signal module.
- Provides named event and function-style signals with automatic queuing,
- order-independent fire/connect behavior, and deferred call support.
- --]]
- local module = {}
- --- Loaded Modules ---
- -- Sleitnick's Signal Module
- local Signal = require(script.Signal)
- --- Private Variables ---
- --[[
- Signals[SignalName] = SignalTable
- ]]
- -- Stores All Signal Tables
- local Signals = {}
- -- Stores names of function signals
- local FunctionSignals = {}
- -- Stores names of event signals
- local EventSignals = {}
- --- Private Functions ---
- -- Throws an error if using duplicate signal name
- local function assertSignalName(signalName)
- if EventSignals[signalName] then
- assert(false, "You have used : "..signalName.." as a signal event name already. Please use a different name.")
- end
- if FunctionSignals[signalName] then
- assert(false, "You have used : "..signalName.." as a signal function name already. Please use a different name.")
- end
- end
- --[[
- Event Signal: {
- ["Signal"] = Signal.new(), -- Signal that's fired to by :Fire() calls
- ["Que"] = {}, -- Holds params to fire calls before connection. Example: {{...}, {...}, {...}}
- }
- ]]
- --[[
- Returns event signal table
- ]]
- -- Creates event signal table and tracks name
- local function newEventSignal(signalName)
- assertSignalName(signalName)
- EventSignals[signalName] = true
- return {
- ["Signal"] = Signal.new(),
- ["Que"] = {},
- }
- end
- --[[
- Function Signal: {
- ["Signal"] = Signal.new(), -- Signal that's waited on for by :Wait() calls
- ["Signal_Receive"] = Signal.new(), -- Signal that's fired to by :Fire() calls
- ["Que"] = {}, -- Holds params to fire calls before connection. Example: {{...}, {...}, {...}}
- }
- ]]
- --[[
- Returns function signal table
- ]]
- -- Creates function signal table and tracks name
- local function newFunctionSignal(signalName)
- assertSignalName(signalName)
- FunctionSignals[signalName] = true
- return {
- ["Signal"] = Signal.new(), -- Signal that's waited on for by :Wait() calls
- ["Signal_Receive"] = Signal.new(), -- Signal that's fired to by :Fire() calls
- ["Que"] = {}, -- Holds params to fire calls before connection. Example: {{...}, {...}, {...}}
- }
- end
- -- Fires the fire function inside of the parameters metatable
- local function fireQueuedCalls(signalName, Que)
- for _, parameters in ipairs(Que) do
- local metatable = getmetatable(parameters)
- -- Metatable used in order to fire regardless of signal type (Event or Function and Regular or Deferred)
- metatable.FireFunction(signalName, parameters)
- end
- end
- --- Public Functions ---
- --[[
- Returns signal connection returned from Sleitnick's Signal:Connect()
- ]]
- -- Creates function signal table if it doesn't exist, creates new connection, then fires any queued calls
- module.ConnectFunction = function(signalName, signalFunction)
- if not Signals[signalName] then
- Signals[signalName] = newFunctionSignal(signalName)
- end
- local SignalReceive = Signals[signalName].Signal_Receive
- local wasConnected = #SignalReceive:GetConnections() ~= 0
- local connection = SignalReceive:Connect(function(...)
- Signals[signalName].Signal:Fire(signalFunction(table.unpack({...})))
- end)
- if not wasConnected then
- fireQueuedCalls(signalName, Signals[signalName].Que)
- end
- return connection
- end
- --[[
- Returns connected function results
- ]]
- -- Creates event signal table if it doesn't exist, if no connection then add params to que, if signal table and connected then :Fire()
- module.FireFunction = function(signalName, ...)
- -- Creates metatable that holds fire function to fire signal
- local metatable = {
- FireFunction = function(signalName, parameters)
- -- Does not fire deferred because :Wait() already called
- Signals[signalName].Signal_Receive:Fire(table.unpack(parameters))
- end
- }
- local parameters = setmetatable({...}, metatable)
- if not Signals[signalName] then
- Signals[signalName] = newFunctionSignal(signalName)
- table.insert(Signals[signalName].Que, parameters)
- elseif #Signals[signalName].Signal_Receive:GetConnections() == 0 then
- table.insert(Signals[signalName].Que, parameters)
- else
- -- By default this fires deferred in order to prevent a race condition with the :Wait()
- Signals[signalName].Signal_Receive:FireDeferred(table.unpack(parameters))
- end
- -- Returns unpacked results of the connected function received from :Wait()
- return table.unpack({Signals[signalName].Signal:Wait()})
- end
- --[[
- Returns signal connection returned from Sleitnick's Signal:Once()
- ]]
- -- Creates function signal table if it doesn't exist, creates new connection, then fires any queued calls
- -- By using :Once() from sleitnick's module, this function will only fire once, and then disconnects itself
- module.ConnectOnceFunction = function(signalName, signalFunction)
- if not Signals[signalName] then
- Signals[signalName] = newFunctionSignal(signalName)
- end
- local SignalReceive = Signals[signalName].Signal_Receive
- local wasConnected = #SignalReceive:GetConnections() ~= 0
- local connection = SignalReceive:Once(function(...)
- Signals[signalName].Signal:Fire(signalFunction(table.unpack({...})))
- end)
- if not wasConnected then
- fireQueuedCalls(signalName, Signals[signalName].Que)
- end
- return connection
- end
- --[[
- Returns signal connection returned from Sleitnick's Signal:Connect()
- ]]
- -- Creates event signal table if it doesn't exist, creates new connection, then fires any queued calls
- module.ConnectEvent = function(signalName, signalFunction)
- if not Signals[signalName] then
- Signals[signalName] = newEventSignal(signalName)
- end
- local Signal = Signals[signalName].Signal
- local wasConnected = #Signal:GetConnections() ~= 0
- local connection = Signal:Connect(signalFunction)
- if not wasConnected then
- fireQueuedCalls(signalName, Signals[signalName].Que)
- end
- return connection
- end
- -- Creates event signal table if it doesn't exist, if no connection then add params to que, if signal table and connected then :Fire()
- module.FireEvent = function(signalName, ...)
- -- Creates metatable that holds fire function to fire signal
- local metatable = {
- FireFunction = function(signalName, parameters)
- Signals[signalName].Signal:Fire(table.unpack(parameters))
- end
- }
- local parameters = setmetatable({...}, metatable)
- if not Signals[signalName] then
- Signals[signalName] = newEventSignal(signalName)
- table.insert(Signals[signalName].Que, parameters)
- elseif #Signals[signalName].Signal:GetConnections() == 0 then
- table.insert(Signals[signalName].Que, parameters)
- else
- Signals[signalName].Signal:Fire(table.unpack(parameters))
- end
- end
- -- Creates event signal table if it doesn't exist, if no connection then add params to que, if signal table and connected then :FireDeferred()
- module.FireDeferredEvent = function(signalName, ...)
- -- Creates metatable that holds fire function to fire signal
- local metatable = {
- FireFunction = function(signalName, parameters)
- Signals[signalName].Signal:FireDeferred(table.unpack(parameters))
- end
- }
- local parameters = setmetatable({...}, metatable)
- if not Signals[signalName] then
- Signals[signalName] = newEventSignal(signalName)
- table.insert(Signals[signalName].Que, parameters)
- elseif #Signals[signalName].Signal:GetConnections() == 0 then
- table.insert(Signals[signalName].Que, parameters)
- else
- Signals[signalName].Signal:FireDeferred(table.unpack(parameters))
- end
- end
- --[[
- Returns signal connection returned from Sleitnick's Signal:Once()
- ]]
- -- Creates event signal table if it doesn't exist, creates new connection, then fires any queued calls
- -- By using :Once() from sleitnick's module, this event will only fire once, and then disconnects itself
- module.ConnectOnceEvent = function(signalName, signalFunction)
- if not Signals[signalName] then
- Signals[signalName] = newEventSignal(signalName)
- end
- local Signal = Signals[signalName].Signal
- local wasConnected = #Signal:GetConnections() ~= 0
- local connection = Signal:Once(signalFunction)
- if not wasConnected then
- fireQueuedCalls(signalName, Signals[signalName].Que)
- end
- return connection
- end
- --[[
- Returns the arguments fired from the Signal
- ]]
- -- Wrapper for Sleitnick's Signal:Wait()
- module.Wait = function(signalName)
- if Signals[signalName] then
- return Signals[signalName].Signal:Wait()
- end
- end
- --[[
- Returns a signal
- ]]
- -- Wrapper for Sleitnick's Signal.Wrap()
- module.Wrap = function(event)
- return Signal.Wrap(event)
- end
- -- Disconnects all connections from the signal using Sleitnick's :DisconnectAll()
- module.DisconnectAll = function(signalName)
- if Signals[signalName] then
- if Signals[signalName].Signal_Receive then
- Signals[signalName].Signal_Receive:DisconnectAll()
- end
- Signals[signalName].Signal:DisconnectAll()
- end
- end
- -- Destroy a signal using Sleitnick's :Destroy() and cleans up the signal table
- module.Destroy = function(signalName)
- if Signals[signalName] then
- if Signals[signalName].Signal_Receive then
- Signals[signalName].Signal_Receive:Destroy()
- end
- Signals[signalName].Signal:Destroy()
- Signals[signalName] = nil
- EventSignals[signalName] = nil
- FunctionSignals[signalName] = nil
- end
- end
- -- Returns a table of all connections from a signal using Sleitnick's :GetConnections()
- module.GetConnections = function(signalName)
- local result = {}
- if Signals[signalName] then
- if Signals[signalName].Signal_Receive then
- result = Signals[signalName].Signal_Receive:GetConnections()
- else
- result = Signals[signalName].Signal:GetConnections()
- end
- end
- return result
- end
- return module
- -------------------------------------------- CHEST HANDLER THAT USES MY SIGNAL MODULE -----------------------------------------
- local module = {}
- --- Services ---
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
- local ProximityPromptService = game:GetService("ProximityPromptService")
- --- Modules ---
- local Shared = require(ReplicatedStorage.Shared)
- local LootPlan = Shared.LootPlan
- local SignalNet = Shared.SignalNet
- local Remotes = Shared.Remotes
- --- Events ---
- -- Event to send new item to player
- local NewItemEvent = Remotes.Server:Get("NewItem")
- --- Private Variables ---
- -- Initialize chest rarity loot plan (single type)
- local ChestPlan = LootPlan.new("Single")
- ChestPlan:AddLoot("Mythical", 0.01)
- ChestPlan:AddLoot("Legendary", 0.1)
- ChestPlan:AddLoot("Epic", 2.5)
- ChestPlan:AddLoot("Rare", 10)
- ChestPlan:AddLoot("Uncommon", 30)
- ChestPlan:AddLoot("Common", 57.39)
- -- Cost of opening a chest
- local chestCost = 50
- -- Table to track player debounce times
- local playerTimes = {}
- -- Debounce time in seconds
- local chestDebounce = 2
- -- Function to get a random accessory from that rarity folder
- local function getRandomAccessoryFromRarity(rarity)
- local folder = ReplicatedStorage:WaitForChild("ChestAccessories"):FindFirstChild(rarity)
- if not folder then return nil end
- local accessories = folder:GetChildren()
- if #accessories == 0 then return nil end
- return accessories[math.random(1, #accessories)].Name
- end
- -- Returns a random item and its rarity from a chest
- local function giveChestReward(player)
- local rarity = ChestPlan:GetRandomLoot()
- local item = getRandomAccessoryFromRarity(rarity)
- return item, rarity
- end
- -- Function to check if player needs to wait for debounce
- local function needToWaitDebounce(player)
- local currentTime = tick()
- if playerTimes[player] then
- if currentTime - playerTimes[player] < chestDebounce then
- return true
- end
- end
- playerTimes[player] = currentTime
- return false
- end
- -- Rewards player with chest if they have enough coins and are outside of the debounce
- local function iceChestPromptTriggered(player)
- local playerCoins = SignalNet.FireFunction("GetPlayerCoins", player)
- if playerCoins < chestCost or needToWaitDebounce(player) then return end
- local item, rarity = giveChestReward(player)
- SignalNet.FireEvent("IncrementPlayerCoins", player, -chestCost)
- SignalNet.FireEvent("AddItem", player, item)
- NewItemEvent:SendToPlayer(player, item, rarity)
- end
- -- Connect function when ice chest prompt triggered
- ProximityPromptService.PromptTriggered:Connect(function(prompt, player)
- if prompt:HasTag("IceChest") then
- iceChestPromptTriggered(player)
- end
- end)
- return module
Advertisement
Add Comment
Please, Sign In to add comment