--[[ Player & Resource Event Utility Functions ------------------------------------------- This module provides functions to: • Execute code when the player character is loaded or unloaded. • Execute code on resource start and stop. • Wait for the player to be logged in before proceeding. ]] local onLoadLast = {} -- keyed by tostring(func) local function _debouncedRun(func) local key = tostring(func) local now = GetGameTimer() local last = onLoadLast[key] or -1e12 if (now - last) < 5000 then debugPrint(("^6Bridge^7 ^3onPlayerLoaded^7 skipped — cooldown %dms remaining"):format(5000 - (now - last))) return end onLoadLast[key] = now CreateThread(function() Wait(2000) -- keep your small delay debugPrint("^6Bridge^7: ^2Executing onPlayerLoaded callback") func() end) end local frameworkLoadFunc = { { framework = Exports.QBXExport, onPlayerLoaded = function(func) RegisterNetEvent('QBCore:Client:OnPlayerLoaded', func) end, onPlayerUnload = function(func) RegisterNetEvent('QBCore:Client:OnPlayerUnload', func) end, waitforLogin = function(timeout) local startTime = GetGameTimer() while not LocalPlayer.state.isLoggedIn and (GetGameTimer() - startTime) < timeout do Wait(100) end return LocalPlayer.state.isLoggedIn end, }, { framework = Exports.QBExport, onPlayerLoaded = function(func) RegisterNetEvent('QBCore:Client:OnPlayerLoaded', func) end, onPlayerUnload = function(func) RegisterNetEvent('QBCore:Client:OnPlayerUnload', func) end, waitforLogin = function(timeout) local startTime = GetGameTimer() while not LocalPlayer.state.isLoggedIn and (GetGameTimer() - startTime) < timeout do Wait(100) end return LocalPlayer.state.isLoggedIn end, }, { framework = Exports.ESXExport, onPlayerLoaded = function(func) RegisterNetEvent("esx:playerLoaded", function() -- make sure shared has loaded (because sql) if waitForSharedLoad() then func() end end) end, onPlayerUnload = function(func) RegisterNetEvent("esx:onPlayerLogout", func) end, waitforLogin = function(timeout) local startTime = GetGameTimer() while (GetGameTimer() - startTime) < timeout do local playerData = ESX.GetPlayerData() if playerData and playerData.job then return true end Wait(100) end end, }, { framework = Exports.OXCoreExport, onPlayerLoaded = function(func) RegisterNetEvent('ox:playerLoaded', func) end, onPlayerUnload = function(func) RegisterNetEvent('ox:playerLogout', func) end, waitforLogin = function(timeout) if OxPlayer["stateId"] then return true end while not OxPlayer["stateId"] do Wait(1000) if OxPlayer.get["stateId"] then return true end end end, }, { framework = Exports.RSGExport, onPlayerLoaded = function(func) RegisterNetEvent('RSGCore:Client:OnPlayerLoaded', func) end, onPlayerUnload = function(func) RegisterNetEvent('RSGCore:Client:OnPlayerUnload', func) end, waitforLogin = function(timeout) local startTime = GetGameTimer() while not LocalPlayer.state.isLoggedIn and (GetGameTimer() - startTime) < timeout do Wait(100) end return LocalPlayer.state.isLoggedIn end, }, { framework = Exports.VorpExport, onPlayerLoaded = function(func) RegisterNetEvent('vorp_core:Client:OnPlayerSpawned', func) end, onPlayerUnload = function(func) --?? end, waitforLogin = function(timeout) local startTime = GetGameTimer() while not LocalPlayer.state.IsInSession and (GetGameTimer() - startTime) < timeout do Wait(100) end return LocalPlayer.state.IsInSession end, }, } ------------------------------------------------------------- -- Player Loaded and Unloaded Events ------------------------------------------------------------- --- Executes a function when the player character is loaded. --- If onStart is true, the function will also run on resource start (after ensuring the player is logged in). --- --- @param func function The function to execute when the player is loaded. --- @param onStart boolean (optional) If true, also execute on resource start. Default is false. --- @usage --- ```lua --- onPlayerLoaded(function() --- print("Player logged in") --- -- Your initialization code here. --- end, true) --- ``` function onPlayerLoaded(func, onStart) if onStart then onResourceStart(function() if not waitForLogin() then return end debugPrint("^6Bridge^7: ^3onResourceStart^7()^2 routed through ^3onPlayerLoaded^7()") _debouncedRun(func) end, true) end local handler = function() _debouncedRun(func) end for i = 1, #frameworkLoadFunc do local data = frameworkLoadFunc[i] jsonPrint(data) if isStarted(data.framework) then debugPrint("^6Bridge^7: ^2Registering ^3"..data.framework.." ^5onPlayerLoaded^7()") data.onPlayerLoaded(handler) return end end print("^1ERROR^7: ^1No supported core detected for onPlayerLoaded - Check starter.lua^7") end --- Executes a function when the player character is unloaded. --- @param func function The function to execute when the player unloads. --- @usage --- ```lua --- onPlayerUnload(function() --- print("Player has logged out of their character") --- -- Your cleanup code here. --- end) --- ``` function onPlayerUnload(func) for i = 1, #frameworkLoadFunc do local data = frameworkLoadFunc[i] if isStarted(data.framework) then debugPrint("^6Bridge^7: ^2Registering ^3"..data.framework.." ^5onPlayerUnload^7()") data.onPlayerUnload(func) return end end print("^1ERROR^7: ^1No supported core detected for onPlayerUnload - Check starter.lua^7") end ------------------------------------------------------------- -- Resource Start and Stop Events ------------------------------------------------------------- --- Executes a function when the resource starts. --- @param func function The function to execute. --- @param thisScript boolean (optional) If true, only runs when this resource starts (default true). --- @usage --- ```lua --- onResourceStart(function() --- print("Script ensured") --- -- Initialization code on resource start. --- end, true) --- ``` local hasPrinted = false function onResourceStart(func, thisScript) debugPrint("^6Bridge^7: ^2Registering ^3onResourceStart^7()") AddEventHandler('onResourceStart', function(resourceName) if getScript() == resourceName and (thisScript or true) then if waitForSharedLoad() then if not hasPrinted then debugPrint("^6Bridge^7: ^2Shared Load Detected^7.") hasPrinted = true end func(resourceName) end end end) end --- Executes a function when the resource stops. --- @param func function The function to execute. --- @param thisScript boolean (optional) If true, only runs when this resource stops (default true). --- @usage --- ```lua --- onResourceStop(function() --- -- Cleanup code here. --- end, true) --- ``` function onResourceStop(func, thisScript) debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^7()") AddEventHandler('onResourceStop', function(resourceName) if getScript() == resourceName and (thisScript or true) then func(resourceName) end end) end ------------------------------------------------------------- -- Wait for Login ------------------------------------------------------------- --- Blocks execution until the player is logged in. --- @usage --- waitForLogin() function waitForLogin() for i = 1, #frameworkLoadFunc do local data = frameworkLoadFunc[i] if isStarted(data.framework) then debugPrint("^6Bridge^7: ^2Waiting for ^3"..data.framework.."^2 player login^7.") local result = data.waitforLogin(10000) if result == true then debugPrint("^6Bridge^7: ^3"..data.framework.."^2 Player Login Detected^7.") else print("^4Error^7: ^2Timeout reached while waiting for player login^7.") end return result end end end local messageShown = false function waitForSharedLoad() local timeout = GetGameTimer() + 900000 -- 15 minutes max wait (had to up this from 2 minutes because of slow servers) local loop = 0 while ((not Jobs or not next(Jobs)) or (not Items or not next(Items)) or (not Vehicles or not next(Vehicles)) ) and (timeout and GetGameTimer() < timeout) do if loop >= 3 and not messageShown then if (not Jobs or not next(Jobs)) then print("^4Debug^7: ^2Waiting for ^7Jobs^2 to be loaded") end if (not Items or not next(Items)) then print("^4Debug^7: ^2Waiting for ^7Items^2 to be loaded") end if (not Vehicles or not next(Vehicles)) then print("^4Debug^7: ^2Waiting for ^7Vehicles^2 to be loaded") end messageShown = true end Wait(1000) loop += 1 end if Jobs and Items and Vehicles then debugPrint("^6Bridge^7: ^2Jobs, Items, and Vehicles Loaded^7.") return true else print("^4Error^7: ^1Timeout reached while waiting for shared load^7.") return false end end