Advertisement
Rochet2

Lua wait function using coroutines

Mar 29th, 2018
577
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 4.56 KB | None | 0 0
  1. --[[
  2. usage:
  3. local w = require("wait") -- assumes this script is saved in wait.lua
  4.  
  5. local function errf(player, creature, gameobject) print(player, creature, gameobject) end -- in case of error, run this function, args are set to nil if missing, so you can detect which ones are there and which are not
  6. local function func(...) print(...) w.wait(3000) print(...) end -- wait 3 seconds in middle
  7. w.call(errf, func, player, creature, gameobject) -- errf can be nil to ignore errors, func is called with arguments that follow it. The first argument (player) is used as the object used for waiting (RegisterEvent) and as the object that fetches other objects when waiting ends.
  8. --]]
  9.  
  10. --[[
  11. This script is a proof of concept and not the most performant and polished solution or API.
  12.  
  13. How it works internally?
  14. Lua coroutines are used to wrap the called function and then yield and resume in addition to timed events (RegisterEvent)
  15. are used to achieve waiting x amount of time in middle of functions.
  16. RegisterEvent is used on the first argument to the called function.
  17.  
  18. What if a player logs out while function is waiting?
  19. All parameters are checked for if they are userdata (player,creature,gameobject). If they are, then they are encapsulated by proxy tables.
  20. These proxies are then offered instead of the actual userdata. The proxies allow changing/updating the underlying userdata.
  21. The proxies store information such as guid to update the userdata.
  22. If updating a proxy fails, the errf function is called.
  23. Updating is needed since when a player relogs the pointer/userdata can be different to that player object.
  24.  
  25. Does it have problems?
  26. - any new objects inside a function are not proxied, so they are dangerous to use. As an example victim = creature:GetVictim(); w.wait(1); print(victim:GetName()); is dangerous
  27. - if a creature dies, the new creature spawned is not going to be recognized as the same npc that died and errf is called
  28. --]]
  29.  
  30. local function data(obj)
  31.     return {mapid = obj:GetMapId(), instid = obj:GetInstanceId(), guid = obj:GetGUID()}
  32. end
  33. local function fetch(waiter, data)
  34.     if waiter:GetMapId() == data.mapid and waiter:GetInstanceId() == data.instid then
  35.         return waiter:GetMap():GetWorldObject(data.guid)
  36.     end
  37. end
  38. local refreshers = {
  39.     Player = {
  40.         data = data,
  41.         fetch = fetch,
  42.     },
  43.     Creature = {
  44.         data = data,
  45.         fetch = fetch,
  46.     },
  47.     GameObject = {
  48.         data = data,
  49.         fetch = fetch,
  50.     },
  51. }
  52.  
  53. local function canproxy(t)
  54.     local ty = type(t)
  55.     return ty == "userdata" and t.GetObjectType and refreshers[t:GetObjectType()]
  56. end
  57.  
  58. local function isproxy(t)
  59.     return type(t) == "table" and t.isproxy
  60. end
  61.  
  62. local function proxy(t, o)
  63.     local mt = {}
  64.     local p = setmetatable(t, mt)
  65.     mt.__index = function(t,k) return function(_, ...) return o[k](o, ...) end end
  66.     local dataf = refreshers[o:GetObjectType()].data
  67.     local fetch = refreshers[o:GetObjectType()].fetch
  68.     local data = dataf(o)
  69.     p.fetch = function(waiter)
  70.         local o = fetch(waiter, data)
  71.         if o then
  72.             data = dataf(o)
  73.         end
  74.         return o
  75.     end
  76.     p.isproxy = true
  77.     return p
  78. end
  79.  
  80. function wrap(errf, f, waiter, ...)
  81.     local co = coroutine.create(f)
  82.     local t, n = {...}, select("#", waiter, ...)
  83.     local proxies = {}
  84.     for i = 1, n do
  85.         local o = t[i]
  86.         if canproxy(o) then
  87.             local p = proxy({}, o)
  88.             p.i = i
  89.             t[i] = p
  90.         end
  91.         if isproxy(t[i]) then
  92.             proxies[#proxies+1] = t[i]
  93.         end
  94.     end
  95.     local alive, delay
  96.     local refreshAndCall, call
  97.     function call()
  98.         -- run coroutine and get delay
  99.         alive, delay = coroutine.resume(co, waiter, table.unpack(t, 1, n))
  100.         -- coroutine ran to finish? end running
  101.         if coroutine.status(co) == "dead" then return end
  102.         -- wait for delay using waiter
  103.         waiter:RegisterEvent(refreshAndCall, delay, 1)
  104.     end
  105.     function refreshAndCall()
  106.         -- refresh proxies
  107.         local errored = false
  108.         for k,v in ipairs(proxies) do
  109.             local f = v.fetch(waiter)
  110.             if not f then
  111.                 t[v.i] = nil
  112.                 errored = true
  113.             else
  114.                 proxy(v, f)
  115.             end
  116.         end
  117.         if errored then
  118.             if errf then return errf(waiter, table.unpack(t, 1, n)) end
  119.             return
  120.         end
  121.         call()
  122.     end
  123.     call()
  124. end
  125.  
  126. return {
  127.     call = wrap,
  128.     wait = coroutine.yield,
  129.     canproxy = canproxy,
  130.     proxy = proxy,
  131. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement