diff --git a/imports/callback/client.lua b/imports/callback/client.lua index 8a0dc086a..c86fa6228 100644 --- a/imports/callback/client.lua +++ b/imports/callback/client.lua @@ -1,26 +1,29 @@ -local events = {} +local pendingCallbacks = {} local timers = {} -local cbEvent = ('__ox_cb_%s') +local cbEvent = '__ox_cb_%s' +local callbackTimeout = GetConvarInt('ox:callbackTimeout', 60000) RegisterNetEvent(cbEvent:format(cache.resource), function(key, ...) - local cb = events[key] - return cb and cb(...) + local cb = pendingCallbacks[key] + pendingCallbacks[key] = nil + + return cb and cb(...) end) ---@param event string ---@param delay number | false prevent the event from being called for the given time local function eventTimer(event, delay) - if delay and type(delay) == 'number' and delay > 0 then - local time = GetGameTimer() + if delay and type(delay) == 'number' and delay > 0 then + local time = GetGameTimer() - if (timers[event] or 0) > time then - return false - end + if (timers[event] or 0) > time then + return false + end - timers[event] = time + delay - end + timers[event] = time + delay + end - return true + return true end ---@param _ any @@ -30,59 +33,61 @@ end ---@param ... any ---@return ... local function triggerServerCallback(_, event, delay, cb, ...) - if not eventTimer(event, delay) then return end + if not eventTimer(event, delay) then return end - local key + local key - repeat - key = ('%s:%s'):format(event, math.random(0, 100000)) - until not events[key] + repeat + key = ('%s:%s'):format(event, math.random(0, 100000)) + until not pendingCallbacks[key] - TriggerServerEvent(cbEvent:format(event), cache.resource, key, ...) + TriggerServerEvent(cbEvent:format(event), cache.resource, key, ...) - ---@type promise | false - local promise = not cb and promise.new() + ---@type promise | false + local promise = not cb and promise.new() - events[key] = function(response, ...) + pendingCallbacks[key] = function(response, ...) response = { response, ... } - events[key] = nil - if promise then - return promise:resolve(response) - end + if promise then + return promise:resolve(response) + end if cb then cb(table.unpack(response)) end - end + end - if promise then - return table.unpack(Citizen.Await(promise)) - end + if promise then + SetTimeout(callbackTimeout, function() promise:reject(("callback event '%s' timed out"):format(key)) end) + + return table.unpack(Citizen.Await(promise)) + end end ---@overload fun(event: string, delay: number | false, cb: function, ...) lib.callback = setmetatable({}, { - __call = triggerServerCallback + __call = triggerServerCallback }) ---@param event string ---@param delay? number | false prevent the event from being called for the given time. ---Sends an event to the server and halts the current thread until a response is returned. function lib.callback.await(event, delay, ...) - return triggerServerCallback(nil, event, delay, false, ...) + return triggerServerCallback(nil, event, delay, false, ...) end local function callbackResponse(success, result, ...) - if not success then - if result then - return print(('^1SCRIPT ERROR: %s^0\n%s'):format(result , Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()) or '')) - end + if not success then + if result then + return print(('^1SCRIPT ERROR: %s^0\n%s'):format(result, + Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()) or '')) + end - return false - end + return false + end - return result, ... + return result, ... end local pcall = pcall @@ -91,9 +96,9 @@ local pcall = pcall ---@param cb function --- Registers an event handler and callback function to respond to server requests. function lib.callback.register(name, cb) - RegisterNetEvent(cbEvent:format(name), function(resource, key, ...) - TriggerServerEvent(cbEvent:format(resource), key, callbackResponse(pcall(cb, ...))) - end) + RegisterNetEvent(cbEvent:format(name), function(resource, key, ...) + TriggerServerEvent(cbEvent:format(resource), key, callbackResponse(pcall(cb, ...))) + end) end return lib.callback diff --git a/imports/callback/server.lua b/imports/callback/server.lua index df4de2edf..ca3758759 100644 --- a/imports/callback/server.lua +++ b/imports/callback/server.lua @@ -1,8 +1,11 @@ -local events = {} -local cbEvent = ('__ox_cb_%s') +local pendingCallbacks = {} +local cbEvent = '__ox_cb_%s' +local callbackTimeout = GetConvarInt('ox:callbackTimeout', 60000) RegisterNetEvent(cbEvent:format(cache.resource), function(key, ...) - local cb = events[key] + local cb = pendingCallbacks[key] + pendingCallbacks[key] = nil + return cb and cb(...) end) @@ -17,16 +20,15 @@ local function triggerClientCallback(_, event, playerId, cb, ...) repeat key = ('%s:%s:%s'):format(event, math.random(0, 100000), playerId) - until not events[key] + until not pendingCallbacks[key] TriggerClientEvent(cbEvent:format(event), playerId, cache.resource, key, ...) ---@type promise | false local promise = not cb and promise.new() - events[key] = function(response, ...) + pendingCallbacks[key] = function(response, ...) response = { response, ... } - events[key] = nil if promise then return promise:resolve(response) @@ -38,6 +40,8 @@ local function triggerClientCallback(_, event, playerId, cb, ...) end if promise then + SetTimeout(callbackTimeout, function() promise:reject(("callback event '%s' timed out"):format(key)) end) + return table.unpack(Citizen.Await(promise)) end end diff --git a/package/client/resource/callback/index.ts b/package/client/resource/callback/index.ts index c4fe2ca33..17eef365d 100644 --- a/package/client/resource/callback/index.ts +++ b/package/client/resource/callback/index.ts @@ -1,9 +1,12 @@ import { cache } from '../cache'; -const activeEvents: Record void> = {}; +const pendingCallbacks: Record void> = {}; +const callbackTimeout = GetConvarInt('ox:callbackTimeout', 60000); onNet(`__ox_cb_${cache.resource}`, (key: string, ...args: any) => { - const resolve = activeEvents[key]; + const resolve = pendingCallbacks[key]; + delete pendingCallbacks[key]; + return resolve && resolve(...args); }); @@ -32,12 +35,14 @@ export function triggerServerCallback( do { key = `${eventName}:${Math.floor(Math.random() * (100000 + 1))}`; - } while (activeEvents[key]); + } while (pendingCallbacks[key]); emitNet(`__ox_cb_${eventName}`, cache.resource, key, ...args); - return new Promise((resolve) => { - activeEvents[key] = resolve; + return new Promise((resolve, reject) => { + pendingCallbacks[key] = resolve; + + setTimeout(reject, callbackTimeout, `callback event '${key}' timed out`); }); } diff --git a/package/server/resource/callback/index.ts b/package/server/resource/callback/index.ts index 994b32ae5..ffc953403 100644 --- a/package/server/resource/callback/index.ts +++ b/package/server/resource/callback/index.ts @@ -1,9 +1,12 @@ import { cache } from '../cache'; -const activeEvents: Record void> = {}; +const pendingCallbacks: Record void> = {}; +const callbackTimeout = GetConvarInt('ox:callbackTimeout', 60000); onNet(`__ox_cb_${cache.resource}`, (key: string, ...args: any) => { - const resolve = activeEvents[key]; + const resolve = pendingCallbacks[key]; + delete pendingCallbacks[key]; + return resolve && resolve(...args); }); @@ -16,12 +19,14 @@ export function triggerClientCallback( do { key = `${eventName}:${Math.floor(Math.random() * (100000 + 1))}:${playerId}`; - } while (activeEvents[key]); + } while (pendingCallbacks[key]); emitNet(`__ox_cb_${eventName}`, playerId, cache.resource, key, ...args); - return new Promise((resolve) => { - activeEvents[key] = resolve; + return new Promise((resolve, reject) => { + pendingCallbacks[key] = resolve; + + setTimeout(reject, callbackTimeout, `callback event '${key}' timed out`); }); }