This is my first community resources post so please give me any feedback you can!
Sorry for the code not being in a github thing I don’t know how to do that unfortunately
playerDebounce is module that handles debounces efficently for you games.
You can use this module to control the same debounces on different scripts and also set different channels for debounces (for example a “swordDebounce” channel for sword debounces!).
Features
- Easy API
- Reverse lookup system for efficent player removal.
- Automatic debounce cleanup with and option to disable it.
- Robust error handling.
- Flexible design for different types of games.
- Able to use different channels for debounces.
Formats
This shows how things are stored in the dictionaries.
-
Channel format.
[UserId] = debounceValue -
Channels format.
[ChannelName] = Channel format -
Reverse lookup format
[UserId] = {[ChannelName], [ChannelName]} ← Channels that player is removed from automatically get removed from the lookup.
API:
-
.init(autoRemove: boolean) can be called anytime when you want to change the AutoRemoveDebounce value.
-
createChannel(channelName: string, values: {}? | nil) should be called when you want to create a channel for debounces. You can also provide default values for the channel with the channel format listed above
-
getDebounceFromChannel(channelName: string, userId: string) should be called when you want to access a debounce from a channel. Provide the channel name and userId to the function.
-
setDebounce(channelName: string, userId: string, debounce: number) should be called when you want to add a debounce to a user on a channel. Provide the channel name, userId and debounce to succesfully set the debounce. If you have AutoRemoveDebounced set to true then it will automatically remove the debounce from the channel once the debounce time has passed.
-
removeDebounce(channelName: string, userId: string) should be called when you want to remove a debounce from a user on a channel. Provide the channel name and userId and the function will remove the debounce from the player on the channel.
-
channelExists(channelName: string) can be called when you want to check if a channel already exists. Returns a boolean.
-
printCache() prints everything : P
Constants
-
InitMessage: string gets printed everytime .init is called.
-
AutoRemoveDebounced: boolean Determines if debounces are automatically removed from the channel once the debounce time has passed. (Not really a constant :P)
Code:
-- You do NOT have to give any credit but do not claim this as your OWN.
--[[
Handles playerDebounces on different channels.
]]
--// ------------ CONSTANTS ------------ \\--
local InitMessage = "Hello from playerDebounce!"
local AutoRemoveDebounced = true --// Not really a constant :P
--// ------------ SERVICES ------------ \\--
local Players = game:GetService("Players")
--// ------------ VARIABLES ------------ \\--
local channels: {[string]: {[string]: number}} = {}
local reverselookup: {[string]: {[string]: boolean}} = {}
--// ------------ FUNCTIONS ------------ \\--
local playerAdded = function(player: Player)
--//
end
local playerRemoved = function(player: Player)
--// If removed player does exists in a channel then we can terminate them from the channel.
local userID = tostring(player.UserId)
--// Check if reverse lookup has the player registered to it.
if reverselookup[userID] then
--// Player has been registered to the reverselookup.
for _channelName, isJoined: boolean in reverselookup[userID] do
if channels[_channelName] and isJoined and channels[_channelName][userID] then
--// Remove player from channel.
channels[_channelName][userID] = nil
end
end
--// Remove player from reverselookup.
reverselookup[userID] = nil
else
--// Player has not been registered to the reverselookup. We can use the old fashioned method of looping all channels.
for _channelName, container in channels do
for user_ID, non_ in container do
if userID == user_ID then
--// Player exists in this channel so remove him.
container[userID] = nil
end
end
end
end
end
--// ------------ MODULE ------------ \\--
local handler = {
init = function(autoRemoveDebounced: boolean)
--// Script has been initialized.
AutoRemoveDebounced = autoRemoveDebounced
print(InitMessage)
end,
createChannel = function(channelName: string, values: {}? | nil)
--// Creates and adds a debounce channel to channels with already provided values if they exist.
if not channels[channelName] then
--// This channel does not already exist. We can move on and create the channel.
if values then
--// Default values were provided. We can add them to the created channel.
channels[channelName] = {}
for _i, _preValue in values do
channels[channelName][_i] = _preValue
if not reverselookup[_i] then
reverselookup[_i] = {}
end
reverselookup[_i][channelName] = true
end
else
--// No default values provided. We can create an empty channel.
channels[channelName] = {}
end
else
--// This channel already exists.
warn("This channel by the name: "..channelName.." already exists. Please access the channel instead of creating another one.")
end
end,
getDebounceFromChannel = function(channelName: string, userId: string)
--// Returns the debounce value for player on the given channel.
if channels[channelName] then
--// This channel does exist.
local value = nil
value = channels[channelName][userId] or nil
--// Return value.
return value
else
--// Channel doesn't exist.
warn("This channel by the name: "..channelName.." does not exist.")
return nil
end
end,
setDebounce = function(channelName: string, userId: string, debounce: number)
--// Sets debounce on a channel with provided name.
if channels[channelName] then
--// Channel does exist so we can add the debounce to the channel.
channels[channelName][userId] = debounce
--// If autoremove then delay a function that removes the debounce.
task.delay(debounce, function()
if channels[channelName] and channels[channelName][userId] then
--// Channel does exist. We can remove the debounce from the user since the time has passed.
channels[channelName][userId] = nil
--// Remove channel from players reverselookup
if reverselookup[userId] and reverselookup[userId][channelName] then
reverselookup[userId][channelName] = nil
end
else
--// Channel doesn't exist.
warn("This channel by the name: "..channelName.." does not exist. Error in AutoRemoving debounce.")
end
end)
else
--// Channel doesn't exist.
warn("This channel by the name: "..channelName.." does not exist.")
end
end,
removeDebounce = function(channelName: string, userId: string)
--// Removes debounce from a player on a channel.
if channels[channelName] and channels[channelName]{userId} then
--// Channel does exist so we can remove the debounce from the channel.
channels[channelName][userId] = nil
--// Remove channel from players reverselookup
if reverselookup[userId] and reverselookup[userId][channelName] then
reverselookup[userId][channelName] = nil
end
else
--// Channel doesn't exist.
warn("This channel by the name: "..channelName.." does not exist. Or player does not have a debounce on the channel.")
end
end,
channelExists = function(channelName: string)
--// Returns true if channel by the name provided exists.
if channels[channelName] then
return true
end
return false
end,
printCache = function()
--// Prints everything.
print(InitMessage, AutoRemoveDebounced, reverselookup, channels)
end,
}
--// ------------ CONNECTIONS ------------ \\--
Players.PlayerRemoving:Connect(playerRemoved)
--// ------------ RETURN ------------ \\--
return handler
Basic use case:
local debounce = require(script.Parent)
local swordChannel = "SWORD"
local swordDebounce = 0.25
debounce.init(true)
if not debounce.channelExists(swordChannel) then
debounce.createChannel(swordChannel, nil)
end
local function SwordAttacked(player: Player)
local userId = tostring(player.UserId)
--// BLA BLA BLA
--// CHECK DEBOUNCE
if debounce.getDebounceFromChannel(swordChannel, userId) then
--//USER IS CLICKING TOO SOOON STOP!! NOW
print("Debounce crossed")
else
--// DO SWORD STUFF
--// SET DEBOUNCE
print("Debounce passed")
debounce.setDebounce(swordChannel, userId, swordDebounce)
end
end
--// BLA BLA
--// SwordEvent.OnServer:Connect(SwordAttacked)
Thanks for reading to the end I appreciate that very much. Leave any feedback you have please!
I’m out!