Time based schedular

A lot of things in a game need to be run based on a per-time basis rather than a per-frame basis.

Examples:

  • Networking
  • Regenerating/recalculating pathfinding
  • Keep-alives for threads

As such, I’ve written a tiny scheduler, of the sort you’d find in an embedded system. There are definitely better ones out there, but this one does all I want it to.

The Scheduler Code:

import bge
import time


bge.schedule_list = []


def run():
    current_time = time.time()
    for task in bge.schedule_list:
        funct = task[0]
        time_between = task[1]
        if len(task) != 3:
            previous_time = current_time - time_between
            task.append(previous_time)
        else:
            previous_time = task[2]
        if previous_time + time_between < current_time:
            task[2] = previous_time + time_between
            funct()

An example task:


def test_task():
    if 'run_num' not in test_task.__dict__:
        test_task.run_num = 0
    print(test_task.run_num)
    test_task.run_num += 1
    
bge.schedule_list.append([test_task, 0.5])

This will print a counter that increments by 1 every half second.

Wouldn’t it make sense to provide scheduler operations (e.g. add, stop/remove) and hide the implementation details?

I mean the user of the scheduler needs to knows the internals of it. This is not a good design.

Imagine you want to sort the list when a new task is added. With the current implementation you need to change all users, not just the scheduler.
Imagine you want to exchange the list with a dictionary. You can’t do that without changing all users.

And Monster is of course correct. So I have developed a further scheduling system, complete with adding it’s own little bit to the blender API.

Use:

Create an always sensor (true triggering every frame) connected to a python module controller running ‘scheduler.start.’
Then you can use it in your script with:

import bge
bge.schedular.add_task(...)
print(bge.schedular.task_list)

Examples:
A function can be made to run every frame:


bge.scheduler.add_task(funct=print, delay=0, parems=['This'])

A function can be made to run every minute:


bge.scheduler.add_task(funct=print, delay=60, parems=['This'])

A function can be made to run every minute including when it is first created:


bge.scheduler.add_task(funct=print, delay=60, firstrun=True, parems=['This'])

Delay a chunk of code by 5 seconds:


bge.scheduler.add_task(funct=print, delay=5, num_executions=1, parems=['This'])

Run a function every 3 seconds 5 times:


bge.scheduler.add_task(funct=print, delay=3, num_executions=5, parems=['This'])

Example Blend: Upon game start, you'll see a note in console that the scheduler has started. If you hit space-bar, the word 'this' will be printed five times in the console, once every second. BGEscheduler.zip (69.7 KB)

Documentation:

'''------------------------------------------------------------------------
        About
------------------------------------------------------------------------


The BGEschedular provides a way to schedule the execution of code, as 
well as provide a way for code running in overlay/underlay scenes to 
inject their code into the main scene. 


Intended Usage:
    - Many things in a game need to be run at regular intervals, or 
      delayed by a certain amount. The schedular provides a way to do 
      this
    - Some code depends on the scene it is run in (eg libload). Having a 
      schedular in the main scene allows a loading scene's code to get 
      and alter the main scene easily


Examples:
    - A grenade needs a three-second fuse. This could be passed to the 
    scheduler (though OOP is probably recommended)
    - A bomb has a count-down from the beginning of the game
    - A weapon has a limited refire rate
    - A keymapper/input script needs to be run every frame, but does not 
      have an inherent object
    - A networking system needs to be run every frame, but does not have 
      a inherent object


Interface:
    - The code scheduler.start is run by an always sensor. The scheduler
      will then place itself in bge.scheduler, which can be accessed 
      from any script.
    


------------------------------------------------------------------------
        Documentation:
------------------------------------------------------------------------
bge.scheduler:
    
    add_task(funct=None, delay=0.0, firstrun=False, parems=None, 
                        num_executions=-1):
        Creates a task that will be run repeatedly
        
        Keyword Arguments:
          funct -- The function to schedule
          delay -- The time (in seconds) between runs
          firstrun -- Whether to run the task now
          parems -- Paremeters to pass into the function
          num_executions -- Number of times to run the task, -1 for 
              infinite
        
        Returns:
          Task -- The newly created task (See the Task documentation)
                
    list task_list
        A list of Tasks, both active and inactive. It is not recommended
        to edit this list. To remove a task use Task.end(), to create a
        task use bge.scheduler.add_task




Task():
    A class for containing a 'task.' Should be created by the 
    scheduler's function 'add_task'
    
    run():
        Runs the function now. Does not reschedule it, use reschedule() 
        to do that
            
    reschedule():
        Sets the time to next run as previous_time + delay
    
    set_delay(time=0.0):
        Sets the time between executions of the task
        
        Keyword Arguments:
            time -- The time (in seconds) between execution of the task
        
    pause():
        Pauses the task
    
    resume():
        Resumes the task
    
    end():
        Removes the task
        
    bool paused
        True if the task is paused
        
    int remaining_executions
        How many more times the task will be run. -1 for infinite
        
    float delay
        The time between runnings
    
    float previous_time
        The absolute time (in seconds) that the function was last run
    
    float time_since_last_run
        The time (in seconds) since the function was previously run