Connection | Connection:Disconnect

Hello! I’m wondering - should I use event connections inside a server script instead of using a debounce table for each player?

Also, how can I use connection:Disconnect() for a specific player in this part of the script?

elseif keycard:GetAttribute("KeycardReader") then
    local prompt = keycard.prompt.ProximityPrompt
    if prompt then
        prompt.Triggered:Connect(function(player)
            if player.Character:FindFirstChild("Keycard") then
                print("checked")
            end
        end)
    end
end

Here’s the full script:

local function setupKeycard(keycard)
    if keycard:GetAttribute("Keycard") then
        local clickDetector = keycard:FindFirstChild("ClickDetector")
        local connection

        if clickDetector then
            connection = clickDetector.MouseClick:Connect(function(player)
                if not player.Backpack:FindFirstChild("Keycard") and not player.Character:FindFirstChild("Keycard") then
                    local toolClone = keycardTool:Clone()
                    toolClone.Parent = player.Backpack
                end
            end)
        end

    elseif keycard:GetAttribute("KeycardReader") then
        local prompt = keycard.prompt.ProximityPrompt
        if prompt then
            prompt.Triggered:Connect(function(player)
                if player.Character:FindFirstChild("Keycard") then
                    print("checked")
                end
            end)
        end
    end
end

What is the context of this script? Where is it placed and what do you want it to do?

This is basically a script for a keycard reader, and it’s located in ServerScriptService.
I’ve used connections before in single-player setups (like conn:Disconnect()), but I’m not sure whether I should use connections here for each player and store them in a table, or just use a debounce table instead.
I need this event to be triggered only once - when a player successfully scans their keycard.

I would say in this case using conn:Disconnect would be fine. Just make sure that if it’s a one-and-done to cleanup the other connections.

You may be able to accomplish this by doing the following:

local PlayerConns = {} -- Universal table for players

local function AddPlayerConns(PlyrName, ConnType, Conn)
    if Conn ~= nil and Conn.Connected then -- Is the connection not nil and is it connected?
        PlayerConns[PlyrName][ConnType] = Conn -- Add to the table
    end
end

local function KillConnType(ConnType)
    for PlyrName, Data in pairs(PlayerConns) do -- Loop through the present connections
        for Type, Conn in pairs(Data) do -- Loop through the player's personal table
            if Type == ConnType then -- Does this one have the connection?
                PlayerConns[PlyrName][Type] = nil -- Remove from the table
                Conn:Disconnect() -- Disconnect the event
            end
        end
    end
end


local function SetUpKeycard(keycard)
    local Conn = nil
    if keycard:GetAttribute("Keycard") then
        local clickDetector = keycard:FindFirstChild("ClickDetector")

        if clickDetector then
            Conn = clickDetector.MouseClick:Connect(function(player)
                if not player.Backpack:FindFirstChild("Keycard") and not player.Character:FindFirstChild("Keycard") then
                    local toolClone = keycardTool:Clone()
                    toolClone.Parent = player.Backpack
                    KillConnType("Red Keycard")
                end
            end)
        end

    elseif keycard:GetAttribute("KeycardReader") then
        local prompt = keycard.prompt.ProximityPrompt
        if prompt then
            Conn = prompt.Triggered:Connect(function(player)
                if player.Character:FindFirstChild("Keycard") then
                    print("checked")
                    KillConnType("Red Keycard")
                end
            end)
        end
    end
    for _, plyr in ipairs(game.Players:GetPlayers()) do
        AddPlayerConns(plyr.Name, "Red Keycard", Conn)
    end
end


game.Players.PlayerAdded:Connect(function(plyr)
    PlayerConns[plyr.Name] = {} -- Adds a reference for the player
end)
game.Players.PlayerRemoving:Connect(function(plyr)
    for _, Conn in pairs(PlayerConns[plyr.Name]) do
        Conn:Disconnect() -- Disconnect all the player's connections
    end
    PlayerConns[plyr.Name] = nil -- Remove their reference
end)

Hopefully this makes sense haha.

I have another question. I want to set up the keycard when the player reaches a certain checkpoint - I have an attribute for this.

Should I track the attribute change in a LocalScript and then fire a RemoteEvent to the server once the player reaches the checkpoint (and have the server verify it before setting up the keycard)? Or should I handle everything on the server instead? I’m just trying to figure out the most resource-efficient way to do this.

Well, using a connection table per player or per object can be cleaner than trying to manage a debounce array for multiple players. With connections, you can disconnect a specific player’s listener.
At the core, both approaches are just using a table to track state.

In your script, the prompt.Triggered:Connect returns a connection, but you never store it. Without storing it, you cannot call :Disconnect() later.

Unless it’s GUI logic I wouldn’t trust the client with that.

Client → you’re relying on the player to tell the server the truth.
Server → you can validate that what they did was “legal” (with the exception of when client verification is necessary).

Yes, 100%.

I’d say make the program functional for the time being (and thinking critically on it) and then optimize later. As a Donald Knuth had stated, premature optimization is the root of all evil (or at least most of it) in programming.