PlayerHandler - Sick of having to deal with race conditions? Easy solution

PlayerHandler


An easy and simple way to not have to deal with race conditions for player events such as PlayerAdded and CharacterAdded.

Source


please note that the Types are not necessary, it is so you know what everything is. Feel free to remove it if you want to.

--!strict
local Players = game:GetService('Players')
local RunService = game:GetService('RunService')

--> Types (Not necessary but so you know what everything is.)
export type PlayerAddedFunction = (player : Player) -> () -> ()
export type PlayerRemovingFunction = (player : Player) -> () -> ()
export type CharacterAddedFunction = (character : Model, player : Player) -> () -> ()
export type LocalCharacterAddedFunction = (character : Model) -> () -> ()

export type PlayerAddedConnections = {[number] : PlayerAddedFunction}
export type PlayerRemovingConnections = {[number] : PlayerRemovingFunction}
export type CharacterAddedConnections = {[number] : CharacterAddedFunction}
export type Connections = {PlayerAdded : PlayerAddedConnections, PlayerRemoving : PlayerRemovingConnections, CharacterAdded : CharacterAddedConnections}

export type LocalConnections = {[number] : LocalCharacterAddedFunction}

export type PlayerAdded = (fn : (player : Player) -> ()) -> ()
export type PlayerRemoving = (fn : (player : Player) -> ()) -> ()
export type CharacterAdded = (fn : (character : Model, player : Player) -> ()) -> ()
export type LocalCharacterAdded = (fn : (character : Model) -> ()) -> ()
export type players = {player : Player?, character : Model?, LocalCharacterAdded : LocalCharacterAdded?, PlayerAdded : PlayerAdded, PlayerRemoving : PlayerRemoving, CharacterAdded : CharacterAdded}

--> Class
local players = {} :: players

--> Connections
local Connections = {
	['PlayerAdded'] = {},
	['PlayerRemoving'] = {},
	['CharacterAdded'] = {},
} :: Connections

local LocalConnections = {} :: LocalConnections

--> Private
Players.PlayerAdded:Connect(function(player : Player)
	for i, v in pairs(Connections.PlayerAdded) do
		task.defer(v, player)
	end

	player.CharacterAdded:Connect(function(character : Model)
		for i, v in pairs(Connections.CharacterAdded) do
			task.defer(v, character, player)
		end
	end)
end)

Players.PlayerRemoving:Connect(function(player : Player)
	for i, v in pairs(Connections.PlayerRemoving) do
		task.defer(v, player)
	end
end)

--> Client
if RunService:IsClient() then
	players.player = Players.LocalPlayer

	function players.LocalCharacterAdded(fn : (character : Model) -> ())
		table.insert(LocalConnections, fn)
		if players.character ~= nil then
			task.defer(fn, players.character)
		end

		return function()
			local index = table.find(LocalConnections, fn)
			if index ~= nil then
				table.remove(LocalConnections, index)
			end
		end
	end

	if players.player.Character ~= nil then
		players.character = players.player.Character
	end

	players.player.CharacterAdded:Connect(function(character : Model)
		players.character = character
		for i, v in pairs(LocalConnections) do
			task.defer(v, character)
		end
	end)
end

--> Public
function players.PlayerAdded(fn : (player : Player) -> ())
	table.insert(Connections.PlayerAdded, fn)
	for i, v in pairs(Players:GetPlayers()) do
		task.defer(fn, v)
	end

	return function()
		local index = table.find(Connections.PlayerAdded, fn)
		if index ~= nil then
			table.remove(Connections.PlayerAdded, index)
		end
	end
end

function players.PlayerRemoving(fn : (player : Player) -> ())
	table.insert(Connections.PlayerRemoving, fn)
	return function()
		local index = table.find(Connections.PlayerRemoving, fn)
		if index ~= nil then
			table.remove(Connections.PlayerRemoving, index)
		end
	end
end

function players.CharacterAdded(fn : (Character : Model, player : Player) -> ())
	table.insert(Connections.CharacterAdded, fn)
	for _, player in pairs(Players:GetPlayers()) do
		local character = player.Character
		if character ~= nil then
			task.defer(fn, character, player)
		end
	end

	return function()
		local index = table.find(Connections.CharacterAdded, fn)
		if index ~= nil then
			table.remove(Connections.CharacterAdded, index)
		end
	end
end

--> return
return players

Examples


Server:

--!strict
local PlayerHandler = require(game:GetService('ReplicatedStorage').PlayerHandler)

PlayerHandler.PlayerAdded(function(player : Player)
	local leaderstats = Instance.new('Folder')
	leaderstats.Name = 'leaderstats'
	
	local kills = Instance.new('NumberValue')
	kills.Name = 'Kills'
	
	local deaths = Instance.new('NumberValue')
	deaths.Name = 'Deaths'
	
	kills.Parent = leaderstats
	deaths.Parent = leaderstats
	leaderstats.Parent = player
end)

PlayerHandler.CharacterAdded(function(character : Model, player : Player)
	local humanoid : Humanoid = character:WaitForChild('Humanoid')
	local leaderstats = player:FindFirstChild('leaderstats')
	local deaths = leaderstats:FindFirstChild('Deaths')
	
	humanoid.Died:Connect(function()
		deaths.Value += 1
	end)
end)

Client:

--!strict
local PlayerHandler = require(game:GetService('ReplicatedStorage').PlayerHandler)
local Player = PlayerHandler.player

local disconnectLocalCharacterAdded = PlayerHandler.LocalCharacterAdded(function(character : Model)
	local Humanoid : Humanoid = character:WaitForChild('Humanoid')
	
	Humanoid.WalkSpeed = 50
end)

task.wait(5)
disconnectLocalCharacterAdded()

All functions will return a function to “Disconnect”.

4 Likes