MUST HAVE ALL THE DATA
[QuestHelper.git] / timeslice.lua
blob22750e323f2bcf9e08a68299b3e379d16777e326
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()
19 local sortable = {}
20 for k, v in pairs(coroutine_time_used) do
21 table.insert(sortable, {name = k, amount = v})
22 end
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))
26 end
27 QuestHelper:TextOut(string.format("%s: %f", QuestHelper:HighlightText("poweron"), GetTime() - coroutine_power_up))
28 end
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
37 coroutine.yield()
38 end
39 end
40 end
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
45 end
47 local prioritize = {
48 init = 100,
49 criteria = 10,
50 lzw = -5,
51 routing = -10,
54 function QH_Timeslice_Add(workfunc, name)
55 local priority = prioritize[name] or 0
56 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s added (%s, %d)", name, tostring(workfunc), priority)) end
57 local ncoro = coroutine.create(workfunc)
58 QuestHelper: Assert(ncoro)
59 table.insert(coroutine_list, {priority = priority, name = name, coro = ncoro, active = true})
60 end
62 function QH_Timeslice_Toggle(name, flag)
63 --if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s toggled to %s", name, tostring(not not flag))) end
64 for _, v in pairs(coroutine_list) do
65 if v.name == name then v.active = flag end
66 end
67 end
69 local started = false
71 function QH_Timeslice_Work()
72 -- There's probably a better way to do this, but. Eh. Lua.
73 coro = nil
74 key = nil
75 for k, v in pairs(coroutine_list) do
76 if v.active and (not coro or v.priority > coro.priority) then coro = v; key = k end
77 end
79 if coro then
80 --if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s running", coro.name)) end
82 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.
83 coroutine_list[key] = nil
84 QuestHelper: Assert(coroutine.status(coro.coro) ~= "dead")
85 end
87 local slicefactor = (QuestHelper_Pref.hide and 0.01 or (QuestHelper_Pref.perf_scale * math.min(coroutine_route_pass, 5)))
88 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
89 local coroutine_intended_stop_time = GetTime() + 4e-3 * slicefactor
90 coroutine_stop_time = coroutine_intended_stop_time - coroutine_time_exceeded
91 coroutine_route_pass = coroutine_route_pass - 5
92 if coroutine_route_pass <= 0 then coroutine_route_pass = 1 end
94 local start = GetTime()
95 local state, err = true, nil -- default values for "we're fine"
96 if start < coroutine_stop_time then -- We don't want to just return on failure because we want to credit the exceeded time properly.
97 coroutine_running = true
98 state, err = coroutine.resume(coro.coro)
99 coroutine_running = false
101 local total = GetTime() - start
103 local coroutine_this_cycle_exceeded = GetTime() - coroutine_intended_stop_time -- may be either positive or negative
104 coroutine_time_exceeded = coroutine_time_exceeded + coroutine_this_cycle_exceeded
106 coroutine_time_used[coro.name] = (coroutine_time_used[coro.name] or 0) + total
108 if not state then
109 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s errored", coro.name)) end
110 QuestHelper_ErrorCatcher_ExplicitError(err, "", string.format("(Coroutine error in %s)\n", coro.name))
113 QuestHelper: Assert(coro.coro)
114 if coroutine.status(coro.coro) == "dead" then
115 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: %s complete", coro.name)) end
116 coroutine_list[key] = nil
119 if coro.name == "routing" then started = true end
120 else
121 if coroutine_verbose then QuestHelper:TextOut(string.format("timeslice: no available tasks")) end
125 function QH_Timeslice_Increment(quantity, name)
126 local an = "(nc) " .. name
127 coroutine_time_used[an] = (coroutine_time_used[an] or 0) + quantity