vanilla-wow-addons – Blame information for rev 1
?pathlinks?
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 |