Skip to content

Instantly share code, notes, and snippets.

@gaymeowing
Last active January 21, 2024 02:29
Show Gist options
  • Save gaymeowing/b9cd1af5518743cdfd389afac9679edc to your computer and use it in GitHub Desktop.
Save gaymeowing/b9cd1af5518743cdfd389afac9679edc to your computer and use it in GitHub Desktop.
PlayerLoaded event handler for Zap https://zap.redblox.dev/
type void = boolean?
event PlayerLoaded = {
from: Client,
type: Reliable,
call: SingleAsync,
data: void
}
--!strict
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local Events = require(Path.To.ZapClient)
if not game:IsLoaded() then
game.Loaded:Wait()
Workspace.PersistentLoaded:Wait()
end
Events.PlayerLoaded.Fire()
--!strict
-- PlayerLoaded
-- Small serverside module for knowing when a player has loaded
-- @Kalrnlo
-- 20/01/2024
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Events = require(Path.To.ZapServer)
local Spawn = require(Path.To.Spawn)
-- [userid] = {threads yielding for player to load}
local SpecificWaitingThreads = {} :: {[number]: {thread}}
local CallbackList = {} :: {{(Player: Player) -> ()}}
local NonSpecificWaitingThreads = {} :: {thread}
local PlayersLoaded = {} :: {Player}
local HasInitiated = false
local function SpawnCallbacks(Callbacks: {{(Player: Player) -> ()}}, Player: Player)
for _, CallbackData in Callbacks do
Spawn(CallbackData[1], Player)
end
end
local function ResumeThreadsWithPlayer(Threads: {thread}, Player: Player)
for _, Thread in Threads do
if Player then
coroutine.resume(Thread, Player)
else
-- just closing the thread as the player left
coroutine.close(Thread)
end
end
end
local function WaitForLoad(Player: Player): Player
if not table.find(PlayersLoaded, Player) then
local SpecificThreads = SpecificWaitingThreads[Player.UserId]
local Thread = coroutine.running()
if SpecificThreads then
table.insert(SpecificThreads, Thread)
else
SpecificWaitingThreads[Player.UserId] = table.create(1, Thread)
end
return coroutine.yield()
else
return Player
end
end
local function SafeOn(Callback: (Player: Player) -> ())
local CallbackData = table.create(1, Callback)
table.insert(CallbackList, CallbackData)
if #PlayersLoaded ~= 0 then
for _, Player in PlayersLoaded do
if not Player then continue end
Spawn(Callback, Player)
end
end
return function()
local Index = table.find(CallbackList, CallbackData)
table.remove(CallbackList, Index)
end
end
local function On(Callback: (Player: Player) -> ())
local CallbackData = table.create(1, Callback)
table.insert(CallbackList, CallbackData)
return function()
local Index = table.find(CallbackList, CallbackData)
table.remove(CallbackList, Index)
end
end
local function Wait(): Player
table.insert(NonSpecificWaitingThreads, coroutine.running())
return coroutine.yield()
end
local function Init()
if not HasInitiated then
HasInitiated = true
else
error("[PlayerLoaded] cannot initalize twice")
end
Events.PlayerLoaded.SetCallback(function(Player)
if not table.find(PlayersLoaded, Player) then
local SpecificThreads = SpecificWaitingThreads[Player.UserId]
if #NonSpecificWaitingThreads ~= 0 then
Spawn(ResumeThreadsWithPlayer, NonSpecificWaitingThreads, Player)
NonSpecificWaitingThreads = {}
end
if SpecificThreads then
Spawn(ResumeThreadsWithPlayer, SpecificThreads, Player)
SpecificWaitingThreads[Player.UserId] = nil
end
if #CallbackList ~= 0 then
Spawn(SpawnCallbacks, CallbackList, Player)
end
end
end)
Players.PlayerRemoving:Connect(function(Player)
local Index = table.find(PlayersLoaded, Player)
SpecificWaitingThreads[Player.UserId] = nil
if Index then
table.remove(PlayersLoaded, Index)
end
end)
end
return {
WaitForLoad = WaitForLoad,
SafeOn = SafeOn,
Wait = Wait,
Init = Init,
On = On,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment