WoWInterface SVN Chronos

[/] [trunk/] [Chronos/] [Chronos.lua] - Rev 2

Compare with Previous | Blame | View Log

--[[
--
--      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 = this: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 "..(this: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, this: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 = this: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 this: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 = this: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 this: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 = this: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 this:GetName()
        --
        --      returns:
        --              true - exists
        --              false - does not
        --]]
        function Chronos.isTimerActive( id ) 
                if ( not Chronos.online ) then 
                        return;
                end

                if ( not id ) then 
                        id = this: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 = this: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()
                if(event == "VARIABLES_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()
                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()
                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 ]]--
        ------------------------------------------------------------------------------
        
        --Event Driver
        if (not ChronosFrame) then
                CreateFrame("Frame", "ChronosFrame");
        end
        ChronosFrame:Hide();
        --Event Registration
        ChronosFrame:RegisterEvent("VARIABLES_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

Compare with Previous | Blame