342 lines
No EOL
12 KiB
Lua
342 lines
No EOL
12 KiB
Lua
--[[
|
|
-- Author: Tim Plate
|
|
-- Project: Advanced Roleplay Environment
|
|
-- Copyright (c) 2022 Tim Plate Solutions
|
|
--]]
|
|
|
|
-- Source: https://github.com/pitermcflebor/pmc-callbacks/
|
|
|
|
local IS_SERVER = IsDuplicityVersion()
|
|
local table_unpack = table.unpack
|
|
-- from scheduler.lua
|
|
local debug = debug
|
|
local debug_getinfo = debug.getinfo
|
|
local msgpack = msgpack
|
|
local msgpack_pack = msgpack.pack
|
|
local msgpack_unpack = msgpack.unpack
|
|
local msgpack_pack_args = msgpack.pack_args
|
|
-- from deferred.lua
|
|
local PENDING = 0
|
|
local RESOLVING = 1
|
|
local REJECTING = 2
|
|
local RESOLVED = 3
|
|
local REJECTED = 4
|
|
|
|
-- custom function to check any type
|
|
local function ensure(obj, typeof, opt_typeof, errMessage)
|
|
local objtype = type(obj)
|
|
local di = debug_getinfo(2)
|
|
local errMessage = errMessage or (opt_typeof == nil and (di.name .. ' expected %s, but got %s') or (di.name .. ' expected %s or %s, but got %s'))
|
|
if typeof ~= 'function' then
|
|
if objtype ~= typeof and objtype ~= opt_typeof then
|
|
error((errMessage):format(typeof, (opt_typeof == nil and objtype or opt_typeof), objtype))
|
|
end
|
|
else
|
|
if objtype == 'table' and not rawget(obj, '__cfx_functionReference') then
|
|
error((errMessage):format(typeof, (opt_typeof == nil and objtype or opt_typeof), objtype))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- SERVER-SIDE
|
|
if IS_SERVER then
|
|
--
|
|
-- @table RegisterServerCallback
|
|
--
|
|
-- @string eventName - The name of the event to be registered
|
|
-- @function eventCallback - The function to be executed when event is fired
|
|
_G.RegisterServerCallback = function(args)
|
|
ensure(args, 'table'); ensure(args.eventName, 'string'); ensure(args.eventCallback, 'function')
|
|
|
|
-- save the callback function on this call
|
|
local eventCallback = args.eventCallback
|
|
-- save the event name on this call
|
|
local eventName = args.eventName
|
|
-- save the event data to return
|
|
local eventData = RegisterNetEvent('pmc__server_callback:'..eventName, function(packed, src, cb)
|
|
-- save the source on this call
|
|
local source = tonumber(source)
|
|
-- check if this is a simulated callback (TriggerServerCallback)
|
|
if not source then
|
|
-- return the simulated data
|
|
cb( msgpack_pack_args( eventCallback(src, table_unpack(msgpack_unpack(packed)) ) ) )
|
|
else
|
|
-- return the data
|
|
TriggerClientEvent(('pmc__client_callback_response:%s:%s'):format(eventName, source), source, msgpack_pack_args( eventCallback(source, table_unpack(msgpack_unpack(packed)) ) ))
|
|
end
|
|
end)
|
|
-- return the event data to UnregisterServerCallback
|
|
return eventData
|
|
end
|
|
|
|
--
|
|
-- @void UnregisterServerCallback
|
|
--
|
|
-- @table eventData - The data from the RegisterServerCallback
|
|
_G.UnregisterServerCallback = function(eventData)
|
|
RemoveEventHandler(eventData)
|
|
end
|
|
|
|
--
|
|
-- @any TriggerClientCallback
|
|
--
|
|
-- @string/number source - The playerId to be triggered
|
|
-- @string eventName - The name of the event to be fired
|
|
-- @table args - The arguments to be sent with the event
|
|
-- [@number timeout - Seconds to wait for response]
|
|
-- [@function timedout - The function that will be executed if timeout is reached]
|
|
-- [@function callback - Asynchronous response]
|
|
_G.TriggerClientCallback = function(args)
|
|
ensure(args, 'table'); ensure(args.source, 'string', 'number'); ensure(args.eventName, 'string'); ensure(args.args, 'table', 'nil'); ensure(args.timeout, 'number', 'nil'); ensure(args.timedout, 'function', 'nil'); ensure(args.callback, 'function', 'nil')
|
|
|
|
-- check if is a valid playerId [1-...]
|
|
if tonumber(args.source) >= 0 then
|
|
-- create a new ticket
|
|
local ticket = tostring(args.source) .. 'x' .. tostring(GetGameTimer())
|
|
-- create a new promise
|
|
local prom = promise.new()
|
|
-- save the callback function on this call
|
|
local eventCallback = args.callback
|
|
-- save the event data on this call
|
|
local eventData = RegisterNetEvent(('pmc__callback_retval:%s:%s:%s'):format(args.source, args.eventName, ticket), function(packed)
|
|
-- check if this call was async
|
|
-- & if promise wasn't rejected or resolved
|
|
if eventCallback and prom.state == PENDING then eventCallback( table_unpack(msgpack_unpack(packed)) ) end
|
|
prom:resolve( table_unpack(msgpack_unpack(packed)) )
|
|
end)
|
|
|
|
-- request the callback
|
|
TriggerClientEvent(('pmc__client_callback:%s'):format(args.eventName), args.source, msgpack_pack(args.args or {}), ticket)
|
|
|
|
-- timeout response
|
|
if args.timeout ~= nil and args.timedout then
|
|
local timedout = args.timedout
|
|
SetTimeout(args.timeout * 1000, function()
|
|
-- check if promise wasn't resolved
|
|
if
|
|
prom.state == PENDING or
|
|
prom.state == REJECTED or
|
|
prom.state == REJECTING
|
|
then
|
|
-- call the timeout callback
|
|
timedout(prom.state)
|
|
-- reject the promise
|
|
if prom.state == PENDING then prom:reject() end
|
|
-- remove the event handler
|
|
RemoveEventHandler(eventData)
|
|
end
|
|
end)
|
|
end
|
|
|
|
-- check if this call was async
|
|
if not eventCallback then
|
|
local result = Citizen.Await(prom)
|
|
-- remove the event handler
|
|
RemoveEventHandler(eventData)
|
|
return result
|
|
end
|
|
else
|
|
-- raise an error if source isn't valid
|
|
error 'source should be equal too or higher than 0'
|
|
end
|
|
end
|
|
|
|
--
|
|
-- @any TriggerServerCallback
|
|
-- Simulate a client callback
|
|
--
|
|
-- @string/number source - The simulated playerId that triggers
|
|
-- @string eventName - The name of the event to be fired
|
|
-- @table args - The arguments to be sent with the event
|
|
-- [@number timeout - Seconds to wait for response]
|
|
-- [@function timedout - The function that will be executed if timeout is reached]
|
|
-- [@function callback - Asynchronous response]
|
|
_G.TriggerServerCallback = function(args)
|
|
ensure(args, 'table'); ensure(args.source, 'string', 'number'); ensure(args.eventName, 'string'); ensure(args.args, 'table', 'nil'); ensure(args.timeout, 'number', 'nil'); ensure(args.timedout, 'function', 'nil'); ensure(args.callback, 'function', 'nil')
|
|
|
|
-- create a new promise
|
|
local prom = promise.new()
|
|
-- save the callback on this call
|
|
local eventCallback = args.callback
|
|
-- save the event name on this call
|
|
local eventName = args.eventName
|
|
TriggerEvent('pmc__server_callback:'..eventName, msgpack_pack(args.args or {}), args.source,
|
|
function(packed)
|
|
-- check if this call was async
|
|
-- & if promise wasn't rejected or resolved
|
|
if eventCallback and prom.state == PENDING then eventCallback( table_unpack(msgpack_unpack(packed)) ) end
|
|
prom:resolve( table_unpack(msgpack_unpack(packed)) )
|
|
end)
|
|
|
|
-- timeout response
|
|
if args.timeout ~= nil and args.timedout then
|
|
local timedout = args.timedout
|
|
SetTimeout(args.timeout * 1000, function()
|
|
-- check if promise wasn't resolved
|
|
if
|
|
prom.state == PENDING or
|
|
prom.state == REJECTED or
|
|
prom.state == REJECTING
|
|
then
|
|
-- call timeout callback
|
|
timedout(prom.state)
|
|
-- reject the promise
|
|
if prom.state == PENDING then prom:reject() end
|
|
end
|
|
end)
|
|
end
|
|
|
|
-- check if this call was async
|
|
if not eventCallback then
|
|
return Citizen.Await(prom)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- CLIENT-SIDE
|
|
if not IS_SERVER then
|
|
local SERVER_ID = GetPlayerServerId(PlayerId())
|
|
|
|
--
|
|
-- @table RegisterClientCallback
|
|
--
|
|
-- @string eventName - The name of the event to be fired
|
|
-- @function eventCallback - The function to be executed when event is fired
|
|
_G.RegisterClientCallback = function(args)
|
|
ensure(args, 'table'); ensure(args.eventName, 'string'); ensure(args.eventCallback, 'function')
|
|
|
|
-- save the callback function on this call
|
|
local eventCallback = args.eventCallback
|
|
-- save the event name on this call
|
|
local eventName = args.eventName
|
|
-- save the event data to return
|
|
local eventData = RegisterNetEvent('pmc__client_callback:'..eventName, function(packed, ticket)
|
|
-- check if this call is simulated (TriggerClientCallback)
|
|
if type(ticket) == 'function' then
|
|
-- return the data to the simulated call
|
|
ticket( msgpack_pack_args( eventCallback( table_unpack(msgpack_unpack(packed)) ) ) )
|
|
else
|
|
-- return the data to the call
|
|
TriggerServerEvent(('pmc__callback_retval:%s:%s:%s'):format(SERVER_ID, eventName, ticket), msgpack_pack_args( eventCallback( table_unpack(msgpack_unpack(packed)) ) ))
|
|
end
|
|
end)
|
|
-- return event data so you can UnregisterClientCallback
|
|
return eventData
|
|
end
|
|
|
|
--
|
|
-- @void UnregisterClientCallback
|
|
--
|
|
-- @table eventData - The data from RegisterClientCallback
|
|
_G.UnregisterClientCallback = function(eventData)
|
|
RemoveEventHandler(eventData)
|
|
end
|
|
|
|
--
|
|
-- @any TriggerServerCallback
|
|
--
|
|
-- @string eventName - The name of the event to be fired
|
|
-- @table args - The arguments passed with the event
|
|
-- [@number timeout - Seconds to wait for response]
|
|
-- [@function timedout - The function that will be executed if timeout is reached]
|
|
-- [@function callback - Asynchronous response]
|
|
_G.TriggerServerCallback = function(args)
|
|
ensure(args, 'table'); ensure(args.args, 'table', 'nil'); ensure(args.eventName, 'string'); ensure(args.timeout, 'number', 'nil'); ensure(args.timedout, 'function', 'nil'); ensure(args.callback, 'function', 'nil')
|
|
|
|
-- create a new promise
|
|
local prom = promise.new()
|
|
-- save the callback function on this call
|
|
local eventCallback = args.callback
|
|
-- save the event data to remove it when resolved
|
|
local eventData = RegisterNetEvent(('pmc__client_callback_response:%s:%s'):format(args.eventName, SERVER_ID),
|
|
function(packed)
|
|
-- check if this call is async
|
|
-- & the promise wasn't rejected or resolved
|
|
if eventCallback and prom.state == PENDING then eventCallback( table_unpack(msgpack_unpack(packed)) ) end
|
|
prom:resolve( table_unpack(msgpack_unpack(packed)) )
|
|
|
|
end)
|
|
|
|
-- fire the callback event
|
|
TriggerServerEvent('pmc__server_callback:'..args.eventName, msgpack_pack( args.args ))
|
|
|
|
-- timeout response
|
|
if args.timeout ~= nil and args.timedout then
|
|
local timedout = args.timedout
|
|
SetTimeout(args.timeout * 1000, function()
|
|
-- check if the promise wasn't resolved yet
|
|
if
|
|
prom.state == PENDING or
|
|
prom.state == REJECTED or
|
|
prom.state == REJECTING
|
|
then
|
|
-- call the timeout callback
|
|
timedout(prom.state)
|
|
-- reject the promise if it wasn't rejected
|
|
if prom.state == PENDING then prom:reject() end
|
|
-- remove the event handler
|
|
RemoveEventHandler(eventData)
|
|
end
|
|
end)
|
|
end
|
|
|
|
-- check if this call is async
|
|
if not eventCallback then
|
|
local result = Citizen.Await(prom)
|
|
-- remove the event handler
|
|
RemoveEventHandler(eventData)
|
|
return result
|
|
end
|
|
end
|
|
|
|
--
|
|
-- @any TriggerClientCallback
|
|
-- Simulate a server callback
|
|
--
|
|
-- @string eventName - The name of the event to be fired
|
|
-- @table args - The arguments to be sent with the event
|
|
-- [@number timeout - Seconds to wait for response]
|
|
-- [@function timedout - The function that will be executed if timeout is reached]
|
|
-- [@function callback - Asynchronous response]
|
|
_G.TriggerClientCallback = function(args)
|
|
ensure(args, 'table'); ensure(args.eventName, 'string'); ensure(args.args, 'table', 'nil'); ensure(args.timeout, 'number', 'nil'); ensure(args.timedout, 'function', 'nil'); ensure(args.callback, 'function', 'nil')
|
|
|
|
-- create a new promise for this call
|
|
local prom = promise.new()
|
|
-- save the callback function on this call
|
|
local eventCallback = args.callback
|
|
-- save the event name on this call
|
|
local eventName = args.eventName
|
|
-- trigger the callback
|
|
TriggerEvent('pmc__client_callback:'..eventName, msgpack_pack(args.args or {}),
|
|
function(packed)
|
|
-- check if it was an async call
|
|
-- & if the promise wasn't rejected or already resolved
|
|
if eventCallback and prom.state == PENDING then eventCallback( table_unpack(msgpack_unpack(packed)) ) end
|
|
prom:resolve( table_unpack(msgpack_unpack(packed)) )
|
|
end)
|
|
|
|
-- timeout response
|
|
if args.timeout ~= nil and args.timedout then
|
|
local timedout = args.timedout
|
|
SetTimeout(args.timeout * 1000, function()
|
|
-- check if the promise wasn't resolved
|
|
if
|
|
prom.state == PENDING or
|
|
prom.state == REJECTED or
|
|
prom.state == REJECTING
|
|
then
|
|
-- call timeout callback
|
|
timedout(prom.state)
|
|
-- check if it's pending and reject
|
|
if prom.state == PENDING then prom:reject() end
|
|
end
|
|
end)
|
|
end
|
|
|
|
-- check if this call is async
|
|
if not eventCallback then
|
|
return Citizen.Await(prom)
|
|
end
|
|
end
|
|
end |