Add timeouts and validation to asset loaders

Enhance several asset-loading helpers in client/functions.lua to avoid indefinite blocking and provide explicit status. LoadModel, RequestAnimDict, LoadAnimSet, and LoadParticleDictionary now accept an optional timeout (default 5000ms), return a boolean load result and the resolved name/hash, and perform existence/validity checks before requesting. LoadModel also accepts string model names (converted via joaat) and logs a warning if the model doesn't exist. These changes make asset loading safer and easier to debug when assets fail to load.

Adds timeout as optional param, stops infinite loops on model/animset/anim dict/particle requests
No breaking changes and backwards compatable
This commit is contained in:
MrNewb
2026-05-10 14:29:33 -04:00
parent 1cee09a394
commit b5ea0f7387
+42 -13
View File
@@ -356,12 +356,23 @@ end
-- Vehicle
function QBCore.Functions.LoadModel(model)
if HasModelLoaded(model) then return end
RequestModel(model)
while not HasModelLoaded(model) do
function QBCore.Functions.LoadModel(model, timeout)
timeout = timeout or 5000
local modelName = model
if type(modelName) ~= 'number' then modelName = joaat(model) end
if HasModelLoaded(modelName) then return true, model end
if not IsModelValid(modelName) and not IsModelInCdimage(modelName) then
print(('[QBCore] Model does not exist: %s'):format(modelName or model))
return false, modelName
end
RequestModel(modelName)
local count = GetGameTimer() + timeout
while not HasModelLoaded(modelName) and GetGameTimer() < count do
Wait(0)
end
return HasModelLoaded(modelName), modelName
end
function QBCore.Functions.SpawnVehicle(model, cb, coords, isnetworked, teleportInto)
@@ -880,12 +891,20 @@ function QBCore.Functions.DrawText3D(x, y, z, text)
ClearDrawOrigin()
end
function QBCore.Functions.RequestAnimDict(animDict)
if HasAnimDictLoaded(animDict) then return end
function QBCore.Functions.RequestAnimDict(animDict, timeout)
timeout = timeout or 5000
if HasAnimDictLoaded(animDict) then return true, animDict end
if not DoesAnimDictExist(animDict) then
print(('[QBCore] Animation dictionary does not exist: %s'):format(animDict))
return false, animDict
end
RequestAnimDict(animDict)
while not HasAnimDictLoaded(animDict) do
local count = GetGameTimer() + timeout
while not HasAnimDictLoaded(animDict) and GetGameTimer() < count do
Wait(0)
end
return HasAnimDictLoaded(animDict), animDict
end
function QBCore.Functions.GetClosestBone(entity, list)
@@ -948,20 +967,30 @@ function QBCore.Functions.SpawnClear(coords, radius)
return true
end
function QBCore.Functions.LoadAnimSet(animSet)
if HasAnimSetLoaded(animSet) then return end
function QBCore.Functions.LoadAnimSet(animSet, timeout)
timeout = timeout or 5000
if HasAnimSetLoaded(animSet) then return true, animSet end
RequestAnimSet(animSet)
while not HasAnimSetLoaded(animSet) do
local count = GetGameTimer() + timeout
while not HasAnimSetLoaded(animSet) and GetGameTimer() < count do
Wait(0)
end
return HasAnimSetLoaded(animSet), animSet
end
function QBCore.Functions.LoadParticleDictionary(dictionary)
if HasNamedPtfxAssetLoaded(dictionary) then return end
function QBCore.Functions.LoadParticleDictionary(dictionary, timeout)
timeout = timeout or 5000
if HasNamedPtfxAssetLoaded(dictionary) then return true, dictionary end
RequestNamedPtfxAsset(dictionary)
while not HasNamedPtfxAssetLoaded(dictionary) do
local count = GetGameTimer() + timeout
while not HasNamedPtfxAssetLoaded(dictionary) and GetGameTimer() < count do
Wait(0)
end
return HasNamedPtfxAssetLoaded(dictionary), dictionary
end
function QBCore.Functions.StartParticleAtCoord(dict, ptName, looped, coords, rot, scale, alpha, color, duration)