dobrograd-13-06-2022/garrysmod/addons/feature-wire/lua/wire/timedpairs.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

94 lines
3.2 KiB
Lua

-- Timedpairs by Grocel. (Rewrite by Divran)
-- It allows you to go through long tables, but without game freezing.
-- Its like a for-pairs loop.
--
-- How to use:
-- WireLib.Timedpairs(string unique name, table, number ticks done at once, function tickcallback[, function endcallback, ...])
--
-- tickcallback is called every tick, it ticks for each KeyValue of the table.
-- Its arguments are the current key and value.
-- Return false in the tickcallback function to break the loop.
-- tickcallback(key, value, ...)
--
-- endcallback is called after the last tickcallback has been called.
-- Its arguments are the same as the last arguments of WireLib.Timedpairs
-- endcallback(lastkey, lastvalue, ...)
if (!WireLib) then return end
local next = next
local pairs = pairs
local unpack = unpack
local pcall = pcall
local functions = {}
function WireLib.TimedpairsGetTable()
return functions
end
function WireLib.TimedpairsStop(name)
functions[name] = nil
end
local function copy( t ) -- custom table copy function to convert to numerically indexed table
local ret = {}
for k,v in pairs( t ) do
ret[#ret+1] = { key = k, value = v }
end
return ret
end
local function Timedpairs()
if not next(functions) then return end
local toremove = {}
for name, data in pairs( functions ) do
for i=1,data.step do
data.currentindex = data.currentindex + 1 -- increment index counter
local lookup = data.lookup or {}
if data.currentindex <= #lookup then -- If there are any more values..
local kv = lookup[data.currentindex] or {} -- Get the current key and value
local ok, err = xpcall( data.callback, debug.traceback, kv.key, kv.value, unpack(data.args) ) -- DO EET
if not ok then -- oh noes
WireLib.ErrorNoHalt( "Error in Timedpairs '" .. name .. "': " .. err )
toremove[#toremove+1] = name
break
elseif err == false then -- They returned false inside the function
toremove[#toremove+1] = name
break
end
else -- Out of keys. Entire table looped
if data.endcallback then -- If we had any end callback function
local kv = lookup[data.currentindex-1] or {} -- get previous key & value
local ok, err = xpcall( data.endcallback, debug.traceback, kv.key, kv.value, unpack(data.args) )
if not ok then
WireLib.ErrorNoHalt( "Error in Timedpairs '" .. name .. "' (in end function): " .. err )
end
end
toremove[#toremove+1] = name
break
end
end
end
for i=1,#toremove do -- Remove all that were flagged for removal
functions[toremove[i]] = nil
end
end
if (CLIENT) then
hook.Add("PostRenderVGUI", "WireLib_Timedpairs", Timedpairs) // Doesn't get paused in single player. Can be important for vguis.
else
hook.Add("Think", "WireLib_Timedpairs", Timedpairs) // Servers still uses Think.
end
function WireLib.Timedpairs(name,tab,step,callback,endcallback,...)
functions[name] = { lookup = copy(tab), step = step, currentindex = 0, callback = callback, endcallback = endcallback, args = {...} }
end
function WireLib.Timedcall(callback,...) // calls the given function like simple timer, but isn't affected by game pausing.
local dummytab = {true}
WireLib.Timedpairs("Timedcall_"..tostring(dummytab),dummytab,1,function(k, v, ...) callback(...) end,nil,...)
end