2025-03-08 13:44:05 +00:00
|
|
|
--[[
|
|
|
|
|
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.
|
|
|
|
|
]]
|
|
|
|
|
|
2025-09-06 14:54:36 +01:00
|
|
|
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
|
|
|
|
|
|
2025-09-24 12:29:48 +01:00
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
-------------------------------------------------------------
|
|
|
|
|
-- 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).
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
|
|
|
|
--- @param func function The function to execute when the player is loaded.
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @param onStart boolean (optional) If true, also execute on resource start. Default is false.
|
2025-03-01 12:58:43 +00:00
|
|
|
--- @usage
|
|
|
|
|
--- ```lua
|
|
|
|
|
--- onPlayerLoaded(function()
|
2025-03-08 13:44:05 +00:00
|
|
|
--- print("Player logged in")
|
|
|
|
|
--- -- Your initialization code here.
|
2025-03-01 12:58:43 +00:00
|
|
|
--- end, true)
|
|
|
|
|
--- ```
|
|
|
|
|
function onPlayerLoaded(func, onStart)
|
|
|
|
|
if onStart then
|
|
|
|
|
onResourceStart(function()
|
2025-03-08 13:44:05 +00:00
|
|
|
if not waitForLogin() then return end
|
2025-09-06 14:54:36 +01:00
|
|
|
debugPrint("^6Bridge^7: ^3onResourceStart^7()^2 routed through ^3onPlayerLoaded^7()")
|
|
|
|
|
_debouncedRun(func)
|
2025-03-01 12:58:43 +00:00
|
|
|
end, true)
|
|
|
|
|
end
|
2025-03-08 13:44:05 +00:00
|
|
|
|
2025-09-06 14:54:36 +01:00
|
|
|
local handler = function()
|
|
|
|
|
_debouncedRun(func)
|
|
|
|
|
end
|
|
|
|
|
|
2025-09-24 12:29:48 +01:00
|
|
|
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
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
2025-09-24 12:29:48 +01:00
|
|
|
print("^1ERROR^7: ^1No supported core detected for onPlayerLoaded - Check starter.lua^7")
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
--- 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)
|
|
|
|
|
--- ```
|
2025-03-01 12:58:43 +00:00
|
|
|
function onPlayerUnload(func)
|
2025-09-24 12:29:48 +01:00
|
|
|
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")
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
-------------------------------------------------------------
|
|
|
|
|
-- Resource Start and Stop Events
|
|
|
|
|
-------------------------------------------------------------
|
2025-03-01 12:58:43 +00:00
|
|
|
|
|
|
|
|
--- Executes a function when the resource starts.
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @param func function The function to execute.
|
|
|
|
|
--- @param thisScript boolean (optional) If true, only runs when this resource starts (default true).
|
2025-03-01 12:58:43 +00:00
|
|
|
--- @usage
|
|
|
|
|
--- ```lua
|
|
|
|
|
--- onResourceStart(function()
|
2025-03-08 13:44:05 +00:00
|
|
|
--- print("Script ensured")
|
|
|
|
|
--- -- Initialization code on resource start.
|
2025-03-01 12:58:43 +00:00
|
|
|
--- end, true)
|
|
|
|
|
--- ```
|
2025-06-06 00:50:13 +01:00
|
|
|
local hasPrinted = false
|
2025-03-01 12:58:43 +00:00
|
|
|
function onResourceStart(func, thisScript)
|
2025-05-07 13:55:12 +01:00
|
|
|
debugPrint("^6Bridge^7: ^2Registering ^3onResourceStart^7()")
|
2025-03-01 12:58:43 +00:00
|
|
|
AddEventHandler('onResourceStart', function(resourceName)
|
|
|
|
|
if getScript() == resourceName and (thisScript or true) then
|
2025-05-12 22:59:31 +01:00
|
|
|
if waitForSharedLoad() then
|
2025-06-06 00:50:13 +01:00
|
|
|
if not hasPrinted then
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Shared Load Detected^7.")
|
|
|
|
|
hasPrinted = true
|
|
|
|
|
end
|
2025-07-17 12:47:36 +01:00
|
|
|
func(resourceName)
|
2025-05-12 22:59:31 +01:00
|
|
|
end
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Executes a function when the resource stops.
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @param func function The function to execute.
|
|
|
|
|
--- @param thisScript boolean (optional) If true, only runs when this resource stops (default true).
|
2025-03-01 12:58:43 +00:00
|
|
|
--- @usage
|
|
|
|
|
--- ```lua
|
|
|
|
|
--- onResourceStop(function()
|
2025-03-08 13:44:05 +00:00
|
|
|
--- -- Cleanup code here.
|
2025-03-01 12:58:43 +00:00
|
|
|
--- end, true)
|
|
|
|
|
--- ```
|
|
|
|
|
function onResourceStop(func, thisScript)
|
2025-03-08 13:44:05 +00:00
|
|
|
debugPrint("^6Bridge^7: ^2Registering ^3onResourceStop^7()")
|
2025-03-01 12:58:43 +00:00
|
|
|
AddEventHandler('onResourceStop', function(resourceName)
|
|
|
|
|
if getScript() == resourceName and (thisScript or true) then
|
2025-07-17 12:47:36 +01:00
|
|
|
func(resourceName)
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
end
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
-------------------------------------------------------------
|
|
|
|
|
-- Wait for Login
|
|
|
|
|
-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
--- Blocks execution until the player is logged in.
|
|
|
|
|
--- @usage
|
2025-03-01 12:58:43 +00:00
|
|
|
--- waitForLogin()
|
|
|
|
|
function waitForLogin()
|
2025-09-24 12:29:48 +01:00
|
|
|
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.")
|
2025-03-11 22:41:13 +00:00
|
|
|
end
|
2025-09-24 12:29:48 +01:00
|
|
|
return result
|
2025-03-11 22:41:13 +00:00
|
|
|
end
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
2025-05-12 22:59:31 +01:00
|
|
|
end
|
|
|
|
|
|
2025-06-06 00:50:13 +01:00
|
|
|
local messageShown = false
|
2025-05-12 22:59:31 +01:00
|
|
|
function waitForSharedLoad()
|
2025-07-10 22:41:28 +01:00
|
|
|
local timeout = GetGameTimer() + 900000 -- 15 minutes max wait (had to up this from 2 minutes because of slow servers)
|
2025-06-06 00:50:13 +01:00
|
|
|
local loop = 0
|
2025-07-10 22:41:28 +01:00
|
|
|
|
2025-06-26 20:03:12 +01:00
|
|
|
while ((not Jobs or not next(Jobs)) or
|
|
|
|
|
(not Items or not next(Items)) or
|
|
|
|
|
(not Vehicles or not next(Vehicles))
|
2025-07-10 22:41:28 +01:00
|
|
|
) and (timeout and GetGameTimer() < timeout) do
|
2025-06-26 20:03:12 +01:00
|
|
|
if loop >= 3 and not messageShown then
|
2025-06-06 00:50:13 +01:00
|
|
|
if (not Jobs or not next(Jobs)) then
|
2025-06-26 20:03:12 +01:00
|
|
|
print("^4Debug^7: ^2Waiting for ^7Jobs^2 to be loaded")
|
2025-06-06 00:50:13 +01:00
|
|
|
end
|
|
|
|
|
if (not Items or not next(Items)) then
|
2025-06-26 20:03:12 +01:00
|
|
|
print("^4Debug^7: ^2Waiting for ^7Items^2 to be loaded")
|
2025-06-06 00:50:13 +01:00
|
|
|
end
|
|
|
|
|
if (not Vehicles or not next(Vehicles)) then
|
2025-06-26 20:03:12 +01:00
|
|
|
print("^4Debug^7: ^2Waiting for ^7Vehicles^2 to be loaded")
|
2025-06-06 00:50:13 +01:00
|
|
|
end
|
2025-06-26 20:03:12 +01:00
|
|
|
messageShown = true
|
2025-06-06 00:50:13 +01:00
|
|
|
end
|
2025-05-12 22:59:31 +01:00
|
|
|
Wait(1000)
|
2025-07-10 22:41:28 +01:00
|
|
|
|
2025-06-06 00:50:13 +01:00
|
|
|
loop += 1
|
2025-05-12 22:59:31 +01:00
|
|
|
end
|
2025-07-10 22:41:28 +01:00
|
|
|
|
|
|
|
|
if Jobs and Items and Vehicles then
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Jobs, Items, and Vehicles Loaded^7.")
|
|
|
|
|
return true
|
|
|
|
|
else
|
2025-06-06 00:50:13 +01:00
|
|
|
print("^4Error^7: ^1Timeout reached while waiting for shared load^7.")
|
2025-05-12 22:59:31 +01:00
|
|
|
return false
|
|
|
|
|
end
|
2025-04-01 14:15:50 +01:00
|
|
|
end
|