mirror of
https://github.com/araxiaonline/GMGenie.git
synced 2026-06-13 02:32:22 -04:00
861 lines
28 KiB
Lua
861 lines
28 KiB
Lua
--[[
|
|
--
|
|
-- Chronos
|
|
-- Keeper of Time
|
|
--
|
|
-- By AnduinLothar, Alexander Brazie, and Thott
|
|
--
|
|
-- Chronos manages time. You can schedule a function to be called
|
|
-- in X seconds, with or without an id. You can request a timer,
|
|
-- which tracks the elapsed duration since the timer was started.
|
|
--
|
|
-- To use as an embeddable addon:
|
|
-- - Put the Chronos folder inside your Interface/AddOns/<YourAddonName>/ folder.
|
|
-- - Add Chronos\Chronos.xml to your toc or load it in your xml before your localization files.
|
|
-- - Add Chronos to the OptionalDeps in your toc
|
|
--
|
|
-- To use as an addon library:
|
|
-- - Put the Chronos folder inside your Interface/AddOns/ folder.
|
|
-- - Add Chronos to the Dependencies in your toc
|
|
--
|
|
-- Please see below or see http://www.wowwiki.com/Chronos_(addon) for details.
|
|
--
|
|
-- $LastChangedBy: karlkfi $
|
|
-- $Date: 2006-12-21 06:19:14 -0600 (Thu, 21 Dec 2006) $
|
|
-- $Rev: 4467 $
|
|
--
|
|
--]]
|
|
|
|
local CHRONOS_REV = 2.12;
|
|
|
|
local isBetterInstanceLoaded = (Chronos and Chronos.version and Chronos.version >= CHRONOS_REV);
|
|
|
|
if (not isBetterInstanceLoaded) then
|
|
|
|
if (not Chronos) then
|
|
Chronos = {};
|
|
end
|
|
|
|
Chronos.version = CHRONOS_REV;
|
|
|
|
------------------------------------------------------------------------------
|
|
--[[ Variables ]] --
|
|
------------------------------------------------------------------------------
|
|
|
|
Chronos.online = true;
|
|
|
|
CHRONOS_DEBUG = false;
|
|
CHRONOS_DEBUG_WARNINGS = false;
|
|
|
|
-- Chronos Data
|
|
if (not ChronosData) then
|
|
ChronosData = {};
|
|
end
|
|
|
|
-- Chronos Recycled Tables Storage
|
|
if (not Chronos.tables) then
|
|
Chronos.tables = {};
|
|
end
|
|
|
|
-- Initialize the Timers
|
|
if (not ChronosData.timers) then
|
|
ChronosData.timers = {};
|
|
end
|
|
|
|
-- Initialize the perform-over-time task list
|
|
if (not ChronosData.tasks) then
|
|
ChronosData.tasks = {};
|
|
end
|
|
|
|
-- Maximum items per frame
|
|
Chronos.MAX_TASKS_PER_FRAME = 100;
|
|
|
|
-- Maximum steps per task
|
|
Chronos.MAX_STEPS_PER_TASK = 300;
|
|
|
|
-- Maximum time delay per frame
|
|
Chronos.MAX_TIME_PER_STEP = .3;
|
|
|
|
Chronos.emptyTable = {};
|
|
|
|
------------------------------------------------------------------------------
|
|
--[[ User Functions ]] --
|
|
------------------------------------------------------------------------------
|
|
|
|
--[[
|
|
-- debug(boolean)
|
|
--
|
|
-- Toggles debug mode
|
|
]] --
|
|
function Chronos.debug(enable)
|
|
if (enable) then
|
|
ChronosFrame:SetScript("OnUpdate", Chronos.OnUpdate_Debug);
|
|
CHRONOS_DEBUG = true;
|
|
CHRONOS_DEBUG_WARNINGS = true;
|
|
else
|
|
ChronosFrame:SetScript("OnUpdate", Chronos.OnUpdate_Quick);
|
|
CHRONOS_DEBUG = false;
|
|
CHRONOS_DEBUG_WARNINGS = false;
|
|
end
|
|
end
|
|
|
|
--[[
|
|
-- Scheduling functions
|
|
-- Parts rewritten by AnduinLothar for efficiency
|
|
-- Parts rewritten by Thott for speed
|
|
-- Written by Alexander
|
|
-- Original by Thott
|
|
--
|
|
-- Usage: Chronos.schedule(when,handler,arg1,arg2,etc)
|
|
--
|
|
-- After <when> seconds pass (values less than one and fractional values are
|
|
-- fine), handler is called with the specified arguments, i.e.:
|
|
-- handler(arg1,arg2,etc)
|
|
--
|
|
-- If you'd like to have something done every X seconds, reschedule
|
|
-- it each time in the handler or preferably use scheduleRepeating.
|
|
--
|
|
-- Also, please note that there is a limit to the number of
|
|
-- scheduled tasks that can be performed per xml object at the
|
|
-- same time.
|
|
--]]
|
|
function Chronos.schedule(when, handler, ...)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
if (not handler) then
|
|
Chronos.printError("ERROR: nil handler passed to Chronos.schedule()");
|
|
return;
|
|
end
|
|
|
|
--local memstart = collectgarbage("count");
|
|
-- -- Assign an id
|
|
-- local id = "";
|
|
-- if ( not this ) then
|
|
-- id = "Keybinding";
|
|
-- else
|
|
-- id = self:GetName();
|
|
-- end
|
|
-- if ( not id ) then
|
|
-- id = "_DEFAULT";
|
|
-- end
|
|
-- if ( not when ) then
|
|
-- Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: ", id , " has sent no interval for this function. ", when );
|
|
-- return;
|
|
-- end
|
|
|
|
-- -- Ensure we're not looping ChronosFrame
|
|
-- if ( id == "ChronosFrame" and ChronosData.lastID ) then
|
|
-- id = ChronosData.lastID;
|
|
-- end
|
|
|
|
-- use recycled tables to avoid excessive garbage collection -AnduinLothar
|
|
--tinsert(ChronosData.sched, Chronos.getTable())
|
|
--local i = #ChronosData.sched
|
|
local recTable = Chronos.getTable()
|
|
-- ChronosData.sched[i].id = id;
|
|
recTable.time = when + GetTime();
|
|
recTable.handler = handler;
|
|
recTable.args = Chronos.getArgTable(...);
|
|
|
|
-- task list is a heap, add new
|
|
local i = #ChronosData.sched + 1
|
|
while (i > 1) do
|
|
if (recTable.time < ChronosData.sched[i - 1].time) then
|
|
i = i - 1;
|
|
else
|
|
break
|
|
end
|
|
end
|
|
tinsert(ChronosData.sched, i, recTable)
|
|
|
|
-- Debug print
|
|
--Chronos.printDebugError("CHRONOS_DEBUG", "Scheduled "..handler.." in "..when.." seconds from "..id );
|
|
--Chronos.printError("Memory change in schedule: "..memstart.."->"..memend.." = "..memend-memstart);
|
|
end
|
|
|
|
|
|
--[[
|
|
-- Chronos.scheduleByName(name, delay, function, arg1, ... );
|
|
--
|
|
-- Same as Chronos.schedule, except it takes a schedule name argument.
|
|
-- Only one event can be scheduled with a given name at any one time.
|
|
-- Thus if one exists, and another one is scheduled, the first one
|
|
-- is deleted, then the second one added.
|
|
--
|
|
--]]
|
|
function Chronos.scheduleByName(name, when, handler, ...)
|
|
if (not name) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No name specified to Chronos.scheduleByName");
|
|
return;
|
|
end
|
|
local namedSchedule = ChronosData.byName[name];
|
|
if (namedSchedule and handler) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: scheduleByName is reasigning \"" .. name .. "\".");
|
|
Chronos.releaseTable(ChronosData.byName[name]);
|
|
else
|
|
if (not handler) then
|
|
if (not namedSchedule) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No handler specified to Chronos.scheduleByName, no previous entry found for scheduled entry \"" .. name .. "\".");
|
|
return;
|
|
end
|
|
if (not namedSchedule.handler) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No handler specified to Chronos.scheduleByName, no handler could be found in previous entry of \"" .. name .. "\" either.");
|
|
return;
|
|
end
|
|
handler = namedSchedule.handler;
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: scheduleByName is updating \"" .. name .. "\" to time: " .. when);
|
|
else
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: scheduleByName is asigning \"" .. name .. "\".");
|
|
end
|
|
end
|
|
ChronosData.byName[name] = Chronos.getTable();
|
|
namedSchedule = ChronosData.byName[name];
|
|
namedSchedule.time = when + GetTime()
|
|
namedSchedule.handler = handler;
|
|
namedSchedule.args = Chronos.getArgTable(...);
|
|
end
|
|
|
|
--[[
|
|
-- unscheduleByName(name);
|
|
--
|
|
-- Removes an entry that was created with scheduleByName()
|
|
--
|
|
-- Args:
|
|
-- name - the name used
|
|
--
|
|
--]]
|
|
function Chronos.unscheduleByName(name)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
if (not name) then
|
|
Chronos.printError("No name specified to Chronos.unscheduleByName");
|
|
return;
|
|
end
|
|
if (ChronosData.byName[name]) then
|
|
Chronos.releaseTable(ChronosData.byName[name]);
|
|
ChronosData.byName[name] = nil;
|
|
end
|
|
|
|
-- Debug print
|
|
--Chronos.printDebugError("CHRONOS_DEBUG", "Cancelled scheduled timer of name ",name);
|
|
end
|
|
|
|
--[[
|
|
-- unscheduleRepeating(name);
|
|
-- Mirrors unscheduleByName for backwards compatibility
|
|
--]]
|
|
Chronos.unscheduleRepeating = Chronos.unscheduleByName;
|
|
|
|
--[[
|
|
-- isScheduledByName(name)
|
|
-- Returns the amount of time left if it is indeed scheduled by name!
|
|
--
|
|
-- returns:
|
|
-- number - time remaining
|
|
-- nil - not scheduled
|
|
--
|
|
--]]
|
|
function Chronos.isScheduledByName(name)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
if (not name) then
|
|
Chronos.printError("No name specified to Chronos.isScheduledByName " .. (self:GetName() or "unknown"));
|
|
return;
|
|
end
|
|
local namedSchedule = ChronosData.byName[name];
|
|
if (namedSchedule) then
|
|
return namedSchedule.time - GetTime();
|
|
end
|
|
|
|
-- Debug print
|
|
--Chronos.printDebugError("CHRONOS_DEBUG", "Did not find timer of name ",name);
|
|
return nil;
|
|
end
|
|
|
|
--[[
|
|
-- isScheduledRepeating(name)
|
|
-- Mirrors isScheduledByName for backwards compatibility
|
|
--]]
|
|
Chronos.isScheduledRepeating = Chronos.isScheduledByName;
|
|
|
|
--[[
|
|
-- Chronos.scheduleRepeating(name, delay, function);
|
|
--
|
|
-- Same as Chronos.scheduleByName, except it repeats without recalling and takes no arguments.
|
|
--
|
|
--]]
|
|
function Chronos.scheduleRepeating(name, when, handler)
|
|
if (not name) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No name specified to Chronos.scheduleRepeating");
|
|
return;
|
|
end
|
|
local namedSchedule = ChronosData.byName[name];
|
|
if (namedSchedule and handler) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: scheduleRepeating is reasigning " .. name);
|
|
Chronos.releaseTable(ChronosData.byName[name]);
|
|
else
|
|
if (not handler) then
|
|
if (not namedSchedule) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No handler specified to Chronos.scheduleRepeating, no previous entry found for scheduled entry '" .. name .. "'.");
|
|
return;
|
|
end
|
|
if (not namedSchedule.handler) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No handler specified to Chronos.scheduleRepeating, no handler could be found in previous entry '" .. name .. "' either.");
|
|
return;
|
|
end
|
|
handler = namedSchedule.handler;
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: scheduleRepeating is updating '" .. name .. "' to time: " .. when);
|
|
else
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: scheduleRepeating is asigning '" .. name .. "'.");
|
|
end
|
|
end
|
|
ChronosData.byName[name] = Chronos.getTable();
|
|
namedSchedule = ChronosData.byName[name];
|
|
namedSchedule.time = when + GetTime();
|
|
namedSchedule.period = when;
|
|
namedSchedule.handler = handler;
|
|
namedSchedule.repeating = true;
|
|
end
|
|
|
|
--[[
|
|
-- Chronos.flushByName(name, when);
|
|
--
|
|
-- Updates the ByName or Repeating event to flush at the time specified. If no time is specified flush will be immediate. If it is a Repeating event the timer will be reset.
|
|
--
|
|
--]]
|
|
function Chronos.flushByName(name, when)
|
|
if (not name) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: No name specified to Chronos.flushByName");
|
|
return;
|
|
elseif (not ChronosData.byName[name]) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos Error Detection: no previous entry found for Chronos.flushByName entry '" .. name .. "'.");
|
|
return;
|
|
end
|
|
if (not when) then
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: flushing '" .. name .. "'.");
|
|
when = GetTime();
|
|
else
|
|
Chronos.printDebugError("CHRONOS_DEBUG_WARNINGS", "Chronos: flushing '" .. name .. "' in " .. when .. " seconds.");
|
|
when = when + GetTime();
|
|
end
|
|
ChronosData.byName[name].time = when;
|
|
end
|
|
|
|
--[[
|
|
-- Chronos.startTimer([ID]);
|
|
-- Starts a timer on a particular
|
|
--
|
|
-- Args
|
|
-- ID - optional parameter to identify who is asking for a timer.
|
|
--
|
|
-- If ID does not exist, self:GetName() is used.
|
|
--
|
|
-- When you want to get the amount of time passed since startTimer(ID) is called,
|
|
-- call getTimer(ID) and it will return the number in seconds.
|
|
--
|
|
--]]
|
|
function Chronos.startTimer(id)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
|
|
if (not id) then
|
|
id = self:GetName();
|
|
end
|
|
|
|
-- Create a table for this id's timers
|
|
if (not ChronosData.timers[id]) then
|
|
ChronosData.timers[id] = Chronos.getTable();
|
|
end
|
|
|
|
-- Clear out an entry if the table is too big.
|
|
if (#ChronosData.timers[id] > Chronos.MAX_TASKS_PER_FRAME) then
|
|
Chronos.printError("Too many Chronos timers created for id " .. tostring(id));
|
|
return;
|
|
end
|
|
|
|
-- Add a new timer entry
|
|
table.insert(ChronosData.timers[id], GetTime());
|
|
end
|
|
|
|
|
|
--[[
|
|
-- endTimer([id]);
|
|
--
|
|
-- Ends the timer and returns the amount of time passed.
|
|
--
|
|
-- args:
|
|
-- id - ID for the timer. If not specified, then ID will
|
|
-- be self:GetName()
|
|
--
|
|
-- returns:
|
|
-- (Number delta, Number start, Number end)
|
|
--
|
|
-- delta - the amount of time passed in seconds.
|
|
-- start - the starting time
|
|
-- now - the time the endTimer was called.
|
|
--]]
|
|
|
|
function Chronos.endTimer(id)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
|
|
if (not id) then
|
|
id = self:GetName();
|
|
end
|
|
|
|
if (not ChronosData.timers[id] or #ChronosData.timers[id] == 0) then
|
|
return nil;
|
|
end
|
|
|
|
local now = GetTime();
|
|
|
|
-- Grab the last timer called
|
|
local startTime = tremove(ChronosData.timers[id]);
|
|
|
|
return (now - startTime), startTime, now;
|
|
end
|
|
|
|
|
|
--[[
|
|
-- getTimer([id]);
|
|
--
|
|
-- Gets the timer and returns the amount of time passed.
|
|
-- Does not terminate the timer.
|
|
--
|
|
-- args:
|
|
-- id - ID for the timer. If not specified, then ID will
|
|
-- be self:GetName()
|
|
--
|
|
-- returns:
|
|
-- (Number delta, Number start, Number end)
|
|
--
|
|
-- delta - the amount of time passed in seconds.
|
|
-- start - the starting time
|
|
-- now - the time the endTimer was called.
|
|
--]]
|
|
|
|
function Chronos.getTimer(id)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
|
|
if (not id) then
|
|
id = self:GetName();
|
|
end
|
|
|
|
local now = GetTime();
|
|
if (not ChronosData.timers[id] or #ChronosData.timers[id] == 0) then
|
|
return 0, 0, now;
|
|
end
|
|
|
|
-- Grab the last timer called
|
|
local startTime = ChronosData.timers[id][#ChronosData.timers[id]];
|
|
|
|
return (now - startTime), startTime, now;
|
|
end
|
|
|
|
--[[
|
|
-- isTimerActive([id])
|
|
-- returns true if the timer exists.
|
|
--
|
|
-- args:
|
|
-- id - ID for the timer. If not specified, then ID will
|
|
-- be self:GetName()
|
|
--
|
|
-- returns:
|
|
-- true - exists
|
|
-- false - does not
|
|
--]]
|
|
function Chronos.isTimerActive(id)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
|
|
if (not id) then
|
|
id = self:GetName();
|
|
end
|
|
|
|
-- Create a table for this id's timers
|
|
if (not ChronosData.timers[id]) then
|
|
return false;
|
|
end
|
|
|
|
return true;
|
|
end
|
|
|
|
--[[
|
|
-- getTime()
|
|
--
|
|
-- returns the Chronos internal elapsed time.
|
|
--
|
|
-- returns:
|
|
-- (elapsedTime)
|
|
--
|
|
-- elapsedTime - time in seconds since Chronos initialized
|
|
--]]
|
|
function Chronos.getTime()
|
|
return ChronosData.elapsedTime;
|
|
end
|
|
|
|
--[[
|
|
-- Chronos.afterInit(func, ...)
|
|
-- Performs func after the game has truely started.
|
|
-- By Thott
|
|
--]]
|
|
function Chronos.afterInit(func, ...)
|
|
local id;
|
|
if (this) then
|
|
id = self:GetName();
|
|
else
|
|
id = "unknown";
|
|
end
|
|
--if(id == "SkyFrame") then
|
|
-- Chronos.printError("Ignoring Sky init");
|
|
-- return;
|
|
--end
|
|
if (ChronosData.initialized) then
|
|
func(...);
|
|
else
|
|
if (not ChronosData.afterInit) then
|
|
ChronosData.afterInit = Chronos.getTable();
|
|
Chronos.schedule(0.2, Chronos.initCheck);
|
|
end
|
|
local recTable = Chronos.getTable();
|
|
recTable.func = func;
|
|
recTable.args = Chronos.getArgTable(...);
|
|
recTable.id = id;
|
|
tinsert(ChronosData.afterInit, recTable);
|
|
end
|
|
end
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
--[[ Table Recycling ]] --
|
|
------------------------------------------------------------------------------
|
|
function Chronos.getTable(...)
|
|
local stack = Chronos.tables;
|
|
if (not stack) then
|
|
Chronos.tables = {};
|
|
stack = Chronos.tables;
|
|
return {};
|
|
end
|
|
local recTable;
|
|
if (#stack >= 1) then
|
|
recTable = tremove(stack)
|
|
else
|
|
recTable = {};
|
|
end
|
|
for i = 1, select("#", ...) do
|
|
recTable[i] = select(i, ...);
|
|
end
|
|
return recTable;
|
|
end
|
|
|
|
-- Release a table to be nilled and used again.
|
|
-- Optionally pass in an unpack(...) as the 2nd arg so that you can return the args:
|
|
-- return Chronos.releaseTable(t1, unpack(t1))
|
|
function Chronos.releaseTable(t1, ...)
|
|
if (type(t1) ~= "table") then
|
|
return;
|
|
end
|
|
|
|
local stack = Chronos.tables;
|
|
if (not stack) then
|
|
Chronos.tables = {};
|
|
stack = Chronos.tables;
|
|
end
|
|
|
|
for k, v in pairs(t1) do
|
|
t1[k] = nil;
|
|
end
|
|
|
|
tinsert(stack, t1);
|
|
return ...;
|
|
end
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
--[[ Helpers Functions ]] --
|
|
------------------------------------------------------------------------------
|
|
function Chronos.getArgTable(...)
|
|
if (select('#', ...) == 0) then
|
|
return Chronos.emptyTable;
|
|
else
|
|
return Chronos.getTable(...);
|
|
end
|
|
end
|
|
|
|
function Chronos.run(func, args)
|
|
if (func) then
|
|
if (args) then
|
|
return func(unpack(args));
|
|
else
|
|
return func();
|
|
end
|
|
end
|
|
end
|
|
|
|
function Chronos.printError(text)
|
|
ChatFrame1:AddMessage(text, RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, 1.0, UIERRORS_HOLD_TIME);
|
|
end
|
|
|
|
function Chronos.printDebugError(var, text)
|
|
if (var) and (getglobal(var)) then
|
|
Chronos.printError(text);
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------------
|
|
--[[ Frame Script Helpers ]] --
|
|
------------------------------------------------------------------------------
|
|
function Chronos.chatColorsInit()
|
|
ChronosData.chatColorsInitialized = true;
|
|
ChronosFrame:UnregisterEvent("UPDATE_CHAT_COLOR");
|
|
end
|
|
|
|
function Chronos.initCheck()
|
|
if (not ChronosData.initialized) then
|
|
if (UnitName("player") and UnitName("player") ~= UKNOWNBEING and UnitName("player") ~= UNKNOWNBEING and UnitName("player") ~= UNKNOWNOBJECT and ChronosData.variablesLoaded and ChronosData.enteredWorld and ChronosData.chatColorsInitialized) then
|
|
ChronosData.initialized = true;
|
|
Chronos.schedule(1, Chronos.initCheck);
|
|
return;
|
|
else
|
|
Chronos.schedule(0.2, Chronos.initCheck);
|
|
return;
|
|
end
|
|
end
|
|
if (ChronosData.afterInit) then
|
|
local i = ChronosData.afterInit_i;
|
|
if (not i) then
|
|
i = 1;
|
|
end
|
|
ChronosData.afterInit_i = i + 1;
|
|
--Chronos.printError("afterInit: processing ",i," of ",ChronosData.afterInit.n," initialization functions, id: ",ChronosData.afterInit[i].id);
|
|
Chronos.run(ChronosData.afterInit[i].func, ChronosData.afterInit[i].args);
|
|
if (i == #ChronosData.afterInit) then
|
|
for i, v in ipairs(ChronosData.afterInit) do
|
|
Chronos.releaseTable(v);
|
|
end
|
|
Chronos.releaseTable(ChronosData.afterInit);
|
|
ChronosData.afterInit = nil;
|
|
ChronosData.afterInit_i = nil;
|
|
else
|
|
Chronos.schedule(0.1, Chronos.initCheck);
|
|
return;
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
-- Sends a chat command through the standard editbox
|
|
--]]
|
|
function Chronos.SendChatCommand(command)
|
|
local text = ChatFrameEditBox:GetText();
|
|
ChatFrameEditBox:SetText(command);
|
|
ChatEdit_SendText(ChatFrameEditBox);
|
|
ChatFrameEditBox:SetText(text);
|
|
end
|
|
|
|
function Chronos.RegisterSlashCommands()
|
|
--Needs to be able Variables load if you want to use Sky
|
|
local chronosFunc = function(msg)
|
|
local _, _, seconds, command = string.find(msg, "([%d\.]+)%s+(.*)");
|
|
if (seconds and command) then
|
|
Chronos.schedule(seconds, Chronos.SendChatCommand, command);
|
|
else
|
|
Chronos.printError(SCHEDULE_USAGE1);
|
|
Chronos.printError(SCHEDULE_USAGE2);
|
|
end
|
|
end
|
|
if (Satellite) then
|
|
Satellite.registerSlashCommand({
|
|
id = "Schedule";
|
|
commands = SCHEDULE_COMM;
|
|
onExecute = chronosFunc;
|
|
helpText = SCHEDULE_DESC;
|
|
replace = true;
|
|
});
|
|
else
|
|
SlashCmdList["CHRONOS_SCHEDULE"] = chronosFunc;
|
|
for i = 1, #SCHEDULE_COMM do setglobal("SLASH_CHRONOS_SCHEDULE" .. i, SCHEDULE_COMM[i]); end
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------------
|
|
--[[ Frame Scripts ]] --
|
|
------------------------------------------------------------------------------
|
|
function Chronos.OnLoad()
|
|
Chronos.framecount = 0;
|
|
|
|
if (not ChronosData.byName) then
|
|
ChronosData.byName = {};
|
|
end
|
|
if (not ChronosData.repeating) then
|
|
ChronosData.repeating = {};
|
|
end
|
|
if (not ChronosData.sched) then
|
|
ChronosData.sched = {};
|
|
end
|
|
ChronosData.elapsedTime = 0;
|
|
|
|
Chronos.afterInit(Chronos.RegisterSlashCommands);
|
|
end
|
|
|
|
function Chronos.OnEvent(self, event, ...)
|
|
if (event == "ADDON_LOADED") then
|
|
ChronosData.variablesLoaded = true;
|
|
ChronosFrame:Show();
|
|
elseif (event == "PLAYER_ENTERING_WORLD") then
|
|
ChronosData.enteredWorld = true;
|
|
Chronos.online = true;
|
|
elseif (event == "PLAYER_LEAVING_WORLD") then
|
|
Chronos.online = false;
|
|
elseif (event == "UPDATE_CHAT_COLOR") then
|
|
Chronos.scheduleByName("ChronosAfterChatColorInit", 1, Chronos.chatColorsInit);
|
|
end
|
|
end
|
|
|
|
function Chronos.OnUpdate_Quick(self, arg1)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
if (not ChronosData.variablesLoaded) then
|
|
return;
|
|
end
|
|
|
|
if (ChronosData.elapsedTime) then
|
|
ChronosData.elapsedTime = ChronosData.elapsedTime + arg1;
|
|
else
|
|
ChronosData.elapsedTime = arg1;
|
|
end
|
|
|
|
-- Execute scheduled tasks that are ready, pulling them off the front of the list queue.
|
|
local now = GetTime();
|
|
local i;
|
|
local task;
|
|
while (#ChronosData.sched > 0) do
|
|
if (not ChronosData.sched[1].time) then
|
|
--Sea.io.printTable(ChronosData.sched[1]);
|
|
tremove(ChronosData.sched, 1);
|
|
elseif (ChronosData.sched[1].time <= now) then
|
|
task = tremove(ChronosData.sched, 1);
|
|
Chronos.run(task.handler, task.args);
|
|
Chronos.releaseTable(task);
|
|
else
|
|
break;
|
|
end
|
|
end
|
|
|
|
-- Execute named scheduled tasks that are ready.
|
|
local k, v = next(ChronosData.byName);
|
|
local newK, newV;
|
|
while (k ~= nil) do
|
|
newK, newV = next(ChronosData.byName, k);
|
|
if (not v.time) then
|
|
--Sea.io.printTable(v);
|
|
ChronosData.byName[k] = nil;
|
|
elseif (v.time <= now) then
|
|
if (v.repeating) then
|
|
ChronosData.byName[k].time = now + v.period;
|
|
v.handler();
|
|
else
|
|
Chronos.run(v.handler, v.args);
|
|
Chronos.releaseTable(ChronosData.byName[k]);
|
|
ChronosData.byName[k] = nil;
|
|
end
|
|
end
|
|
k, v = newK, newV;
|
|
end
|
|
end
|
|
|
|
function Chronos.OnUpdate_Debug(self, arg1)
|
|
if (not Chronos.online) then
|
|
return;
|
|
end
|
|
if (not ChronosData.variablesLoaded) then
|
|
return;
|
|
end
|
|
local memstart = collectgarbage("count");
|
|
|
|
if (ChronosData.elapsedTime) then
|
|
ChronosData.elapsedTime = ChronosData.elapsedTime + arg1;
|
|
else
|
|
ChronosData.elapsedTime = arg1;
|
|
end
|
|
|
|
local now = GetTime();
|
|
local i;
|
|
local task;
|
|
-- Execute scheduled tasks that are ready, popping them off the heap.
|
|
while (#ChronosData.sched > 0) do
|
|
if (ChronosData.sched[1].time <= now) then
|
|
task = tremove(ChronosData.sched, 1);
|
|
Chronos.run(task.handler, task.args);
|
|
Chronos.releaseTable(task);
|
|
else
|
|
break;
|
|
end
|
|
end
|
|
|
|
local memend = collectgarbage("count");
|
|
if (memend - memstart > 0) then
|
|
Chronos.printError("gcmemleak from ChronosData.sched in OnUpdate: " .. (memend - memstart));
|
|
end
|
|
|
|
-- Execute named scheduled tasks that are ready.
|
|
memstart = memend;
|
|
local k, v = next(ChronosData.byName);
|
|
local newK, newV;
|
|
while (k ~= nil) do
|
|
newK, newV = next(ChronosData.byName, k);
|
|
if (v.time <= now) then
|
|
local m = collectgarbage("count");
|
|
if (v.repeating) then
|
|
ChronosData.byName[k].time = now + v.period;
|
|
v.handler();
|
|
else
|
|
Chronos.run(v.handler, v.args);
|
|
Chronos.releaseTable(ChronosData.byName[k]);
|
|
ChronosData.byName[k] = nil;
|
|
end
|
|
local mm = collectgarbage("count");
|
|
memstart = memstart + mm - m;
|
|
end
|
|
k, v = newK, newV;
|
|
end
|
|
|
|
memend = collectgarbage("count");
|
|
if (memend - memstart > 0) then
|
|
Chronos.printError("gcmemleak from ChronosData.byName in OnUpdate: " .. (memend - memstart));
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------------------------
|
|
--[[ Frame Script Assignment ]] --
|
|
------------------------------------------------------------------------------
|
|
|
|
Chronos.OnUpdate_Quick();
|
|
|
|
--Event Driver
|
|
if (not ChronosFrame) then
|
|
CreateFrame("Frame", "ChronosFrame");
|
|
end
|
|
ChronosFrame:Hide();
|
|
--Event Registration
|
|
ChronosFrame:RegisterEvent("ADDON_LOADED");
|
|
ChronosFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
|
|
ChronosFrame:RegisterEvent("PLAYER_LEAVING_WORLD");
|
|
ChronosFrame:RegisterEvent("UPDATE_CHAT_COLOR");
|
|
--Frame Scripts
|
|
ChronosFrame:SetScript("OnEvent", Chronos.OnEvent);
|
|
ChronosFrame:SetScript("OnUpdate", Chronos.OnUpdate_Quick);
|
|
--OnLoad Call
|
|
Chronos.OnLoad();
|
|
end
|
|
|