vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 --
3 -- Chronos
4 -- Keeper of Time
5 --
6 -- By Alexander Brazie, Thott and AnduinLothar
7 --
8 -- Chronos manages time. You can schedule a function to be called
9 -- in X seconds, with or without an id. You can request a timer,
10 -- which tracks the elapsed duration since the timer was started.
11 --
12 -- You can also create Tasks - functions which will perform a
13 -- complex operation over time, reducing system lag if used properly.
14 --
15 -- Please see below or see http://www.wowwiki.com/Chronos for details.
16 --
17 -- $LastChangedBy: karlkfi $
18 -- $Date: 2005-11-14 09:01:30 -0800 (Mon, 14 Nov 2005) $
19 -- $Rev: 2789 $
20 --
21 --]]
22  
23 CHRONOS_REV = "$Rev: 2789 $";
24 CHRONOS_DEBUG = false;
25 CH_DEBUG = "CHRONOS_DEBUG";
26 CHRONOS_DEBUG_WARNINGS = false;
27 CH_DEBUG_T = "CHRONOS_DEBUG_WARNINGS";
28  
29 -- Chronos Data
30 ChronosData = {
31 -- Initialize the startup time
32 elaspedTime = 0;
33  
34 -- Initialize the VariablesLoaded flag
35 variablesLoaded = false;
36  
37 -- Initialize the EnteredWorld flag
38 enteredWorld = false;
39  
40 -- Last ID
41 lastID = nil;
42  
43 -- Initialize the Timers
44 timers = {};
45  
46 -- Initialize the perform-over-time task list
47 tasks = {};
48 };
49 -- Prototype Chronos
50 Chronos = {
51 -- Online or off
52 online = true;
53  
54 -- Maximum items per frame
55 MAX_TASKS_PER_FRAME = 100;
56  
57 -- Maximum steps per task
58 MAX_STEPS_PER_TASK = 300;
59  
60 -- Maximum time delay per frame
61 MAX_TIME_PER_STEP = .3;
62  
63 emptyTable = {};
64  
65 getArgTable = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
66 if (( a1 == nil ) and ( a2 == nil ) and ( a3 == nil ) and ( a4 == nil ) and ( a5 == nil ) and ( a6 == nil ) and ( a7 == nil ) and ( a8 == nil ) and ( a9 == nil ) and ( a10 == nil ) and ( a11 == nil ) and ( a12 == nil ) and ( a13 == nil ) and ( a14 == nil ) and ( a15 == nil ) and ( a16 == nil ) and ( a17 == nil ) and ( a18 == nil ) and ( a19 == nil ) and ( a20 == nil )) then
67 return Chronos.emptyTable;
68 else
69 local args = {};
70 table.setn(args, 0);
71 table.insert(args, a1);
72 table.insert(args, a2);
73 table.insert(args, a3);
74 table.insert(args, a4);
75 table.insert(args, a5);
76 table.insert(args, a6);
77 table.insert(args, a7);
78 table.insert(args, a8);
79 table.insert(args, a9);
80 table.insert(args, a10);
81 table.insert(args, a11);
82 table.insert(args, a12);
83 table.insert(args, a13);
84 table.insert(args, a14);
85 table.insert(args, a15);
86 table.insert(args, a16);
87 table.insert(args, a17);
88 table.insert(args, a18);
89 table.insert(args, a19);
90 table.insert(args, a20);
91 return args;
92 end
93 end;
94  
95 --
96 -- pop ( table )
97 --
98 -- Removes a value and returns it from the table
99 -- Arg:
100 -- table - the table
101 --
102 -- duplicated from Sea
103 pop = function (table1)
104 if(not table1) then
105 Chronos.printDebugError(nil, "Bad table passed to pop");
106 return nil;
107 end
108 local n = table.getn(table1);
109 if(not n) then
110 Chronos.printDebugError(nil, "Bad table.getn() passed to pop");
111 return nil;
112 end
113 if ( n == 0 ) then
114 return nil;
115 end
116  
117 local v = table1[n];
118 table.setn(table1, n-1)
119 --Doesn't nil the entry like table.remove, so it's never garbace collected and can be replaced
120 --v = table.remove(table1);
121 return v;
122 end;
123  
124 printError = function (text)
125 ChatFrame1:AddMessage(text, RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, 1.0, UIERRORS_HOLD_TIME);
126 end;
127  
128 printDebugError = function (var, text)
129 if (var) and (getglobal(var)) then
130 Chronos.printError(text);
131 end
132 end;
133  
134 debug = function (enable)
135 if (enable) then
136 Chronos_OnUpdate = Chronos_OnUpdate_Debug;
137 CHRONOS_DEBUG = true;
138 CHRONOS_DEBUG_WARNINGS = true;
139 else
140 Chronos_OnUpdate = Chronos_OnUpdate_Quick;
141 CHRONOS_DEBUG = false;
142 CHRONOS_DEBUG_WARNINGS = false;
143 end
144 end;
145  
146 --[[
147 -- Scheduling functions
148 -- Parts rewritten by AnduinLothar for efficiency
149 -- Parts rewritten by Thott for speed
150 -- Written by Alexander
151 -- Original by Thott
152 --
153 -- Usage: Chronos.schedule(when,handler,arg1,arg2,etc)
154 --
155 -- After <when> seconds pass (values less than one and fractional values are
156 -- fine), handler is called with the specified arguments, i.e.:
157 -- handler(arg1,arg2,etc)
158 --
159 -- If you'd like to have something done every X seconds, reschedule
160 -- it each time in the handler or preferably use scheduleRepeating.
161 --
162 -- Also, please note that there is a limit to the number of
163 -- scheduled tasks that can be performed per xml object at the
164 -- same time.
165 --]]
166 schedule = function (when,handler,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
167 if ( not Chronos.online ) then
168 return;
169 end
170 if ( not handler) then
171 Chronos.printError("ERROR: nil handler passed to Chronos.schedule()");
172 return;
173 end
174  
175 --local memstart = gcinfo();
176 -- -- Assign an id
177 -- local id = "";
178 -- if ( not this ) then
179 -- id = "Keybinding";
180 -- else
181 -- id = this:GetName();
182 -- end
183 -- if ( not id ) then
184 -- id = "_DEFAULT";
185 -- end
186 -- if ( not when ) then
187 -- Chronos.printDebugError(CH_DEBUG_T, "Chronos Error Detection: ", id , " has sent no interval for this function. ", when );
188 -- return;
189 -- end
190  
191 -- -- Ensure we're not looping ChronosFrame
192 -- if ( id == "ChronosFrame" and ChronosData.lastID ) then
193 -- id = ChronosData.lastID;
194 -- end
195  
196 local task;
197 -- reuse task memory if possible to avoid excessive garbage collection --Thott
198 if(not ChronosData.sched[ChronosData.sched.n+1]) then
199 ChronosData.sched[ChronosData.sched.n+1] = {};
200 end
201 ChronosData.sched.n = ChronosData.sched.n+1;
202 local i = ChronosData.sched.n;
203 -- ChronosData.sched[i].id = id;
204 ChronosData.sched[i].time = when + GetTime();
205 ChronosData.sched[i].handler = handler;
206 ChronosData.sched[i].args = Chronos.getArgTable(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
207  
208 -- task list is a heap, add new --Thott
209 while(i > 1) do
210 parent = floor(i/2);
211 if(ChronosData.sched[parent].time > ChronosData.sched[i].time) then
212 Chronos_Swap(i,parent);
213 else
214 break;
215 end
216 i = parent;
217 end
218  
219 -- Debug print
220 --Chronos.printDebugError(CH_DEBUG, "Scheduled ", handler," in ",when," seconds from ", id );
221 --Chronos.printError("Memory change in schedule: ",memstart,"->",memend," = ",memend-memstart);
222 end;
223  
224  
225 --[[
226 -- Chronos.scheduleByName(name, delay, function, arg1, ... );
227 --
228 -- Same as Chronos.schedule, except it takes a schedule name argument.
229 -- Only one event can be scheduled with a given name at any one time.
230 -- Thus if one exists, and another one is scheduled, the first one
231 -- is deleted, then the second one added.
232 --
233 --]]
234 scheduleByName = function (name,when,handler,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
235 if ( not name ) then
236 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: No name specified to Chronos.scheduleByName");
237 return;
238 end
239 if(ChronosData.byName[name] and handler) then
240 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: scheduleByName is reasigning '".. name.."'.");
241 ChronosData.byName[name] = { time = when+GetTime(), handler = handler, arg = Chronos.getArgTable(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20); };
242 else
243 if ( not handler ) then
244 if ( not ChronosData.byName[name] ) then
245 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: No handler specified to Chronos.scheduleByName, no previous entry found for scheduled entry '".. name.."'.");
246 return;
247 end
248 if ( not ChronosData.byName[name].handler ) then
249 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: No handler specified to Chronos.scheduleByName, no handler could be found in previous entry of '".. name.."' either.");
250 return;
251 end
252 handler = ChronosData.byName[name].handler;
253 Chronos.printDebugError(CH_DEBUG_T,"Chronos: scheduleByName is updating '".. name.."' to time: ".. when);
254 else
255 Chronos.printDebugError(CH_DEBUG_T,"Chronos: scheduleByName is asigning '".. name.."'.");
256 end
257 ChronosData.byName[name] = { time = when+GetTime(), handler = handler, arg = Chronos.getArgTable(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20); };
258 end
259 end;
260  
261  
262  
263 --[[
264 -- unscheduleByName(name);
265 --
266 -- Removes an entry that was created with scheduleByName()
267 --
268 -- Args:
269 -- name - the name used
270 --
271 --]]
272 unscheduleByName = function(name)
273 if ( not Chronos.online ) then
274 return;
275 end
276 if ( not name ) then
277 Chronos.printError("No name specified to Chronos.unscheduleByName");
278 return;
279 end
280 if(ChronosData.byName[name]) then
281 ChronosData.byName[name] = nil;
282 end
283  
284 -- Debug print
285 --Chronos.printDebugError(CH_DEBUG, "Cancelled scheduled timer of name ",name);
286 end;
287  
288 --[[
289 -- isScheduledByName(name)
290 -- Returns the amount of time left if it is indeed scheduled by name!
291 --
292 -- returns:
293 -- number - time remaining
294 -- nil - not scheduled
295 --
296 --]]
297 isScheduledByName = function (name)
298 if ( not Chronos.online ) then
299 return;
300 end
301 if ( not name ) then
302 Chronos.printError("No name specified to Chronos.isScheduledByName ", this:GetName());
303 return;
304 end
305 if(ChronosData.byName[name]) then
306 return ChronosData.byName[name].time - GetTime();
307 end
308  
309 -- Debug print
310 --Chronos.printDebugError(CH_DEBUG, "Did not find timer of name ",name);
311 return nil;
312 end;
313  
314 --[[
315 -- Chronos.scheduleRepeating(name, delay, function);
316 --
317 -- Same as Chronos.scheduleByName, except it repeats without recalling and takes no arguments.
318 --
319 --]]
320 scheduleRepeating = function (name,when,handler)
321 if ( not name ) then
322 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: No name specified to Chronos.scheduleRepeating");
323 return;
324 end
325 if(ChronosData.byName[name] and handler) then
326 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: scheduleRepeating is reasigning ".. name);
327 ChronosData.byName[name] = { time = when+GetTime(), period = when, handler = handler, repeating = true };
328 else
329 if ( not handler ) then
330 if ( not ChronosData.byName[name] ) then
331 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: No handler specified to Chronos.scheduleRepeating, no previous entry found for scheduled entry '".. name.."'.");
332 return;
333 end
334 if ( not ChronosData.byName[name].handler ) then
335 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: No handler specified to Chronos.scheduleRepeating, no handler could be found in previous entry '".. name.."' either.");
336 return;
337 end
338 handler = ChronosData.byName[name].handler;
339 Chronos.printDebugError(CH_DEBUG_T,"Chronos: scheduleRepeating is updating '".. name.."' to time: ".. when);
340 else
341 Chronos.printDebugError(CH_DEBUG_T,"Chronos: scheduleRepeating is asigning '".. name.."'.");
342 end
343 ChronosData.byName[name] = { time = when+GetTime(), period = when, handler = handler, repeating = true };
344 end
345 end;
346  
347 --[[
348 -- Chronos.flushByName(name, when);
349 --
350 -- 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.
351 --
352 --]]
353 flushByName = function (name,when)
354 if ( not name ) then
355 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: No name specified to Chronos.flushByName");
356 return;
357 elseif ( not ChronosData.byName[name] ) then
358 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: no previous entry found for Chronos.flushByName entry '".. name.."'.");
359 return;
360 end
361 if (not when) then
362 Chronos.printDebugError(CH_DEBUG_T,"Chronos: flushing '".. name.."'.");
363 when = GetTime();
364 else
365 Chronos.printDebugError(CH_DEBUG_T,"Chronos: flushing '".. name.."' in "..when.." seconds.");
366 when = when+GetTime();
367 end
368 ChronosData.byName[name].time = when;
369 end;
370  
371 --[[
372 -- Chronos.startTimer([ID]);
373 -- Starts a timer on a particular
374 --
375 -- Args
376 -- ID - optional parameter to identify who is asking for a timer.
377 --
378 -- If ID does not exist, this:GetName() is used.
379 --
380 -- When you want to get the amount of time passed since startTimer(ID) is called,
381 -- call getTimer(ID) and it will return the number in seconds.
382 --
383 --]]
384 startTimer = function ( id )
385 if ( not Chronos.online ) then
386 return;
387 end
388  
389 if ( not id ) then
390 id = this:GetName();
391 end
392  
393 -- Create a table for this id's timers
394 if ( not ChronosData.timers[id] ) then
395 ChronosData.timers[id] = {};
396 ChronosData.timers[id].n = 0;
397 end
398  
399 -- Clear out an entry if the table is too big.
400 if (ChronosData.timers[id].n > Chronos.MAX_TASKS_PER_FRAME) then
401 Chronos.printError("Too many Chronos timers created for id ",id);
402 return;
403 end
404  
405 -- Add a new timer entry
406 table.insert(ChronosData.timers[id],GetTime());
407 end;
408  
409  
410 --[[
411 -- endTimer([id]);
412 --
413 -- Ends the timer and returns the amount of time passed.
414 --
415 -- args:
416 -- id - ID for the timer. If not specified, then ID will
417 -- be this:GetName()
418 --
419 -- returns:
420 -- (Number delta, Number start, Number end)
421 --
422 -- delta - the amount of time passed in seconds.
423 -- start - the starting time
424 -- now - the time the endTimer was called.
425 --]]
426  
427 endTimer = function( id )
428 if ( not Chronos.online ) then
429 return;
430 end
431  
432 if ( not id ) then
433 id = this:GetName();
434 end
435  
436 if ( not ChronosData.timers[id] or ChronosData.timers[id].n == 0) then
437 return nil;
438 end
439  
440 local now = GetTime();
441  
442 -- Grab the last timer called
443 local startTime = Chronos.pop(ChronosData.timers[id]);
444  
445 return (now - startTime), startTime, now;
446 end;
447  
448  
449 --[[
450 -- getTimer([id]);
451 --
452 -- Gets the timer and returns the amount of time passed.
453 -- Does not terminate the timer.
454 --
455 -- args:
456 -- id - ID for the timer. If not specified, then ID will
457 -- be this:GetName()
458 --
459 -- returns:
460 -- (Number delta, Number start, Number end)
461 --
462 -- delta - the amount of time passed in seconds.
463 -- start - the starting time
464 -- now - the time the endTimer was called.
465 --]]
466  
467 getTimer = function( id )
468 if ( not Chronos.online ) then
469 return;
470 end
471  
472 if ( not id ) then
473 id = this:GetName();
474 end
475  
476 local now = GetTime();
477 if ( not ChronosData.timers[id] or ChronosData.timers[id].n == 0) then
478 return 0,0,now;
479 end
480  
481 -- Grab the last timer called
482 local startTime = ChronosData.timers[id][ChronosData.timers[id].n];
483  
484 return (now - startTime), startTime, now;
485 end;
486  
487 --[[
488 -- isTimerActive([id])
489 -- returns true if the timer exists.
490 --
491 -- args:
492 -- id - ID for the timer. If not specified, then ID will
493 -- be this:GetName()
494 --
495 -- returns:
496 -- true - exists
497 -- false - does not
498 --]]
499 isTimerActive = function ( id )
500 if ( not Chronos.online ) then
501 return;
502 end
503  
504 if ( not id ) then
505 id = this:GetName();
506 end
507  
508 -- Create a table for this id's timers
509 if ( not ChronosData.timers[id] ) then
510 return false;
511 end
512  
513 return true;
514 end;
515  
516 --[[
517 -- getTime()
518 --
519 -- returns the Chronos internal elapsed time.
520 --
521 -- returns:
522 -- (elaspedTime)
523 --
524 -- elapsedTime - time in seconds since Chronos initialized
525 --]]
526  
527 getTime = function()
528 return ChronosData.elapsedTime;
529 end;
530  
531 --[[
532 -- Chronos.everyFrame(func,arg1,...)
533 --
534 -- runs func(arg1,...) every frame until func returns true.
535 -- This is the most effecient way to have something run
536 -- every frame, either forever, or until some job is done.
537 --
538 -- By Thott
539 --]]
540 everyFrame = function(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
541 table.insert(ChronosData.everyFrame,{func=func,arg=Chronos.getArgTable(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)});
542 end;
543  
544 --[[
545 -- performTask( TaskObject );
546 --
547 -- Queues up a task to be completed over time.
548 -- Contains a before and after function
549 -- to be called when the task is started and
550 -- completed.
551 --
552 -- Args:
553 -- TaskObject - a table containing:
554 -- {
555 -- (Required:)
556 -- step - function to be performed, must be fast
557 --
558 -- isDone - function which determines if the
559 -- task is completed.
560 -- Returns true when done
561 -- Returns false if the task should continue
562 -- to call step() each frame.
563 --
564 --
565 -- (Optional:)
566 -- stepArgs - arguments to be passed to step
567 -- doneArgs - arguments to be passed to isDone
568 --
569 -- before - function called before the first step
570 -- beforeArgs - arguments passed to Before
571 --
572 -- after - function called when isDone returns true
573 -- afterArgs - arguments passed
574 --
575 -- limit - a number defining the maximum number
576 -- of steps that will be peformed before
577 -- the task is removed to prevent lag.
578 -- (Defaults to 100)
579 -- }
580 --]]
581  
582 performTask = function (taskTable, name)
583 if ( not Chronos.online ) then
584 return;
585 end
586  
587 -- Valid table?
588 if ( not taskTable ) then
589 Chronos.printError("Chronos Error Detection: Invalid table to Chronos.peformTask", this:GetName());
590 return nil;
591 end
592  
593 -- Must contain a step function
594 if ( not taskTable.step or type(taskTable.step) ~= "function" ) then
595 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: You must specify a step function to be called to perform the task. (",this:GetName(),")");
596 return nil;
597 end
598  
599 -- Must contain a completion function
600 if ( not taskTable.isDone or type(taskTable.isDone) ~= "function" ) then
601 Chronos.printDebugError(CH_DEBUG_T,"Chronos Error Detection: You must specify an isDone function to be called to indicate if the task is complete. (",this:GetName(),")");
602 return nil;
603 end
604  
605 -- Get an ID
606 if ( not name ) then
607 name = this:GetName();
608 end
609  
610 -- Set the limit
611 if ( not taskTable.limit ) then
612 taskTable.limit = Chronos.MAX_STEPS_PER_TASK;
613 end
614  
615 local foundId = nil;
616  
617 for i=1,table.getn(ChronosData.tasks) do
618 if ( ChronosData.tasks[i].name == id ) then
619 foundId = i;
620 end
621 end
622  
623 -- Add it to the task list
624 if ( not foundId ) then
625 taskTable.name = name;
626 table.insert(ChronosData.tasks, taskTable);
627 return true;
628 elseif ( not ChronosData.tasks[foundId].errorSent ) then
629 ChronosData.tasks[foundId].errorSent = true;
630 Chronos.printError("Chronos Error Detection: There's already a task with the ID: ", name );
631 return nil;
632 end
633 end;
634  
635 --[[
636 -- Chronos.afterInit(func, ...)
637 -- Performs func after the game has truely started.
638 -- By Thott
639 --]]
640 afterInit = function (func, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
641 local id;
642 if(this) then
643 id = this:GetName();
644 else
645 id = "unknown";
646 end
647 --if(id == "SkyFrame") then
648 -- Chronos.printError("Ignoring Sky init");
649 -- return;
650 --end
651 if(ChronosData.initialized) then
652 func(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
653 else
654 if(not ChronosData.afterInit) then
655 ChronosData.afterInit = {};
656 ChronosData.afterInit.n = 0;
657 Chronos.schedule(0.2,Chronos_InitCheck);
658 end
659 local n = ChronosData.afterInit.n+1;
660 ChronosData.afterInit[n] = {};
661 ChronosData.afterInit[n].func = func;
662 ChronosData.afterInit[n].args = Chronos.getArgTable(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20);
663 ChronosData.afterInit[n].id = id;
664 ChronosData.afterInit.n = n;
665 end
666 end;
667 };
668  
669 --[[
670 -- unscheduleRepeating(name);
671 -- Mirrors unscheduleByName for backwards compatibility
672 --]]
673 Chronos.unscheduleRepeating = Chronos.unscheduleByName;
674  
675 --[[
676 -- isScheduledRepeating(name)
677 -- Mirrors isScheduledByName for backwards compatibility
678 --]]
679 Chronos.isScheduledRepeating = Chronos.isScheduledByName;
680  
681 --[[ Event Handlers ]]--
682 function Chronos_OnLoad()
683 Chronos.framecount = 0;
684  
685 ChronosData.byName = {};
686 ChronosData.repeating = {};
687 ChronosData.tasks = {};
688 ChronosData.tasks.n = 0;
689 ChronosData.sched = {};
690 ChronosData.sched.n = 0;
691 ChronosData.elapsedTime = 0;
692 ChronosData.variablesLoaded = false;
693 ChronosData.everyFrame = {};
694 ChronosData.everyFrame.n = 0;
695  
696 --[[ Convert Revision to Numeric ]]--
697 if (convertRev) then
698 convertRev("CHRONOS_REV");
699 end
700  
701 Chronos.afterInit(Chronos_SkyRegister);
702 this:RegisterEvent("VARIABLES_LOADED");
703 this:RegisterEvent("PLAYER_ENTERING_WORLD");
704 this:RegisterEvent("PLAYER_LEAVING_WORLD");
705 end
706  
707 function Chronos_OnEvent(event)
708 if(event == "VARIABLES_LOADED") then
709 ChronosData.variablesLoaded = true;
710 ChronosFrame:Show();
711 elseif (event == "PLAYER_ENTERING_WORLD") then
712 ChronosData.enteredWorld = true;
713 ChronosData.online = true;
714 elseif (event == "PLAYER_LEAVING_WORLD") then
715 ChronosData.online = false;
716 end
717 end
718  
719 function Chronos_InitCheck()
720 if(not ChronosData.initialized) then
721 if(UnitName("player") and UnitName("player")~=UKNOWNBEING and UnitName("player")~=UNKNOWNBEING and UnitName("player")~=UNKNOWNOBJECT and ChronosData.variablesLoaded and ChronosData.enteredWorld) then
722 ChronosData.initialized = true;
723 Chronos.schedule(1,Chronos_InitCheck);
724 return;
725 else
726 Chronos.schedule(0.2,Chronos_InitCheck);
727 return;
728 end
729 end
730 if(ChronosData.afterInit) then
731 local i = ChronosData.afterInit_i;
732 if(not i) then
733 i = 1;
734 end
735 ChronosData.afterInit_i = i+1;
736 --Chronos.printError("afterInit: processing ",i," of ",ChronosData.afterInit.n," initialization functions, id: ",ChronosData.afterInit[i].id);
737 Chronos_Run(ChronosData.afterInit[i].func,ChronosData.afterInit[i].args);
738 if(i == ChronosData.afterInit.n) then
739 ChronosData.afterInit = nil;
740 ChronosData.afterInit_i = nil;
741 else
742 Chronos.schedule(0.1,Chronos_InitCheck);
743 return;
744 end
745 end
746 end;
747 function Chronos_Run(func,arg)
748 if(func) then
749 if(arg) then
750 return func(unpack(arg));
751 else
752 return func();
753 end
754 end
755 end
756 function Chronos_Swap(i,j)
757 local t = ChronosData.sched[i];
758 ChronosData.sched[i] = ChronosData.sched[j];
759 ChronosData.sched[j] = t;
760 end
761 function Chronos_PopTask()
762 if(ChronosData.sched.n == 1) then
763 ChronosData.sched.n = 0;
764 return 1;
765 end
766 Chronos_Swap(1,ChronosData.sched.n);
767 ChronosData.sched.n = ChronosData.sched.n - 1;
768 local i = 1;
769 local new;
770 while(ChronosData.sched[i] and i <= ChronosData.sched.n) do
771 new = i*2;
772 if(new > ChronosData.sched.n) then
773 break;
774 elseif(new < ChronosData.sched.n) then
775 if(ChronosData.sched[new+1].time < ChronosData.sched[new].time) then
776 new = new + 1;
777 end
778 end
779 if(ChronosData.sched[new].time < ChronosData.sched[i].time) then
780 Chronos_Swap(i,new);
781 i = new;
782 else
783 break;
784 end
785 end
786 return ChronosData.sched.n+1;
787 end
788  
789 function Chronos_OnUpdate_Quick(dt)
790 --local memstart = gcinfo();
791 if ( not Chronos.online ) then
792 return;
793 end
794 if ( ChronosData.variablesLoaded == false ) then
795 return;
796 end
797  
798 if ( ChronosData.elapsedTime ) then
799 ChronosData.elapsedTime = ChronosData.elapsedTime + dt;
800 else
801 ChronosData.elapsedTime = dt;
802 end
803  
804 -- execute all the everyFrame tasks, deleting any that return true.
805 for i=1,ChronosData.everyFrame.n do
806 if(Chronos_Run(ChronosData.everyFrame[i].func,ChronosData.everyFrame[i].arg)) then
807 -- delete
808 ChronosData.everyFrame[i] = Chronos.pop(ChronosData.everyFrame);
809 end
810 end
811  
812 local now = GetTime();
813 local i;
814 -- Execute scheduled tasks that are ready, popping them off the heap.
815 while(ChronosData.sched.n > 0) do
816 if(ChronosData.sched[1].time <= now) then
817 i = Chronos_PopTask();
818 Chronos_Run(ChronosData.sched[i].handler,ChronosData.sched[i].args);
819 else
820 break;
821 end
822 end
823  
824 -- Execute named scheduled tasks that are ready.
825 local k,v = next(ChronosData.byName);
826 local newK, newV;
827 while (k ~= nil) do
828 newK,newV = next(ChronosData.byName, k);
829 if(v.time <= now) then
830 if (v.repeating) then
831 ChronosData.byName[k].time = now + v.period;
832 v.handler();
833 else
834 local y = v;
835 ChronosData.byName[k] = nil;
836 Chronos_Run(y.handler,y.arg);
837 end
838 end
839 k,v = newK,newV;
840 end
841  
842 local timeThisUpdate = 0;
843 local largest = 0;
844 local largestName = nil;
845  
846 -- Perform tasks if the time limit is not exceeded
847 -- Only perform each task once at most per update
848 --
849 for i=1, table.getn(ChronosData.tasks) do
850 -- Perform a task
851 runTime = Chronos_OnUpdate_Tasks(timeThisUpdate);
852 timeThisUpdate = timeThisUpdate + runTime;
853  
854 -- Check if this was the biggest hog yet
855 if ( runTime > largest ) then
856 largest = runTime;
857 largestName = i;
858 end
859  
860 -- Check if we've overrun our limit
861 if ( timeThisUpdate > Chronos.MAX_TIME_PER_STEP ) then
862 Chronos.printDebugError(CH_DEBUG_T,"Chronos Warning: Maximum cpu usage time exceeded on task. Largest task was: ", largestName );
863 break;
864 end
865 end
866 end
867  
868 Chronos_OnUpdate = Chronos_OnUpdate_Quick;
869  
870 function memcheck(memstart,where)
871 local memend = gcinfo();
872 if(memstart ~= memend) then
873 Chronos.printError("Memory change in ",where,": ",memstart,"->",memend," = ",memend-memstart);
874 end
875 return gcinfo();
876 end
877  
878 function Chronos_OnUpdate_Debug(dt)
879 local memstart = gcinfo();
880 if ( not Chronos.online ) then
881 return;
882 end
883 if ( ChronosData.variablesLoaded == false ) then
884 return;
885 end
886  
887 if ( ChronosData.elapsedTime ) then
888 ChronosData.elapsedTime = ChronosData.elapsedTime + dt;
889 else
890 ChronosData.elapsedTime = dt;
891 end
892  
893 local timeThisUpdate = 0;
894 local largest = 0;
895 local largestName = nil;
896  
897 -- execute all the everyFrame tasks, deleting any that return true.
898 for i=1,ChronosData.everyFrame.n do
899 if(Chronos_Run(ChronosData.everyFrame[i].func,ChronosData.everyFrame[i].arg)) then
900 -- delete
901 ChronosData.everyFrame[i] = Chronos.pop(ChronosData.everyFrame);
902 end
903 end
904  
905 local now = GetTime();
906 local i;
907 -- Execute scheduled tasks that are ready, popping them off the heap.
908 while(ChronosData.sched.n > 0) do
909 if(ChronosData.sched[1].time <= now) then
910 i = Chronos_PopTask();
911 Chronos_Run(ChronosData.sched[i].handler,ChronosData.sched[i].args);
912 else
913 break;
914 end
915 end
916  
917 -- Execute named scheduled tasks that are ready.
918 local memstart = gcinfo();
919 local k,v = next(ChronosData.byName);
920 local newK, newV;
921 while (k ~= nil) do
922 newK,newV = next(ChronosData.byName, k);
923 if(v.time <= now) then
924 local m = gcinfo();
925 if (v.repeating) then
926 ChronosData.byName[k].time = now + v.period;
927 v.handler();
928 else
929 local y = v;
930 ChronosData.byName[k] = nil;
931 Chronos_Run(y.handler,y.arg);
932 end
933 local mm = gcinfo();
934 memstart = memstart + mm - m;
935 end
936 k,v = newK,newV;
937 end
938 local memend = gcinfo();
939 if(memend - memstart > 0) then
940 Sea.io.print("gcmemleak from ChronosData.byName in OnUpdate: ",memend - memstart);
941 end
942  
943 local largest = 0;
944 local largestName = nil;
945  
946 -- Perform tasks if the time limit is not exceeded
947 -- Only perform each task once at most per update
948 --
949 memstart = memcheck(memstart,"OnUpdate, after all scheduled tasks, before Chronos tasks");
950 local ctn = table.getn(ChronosData.tasks);
951 for i=1, ctn do
952 -- Perform a task
953 runTime = Chronos_OnUpdate_Tasks(timeThisUpdate);
954 timeThisUpdate = timeThisUpdate + runTime;
955  
956 -- Check if this was the biggest hog yet
957 if ( runTime > largest ) then
958 largest = runTime;
959 largestName = i;
960 end
961  
962 -- Check if we've overrun our limit
963 if ( timeThisUpdate > Chronos.MAX_TIME_PER_STEP ) then
964 Chronos.printDebugError(CH_DEBUG_T,"Chronos Warning: Maximum cpu usage time exceeded on task. Largest task was: ", largestName );
965 break;
966 end
967  
968 if ( largestName ) then
969 -- ### Remove later for efficiency
970 --Chronos.printDebugError(CH_DEBUG, " Largest named task: ", largestName );
971 end
972 end
973 memstart = memcheck(memstart,"OnUpdate, end");
974 end
975  
976 -- Updates a single task
977 function Chronos_OnUpdate_Tasks(timeThisUpdate)
978 if ( not Chronos.online ) then
979 return;
980 end
981  
982 -- Lets start the timer
983 Chronos.startTimer();
984  
985 -- Execute the first task
986 if ( ChronosData.tasks[1] ) then
987 -- Obtains the first task
988 local task = table.remove(ChronosData.tasks, 1);
989  
990 -- Start the task if not yet started
991 if ( not task.started ) then
992 Chronos_Run(task.before,task.beforeArgs);
993  
994 -- Mark the task as started
995 task.started = true;
996 end
997  
998 -- Perform a step in the task
999 Chronos_Run(task.step,task.stepArgs);
1000  
1001 -- Check if the task is completed.
1002 if ( task.isDone() ) then
1003 -- Call the after-task
1004 if ( task.after ) then
1005 if ( task.afterArgs ) then
1006 task.after(unpack(task.afterArgs) );
1007 else
1008 task.after();
1009 end
1010 end
1011 else
1012 if ( not task.count ) then
1013 task.count = 1;
1014 else
1015 task.count = task.count + 1;
1016 end
1017  
1018 if ( task.count < task.limit ) then
1019 -- Move them to the back of the list
1020 table.insert(ChronosData.tasks, task);
1021 else
1022 Chronos.printDebugError(CH_DEBUG_T, "Task killed due to limit-break: ", task.name );
1023 end
1024 end
1025 end
1026  
1027 -- End the timer
1028 return Chronos.endTimer();
1029 end
1030  
1031 -- Command Registration
1032 -- Notes to self:
1033 -- * Relocate to its own module?
1034 --
1035  
1036 function Chronos_SkyRegister()
1037 local chronosFunc = function(msg)
1038 local _,_,seconds,command = string.find(msg,"([%d\.]+)%s+(.*)");
1039 if(seconds and command) then
1040 Chronos.schedule(seconds,Chronos_SendChatCommand,command);
1041 else
1042 Chronos.printError(SCHEDULE_USAGE1);
1043 Chronos.printError(SCHEDULE_USAGE2);
1044 end
1045 end
1046 if(Sky) then
1047 Sky.registerSlashCommand(
1048 {
1049 id = "Schedule";
1050 commands = SCHEDULE_COMM;
1051 onExecute = chronosFunc;
1052 helpText = SCHEDULE_DESC;
1053 }
1054 );
1055 else
1056 SlashCmdList["CHRONOS_SCHEDULE"] = chronosFunc;
1057 for i = 1, table.getn(SCHEDULE_COMM) do setglobal("SLASH_CHRONOS_SCHEDULE"..i, SCHEDULE_COMM[i]); end
1058 end
1059 end
1060  
1061 --[[
1062 -- Sends a chat command through the standard
1063 --]]
1064 function Chronos_SendChatCommand(command)
1065 local text = ChatFrameEditBox:GetText();
1066 ChatFrameEditBox:SetText(command);
1067 ChatEdit_SendText(ChatFrameEditBox);
1068 ChatFrameEditBox:SetText(text);
1069 end
1070