TIMER

From HEWIKI
Jump to: navigation, search

Contents

This page discusses how to get HSL calls at regular intervals via the TIMER datatype.

TIMER is a complex data type that causes a call to an HSL script after a specified interval of time.

Overview

Managing the concept of time, intervals and timers is important in MMOs. In particular, MMOs have various unusual needs for keeping track of time. For example, some game systems might work on real time (called "wall time", as in what time the "clock on the wall" says it is), vs. time that only elapses while logged on. Many common MMO game-play features, such as enhancements / buffs /etc. can be implemented using a Timer. HeroEngine timers contain important features such as the ability to be paused (for example, if a player is talking to the game-staff for customer service, a script can pause timers that might otherwise go off when it isn't appropraite, such as bleeding to death, etc.).

A timer is implemented as a field data type that causes a script call after a specified interval of time. HSL timers are like .NET timers in that they automatically repeat. If a scripter wants a 1-shot timer, they build removal or stopping of the timer into the tick() functionality.

When the timer's timespan has elapsed, a call is made to either a function or method with the format of <timer_field_name>_tick(). If the script subfield is set to a valid script, the function in that script is called; otherwise, the method is called on the node containing this timer field.

Related datatypes

Processing the tick

When do timers tick?

(running) Timers are evaluated by the engine each cycle to determine if the elapsed time is greater than the last fired time plus the fire rate. That means, timers fire based on actual time elapsed.

Now, we can't bend the laws of physics or speed of processors so if you have a timer that when it ticks it processes data that takes 10 hours but your firerate is set to 2 hours...we can't magically make your code run faster. When the 10 hour long tick processing finishes, the timer will almost immediately (the uncertainty here is that some other asynchronous event may be processed in front of the timer) tick again and start processing the tick.

Let's examine a more complex processing flow...

Imagine the script below is executed (calling foo()), when does a timer that needs to tick fire?

function foo()
   bar()
   // stuff
.
 
function bar()
  // do bar related stuff
.
 
function some_tick()
  // timer tick related stuff
.

From a processing standpoint...

  1. function foo() is invoked as the result of an external event such as a CLI invoked script call or user input
  2. during the processing of foo() function bar() is called
  3. function bar() is processed and completes its execution
  4. bar() returns allowing foo() to continue processing from the point at which bar was invoked
  5. foo() completes any remaining processing and returns
  6. HeroMachine (VM) is no longer processing any script code
  7. C++ evaluates what other asynchronous events are pending or have arrived in the meantime
  8. C++ during evaluation of its timers determines that one should fire so it schedules it for execution
  9. HeroMachine (VM) is notified of the timer firing so it can run the script logic for tick()

It is technically more complicated, because the engine is asynchronous event driven under the hood so there is not just one timer waiting to tick but also countless other events occurring in C++ that must be scheduled to be executed by the HeroMachine including:


All of those are executed one at a time, in the order the engine has determined to schedule them. Recall the engine is multi-threaded, but the HeroMachine (VM) is not.

Note, there is some additional complexity with HeroMachines in that certain types of HeroScript calls invoke new HeroMachine (VM)s and place them onto the HeroMachine stack either for immediate execution or execution following the completion of the current machine's processing (CallMethodPostEvent(), CallScript()). In these cases, there is still only one HeroMachine (VM) executing at any moment in time but there may be multiple on the stack either waiting to resume or process upon the completion of the current VM's task. You can think of these additional HeroMachines as simply one more asynchronous event that the C++ has to schedule for execution. (please note, invoking additional HeroMachines on the stack is expensive and should be only done when necessary).

Subfields

TIMER is a complex data type with member subfields. All time stamps are relative to the time source of the timer. The time stamp of a LOCAL time source timer will more than likely be different than a COORDINATED time stamp.

timerState

realTime

startTime

stopTime

lastFired

fireRate

elapsedTime

pausedTime

script


timerTimeSource

Functions

The TIMER data type also has member functions that can be called.

start()

stop()

pause()

unpause()

forceTick()

timeRemaining()

setTimeSource(string)

initializeDefault()

Examples

Procedural/Functional Style Timer

A procedural style timer calls a particular procedure (function) in a specified script.

Create field "custom_timer"

: cfd "custom_timer" timer;

Define "candle" class, with the above field.

: ccd "candle", asset, custom_timer;
  --> Node 4  (for example)

The following example assumes that a class GenericTimer has already been created, which has a timer field named myTimer

function foo()
  n as NodeRef of Class GenericTimer = CreateNodeFromClass("GenericTimer")
  n.myTimer.script = SYSTEM.EXEC.THISSCRIPT
  n.myTimer.fireRate = 0:05:45
  n.myTimer.realTime = true
  n.myTimer.start()
.
 
function myTimer_tick()
  where me is kindof GenericTimer
    if (me.myTimer.elapsedTime.inMilliseconds > 100) 
      println( me.myTimer.lastFired.hour )
      println( me.myTimer.startTime.monthString )
    .
    me.myTimer.stop()
  .
.

Object Oriented Style Timer

Object oriented style timers call a method in the class methods script for the class from which the node is instantiated (or constructed via GLOMming) checking the class(es) making up the instantiation in which the timer field is a member.


Create a timer field "myTimer":

: cfd "myTimer", timer;

Define "GenericTimer" class, with the above field:

: ccd "GenericTimer", data; myTimer;

The following example assumes that a class GenericTimer has already been created, which has a timer field named myTimer

Create a class methods script for the GenericTimer (e.g. GenericTimerClassMethods):

// GenericTimerClassMethods
 
// This function demonstrates how a timer instance could be instantiated and started
//   using the client console window's "call" syntax (e.g. call GenericTimerClassMethods start)
function start()
  timer as noderef of class GenericTimer = CreateNodeFromClass("GenericTimer")
  timer.Initialize_myTimer()
.
 
// Setup the timer by setting the firerate and starting it
method Initialize_myTimer()
  if me.myTimer.timerState = ON
    me.myTimer.stop()
  .
 
  me.myTimer.firerate = 0:00:05  
  me.myTimer.start()
.
 
// <field_name>_Tick() naming convention allows the engine to tie the DOM field to the method to be called
method myTimer_Tick()
  if GetFrameNumber() mod 2 = 0
    println("tick")
  else
    println("tock")
  .
.

See also

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox