1 QuestHelper_File
["timeslice.lua"] = "Development Version"
2 QuestHelper_Loadtime
["timeslice.lua"] = GetTime()
4 -- Any non-local item here is part of an available public interface.
6 local coroutine_running
= false
7 local coroutine_stop_time
= 0
8 local coroutine_list
= {}
9 local coroutine_route_pass
= 1
11 local coroutine_verbose
= false
13 local coroutine_time_used
= {}
14 local coroutine_power_up
= GetTime()
16 local coroutine_time_exceeded
= 0
18 function QH_Timeslice_DumpPerf()
20 for k
, v
in pairs(coroutine_time_used
) do
21 table.insert(sortable
, {name
= k
, amount
= v
})
23 table.sort(sortable
, function(a
, b
) return a
.name
< b
.name
end)
24 for _
, v
in pairs(sortable
) do
25 QuestHelper
:TextOut(string.format("%s: %f", QuestHelper
:HighlightText(v
.name
), v
.amount
))
27 QuestHelper
:TextOut(string.format("%s: %f", QuestHelper
:HighlightText("poweron"), GetTime() - coroutine_power_up
))
30 function QH_Timeslice_Yield()
31 if coroutine_running
then
32 -- Check if we've run our alotted time
33 if GetTime() > coroutine_stop_time
then
34 -- As a safety, reset stop time to 0. If somehow we fail to set it next time,
35 -- we'll be sure to yield promptly.
36 coroutine_stop_time
= 0
42 function QH_Timeslice_Bonus(quantity
)
43 if coroutine_verbose
then QuestHelper
:TextOut(string.format("timeslice: %d bonus", quantity
)) end
44 coroutine_route_pass
= coroutine_route_pass
+ quantity
55 function QH_Timeslice_Add(workfunc
, name
)
56 local priority
= prioritize
[name
] and prioritize
[name
][1] or 0
57 local sharding
= prioritize
[name
] and prioritize
[name
][2] or 1
58 if coroutine_verbose
then QuestHelper
:TextOut(string.format("timeslice: %s added (%s, %d)", name
, tostring(workfunc
), priority
)) end
59 local ncoro
= coroutine
.create(workfunc
)
60 QuestHelper
: Assert(ncoro
)
61 table.insert(coroutine_list
, {priority
= priority
, sharding
= sharding
, name
= name
, coro
= ncoro
, active
= true})
64 function QH_Timeslice_Toggle(name
, flag
)
65 --if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s toggled to %s", name, tostring(not not flag))) end
66 for _
, v
in pairs(coroutine_list
) do
67 if v
.name
== name
then v
.active
= flag
end
73 function QH_Timeslice_Work()
74 -- There's probably a better way to do this, but. Eh. Lua.
77 for k
, v
in pairs(coroutine_list
) do
79 --if v.sharding then QuestHelper:TextOut(string.format("%d mod %d is %d, %s", time(), v.sharding, bit.mod(time(), v.sharding), tostring(bit.mod(time(), v.sharding) == 0))) end
80 if (not v
.sharding
or bit
.mod(time(), v
.sharding
) == 0) and (not coro
or (v
.priority
> coro
.priority
)) then
88 --if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s running", coro.name)) end
90 if coroutine
.status(coro
.coro
) == "dead" then -- Someone was claiming to get an infinite loop with this. I don't see how it's possible, but this should at least fix the infinite loop.
91 coroutine_list
[key
] = nil
92 QuestHelper
: Assert(coroutine
.status(coro
.coro
) ~= "dead")
95 local slicefactor
= (QuestHelper_Pref
.hide
and 0.01 or (QuestHelper_Pref
.perf_scale
* math
.min(coroutine_route_pass
, 5)))
96 if not started
then slicefactor
= 5 * QuestHelper_Pref
.perfload_scale
* math
.min(coroutine_route_pass
, 5) end -- the init process gets much higher priority so we get done with it faster
97 local coroutine_intended_stop_time
= GetTime() + 4e-3 * slicefactor
98 coroutine_stop_time
= coroutine_intended_stop_time
- coroutine_time_exceeded
99 coroutine_route_pass
= coroutine_route_pass
- 5
100 if coroutine_route_pass
<= 0 then coroutine_route_pass
= 1 end
102 local start
= GetTime()
103 local state
, err
= true, nil -- default values for "we're fine"
104 if start
< coroutine_stop_time
then -- We don't want to just return on failure because we want to credit the exceeded time properly.
105 coroutine_running
= true
106 state
, err
= coroutine
.resume(coro
.coro
)
107 coroutine_running
= false
109 local total
= GetTime() - start
111 local coroutine_this_cycle_exceeded
= GetTime() - coroutine_intended_stop_time
-- may be either positive or negative
112 coroutine_time_exceeded
= coroutine_time_exceeded
+ coroutine_this_cycle_exceeded
114 coroutine_time_used
[coro
.name
] = (coroutine_time_used
[coro
.name
] or 0) + total
117 if coroutine_verbose
then QuestHelper
:TextOut(string.format("timeslice: %s errored", coro
.name
)) end
118 QuestHelper_ErrorCatcher_ExplicitError(err
, "", string.format("(Coroutine error in %s)\n", coro
.name
))
121 QuestHelper
: Assert(coro
.coro
)
122 if coroutine
.status(coro
.coro
) == "dead" then
123 if coroutine_verbose
then QuestHelper
:TextOut(string.format("timeslice: %s complete", coro
.name
)) end
124 coroutine_list
[key
] = nil
127 if coro
.name
== "routing" then started
= true end
129 if coroutine_verbose
then QuestHelper
:TextOut(string.format("timeslice: no available tasks")) end
133 function QH_Timeslice_Increment(quantity
, name
)
134 local an
= "(nc) " .. name
135 coroutine_time_used
[an
] = (coroutine_time_used
[an
] or 0) + quantity