!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! oldaemon The Objectloop Daemon
! Version 1.0 By L. Ross Raszewski
! 12.2.2002 (
[email protected])
! For Z-code and Glulx
! No external requirements
!
! It is a well known fact that one of the major causes of game sluggishness
! is use of the general-form objectloop.
! The general form objectloop is any objectloop construct other than
! (x in y), (x from y), or (x near y).
!
! If a game uses the general-form objectloop within a daemon or each_turn
! construct, the latency between turn cycles is substantially increased.
! Inform's standard library already performs one such objectloop
! (In 'MoveFloatingObjects')
!
! The purpose of the Objectloop Daemon is to reduce the number of times
! that an objectloop must be executed by combining a number of tasks into
! a single loop.
!
! To use the objectloop daemon, simply insert the line:
! StartDaemon(loop_daemon);
! In your Initialise function. This will activate the daemon.
! Now, instead of performing an objectloop in the context of a daemon
! or each_turn, place an object inside the loop_daemon object to
! perform the task. objectloop daemon events have the following structure:
!
! Object loop_task
! with
! exec [ o;
! ! Required. When the daemon is run, the exec function will be
! ! executed for every object in the game
! ! The exec method corresponds to the body of the original
! ! objectloop
! ],
! init [;
! ! Optional. If provided, this method is called before looping begins
! ],
! fini [;
! ! Optional. If provided, this method is called after looping ends
! ],
! pred [ o;
! ! Optional. If provided, this is called for every object immediately
! ! before exec. It should return false to exclude the object, true to
! ! include it. The pred method corresponds to the loop condition.
! ! If no pred is included, the loop is equivalent to "loop over
! ! all objects"
! ];
!
! Here is an example of a loop task:
! This code shows a daemon which sets the 'general' attribute on every
! object of class 'Foo' once per turn. If several such daemons exist,
! they might produce a substantial lag. The objectloop daemon version
! will perform only one objectloop, regardless of how many tasks are
! assigned.
! ...
! daemon [o;
! objectloop(o ofclass Foo)
! give o general;
! ],
! ...
!
! And now the objectloop daemon version:
!
! object flag_set loop_daemon
! with pred [o; return (o ofclass Foo); ],
! exec [o; give o general; ];
!
! Placing a task inside the loop_daemon enables it -- the task will be run
! the next time daemons are executed. Removing a task disables it.
!
! Caveats:
! 1. Removing a task
! It is safe to remove a task from the objectloop daemon at any time.
! The task will stop immediately. Of course, disabling a task does not
! undo work already done. For reference, here is the effect of removing
! a task at various times:
! When the daemon is not currently executing:
! The task will not be executed when the daemon starts
! Within the task's 'init':
! The task will not be executed on this turn
! Within the task's 'fini':
! The task has already been done for this turn, but
! will not be done on subsequent turns
! Within an 'exec': The task will not be applied to any more objects.
! This is roughly equivalent to performing a 'break'
! within a normal objectloop.
! To allow a task to break in the middle, but to make it
! continue to run on the next turn, it is
! necessary to re-insert it (via a daemon of its own).
! 'fini' will not be run for this task.
! Within a 'pred': If the pred function returns true, the task will
! *still be run* on the current object, but will not
! be run on any other objects.
!
! 2. Interlacing. In a traditional demon-based objectloop, the body of each
! loop is executed once for every object consecutively.
! If there are two such loops, the first loop is executed for
! every object, then the second loop is executed for every
! object. Within the objectloop daemon, the order is changed
! slightly. Tasks are interlaced. That is, every task is run
! for the first object, then every task is run for the second
! object. This may be important if the body of the loop makes
! use of global variables.
! 3. Scope. Since 'exec' is excecuted fresh for each object, you cannot use
! local variables to collect information over the course of an
! objectloop. The recommended solution to this is to attach
! properties to the task which are used in place fo local variables.
! You must then also use the .init() routine to reset these
! properties each time
! 4. Context. The loop_daemon is run in the context of a daemon. Consult the
! inform designer's manual for a discussion of where exactly daemons
! are executed during the course of a turn cycle. Individual tasks are
! executed in lineage order. Using 'move x to loop_daemon' will make
! x the first task to be executed. For finer control, use pmove and
! rmove (in Utility.h)
! 5. Speed. The loop daemon imposes some overhead over a standard objectloop.
! It is not sensible to use it to replace a single objectloop.
! However, it will perform at most one objectloop per turn, regardless
! of how many tasks are active. Therefore, if there is more than one
! such loop, the daemon will save time. The amount of time it saves
! is proportional to the total number of objects in the game.
! If no tasks are active, the daemon does not perform the objectloop.
!
! The author would appreciate any and all commentary. This library extention
! is free for use in all non-commercial and competition games.
! Permission is required (and will almost certainly be forthcoming) for
! commercial releases.
!
ifndef OLDAEMON_LIBRARY;
Constant OLDAEMON_LIBRARY 32;
Object loop_daemon "Objectloop Daemon"
with
init 0, fini 0, exec 0, pred OLDAEMON_LIBRARY,
daemon
[x o y;
for(x=child(self):x:x=y)
{
y=sibling(x);
if (x provides init)
x.init();
}
if (~~child(self)) return;
objectloop(o)
if (~~child(self)) return; else
for(x=child(self):x:x=y)
{
y=sibling(x);
if (~~(x provides pred) || x.pred(o))
x.exec(o);
}
for(x=child(self):x:x=y)
{
y=sibling(x);
if (x provides fini)
x.fini();
}
];
endif;