Difference between revisions of "Timeout-Scheduler"

From Second Life Wiki
Jump to navigation Jump to search
Line 3: Line 3:
== What it is ==
== What it is ==


Timeout-Scheduler is a function which provides a very versatile tool for handling multiple independent timer events, optionally supplemented with arbitrary data.
Timeout-Scheduler is a function which provides a simple yet versatile tool for handling multiple independent timer events.


== Some key ideas ==
== Some key ideas ==
Line 9: Line 9:
* Easy handling of multiple independent, asynchronous timeout events (for example dialog timeouts for multiple users).
* Easy handling of multiple independent, asynchronous timeout events (for example dialog timeouts for multiple users).
* One central function (in conjunction with a timer event) for getting, setting, modifying and removing timeout events.
* One central function (in conjunction with a timer event) for getting, setting, modifying and removing timeout events.
* Timeout events can be supplemented with additional data which is returned for example when the respective timeout occurs.
* The data associated with the timeout event can be fetched manually or when the timeout is due.
* Hierarchical conditional selection of events, offering many possibilities to do things with very few function calls.
* Hierarchical conditional selection of events, offering many possibilities to do things with very few function calls.


== Short overview of the function ==
== Short overview of the function ==


The <code>sched()</code> function takes a few arguments, of which some can be left empty depending on the use case.
The <code>sched()</code> function takes two arguments:
 
*<code>base</code> and <code>cond</code>, the first two parameters of the function, are used to give the timeout one or more '''names''' which can be used to '''look up''' this timeout event later, for instance to retrieve data about it or to remove it manually. The <code>base</code> paramter must usually be non-empty; the <code>cond</code> list takes up to two additional names for this timeout (see below on how to use the <code>cond</code> parameter for doing cool stuff).


*<code>time</code> can take three different kinds of values with different meanings:
*<code>time</code> can take three different kinds of values with different meanings:
** '''Positive''' values are used for setting '''new''' timeouts. The value is the number of seconds after which the event times out and the hander in the <code>timer</code> event is triggered.
** '''Positive''' values are used for setting '''new''' timeouts. The value is the time in seconds after which the timeout is triggered from within the <code>timer</code> event.
** A value of '''0''' is used to '''remove''' an existing event (and fetching that event's data), see below.
** A value of '''0''' is used to '''remove''' an existing event (and fetching that event's data).
** A value of '''-1''' is used to '''retrieve''' the data associated with an existing event '''without deleting''' the event.
** Any '''negative''' value may be used to '''retrieve''' the data associated with an existing event '''without deleting''' the event.


*<code>data</code> is a list of '''additional data''' to associate with the timeout event. This data '''cannot''' be used to look up events, but it is returned in addition to the other timeout data when a timeout event is retrieved, removed or triggered.
*<code>data</code> is a list of data associated with the timeout event. It is used in two ways: Firstly, it is used to look up existing timeout events matching that data. Secondly, the given data is stored in the schedule when a new timeout event is created.


== Creating simple timeout events ==
== Creating simple timeout events ==


Let's start by creating simple timeout events. We'll only use the first parameter <code>base</code> to give the timeouts a name and <code>data</code> to assign some additional data. Try this:
Try this to create a few simple timeout events:


<lsl>
<lsl>
sched("timeout3", [], 3, []);
sched(1.8, ["Some other timeout"]);
sched("timeout2", [], 2, ["SomeData0", "yay"]);
sched(2.5, ["Yet", "Another", "Timeout", "Event"]);
sched("timeout1", [], 1, ["SomeData0", "woah", "SomeData1", "moah"]);
sched(0.6, ["First timeout"]);
</lsl>
</lsl>


Line 41: Line 39:
<lsl>
<lsl>
////////// NOTES ///////////////////////
////////// NOTES ///////////////////////
 
// Timeout-Scheduler v0.1.1-Dev (2010-11) by Ochi Wolfe
// Timeout-Scheduler v0.1.2-Dev (2010-12) by Ochi Wolfe
 
// You may use, change and distribute this code as you wish.
// You may use, change and distribute this code as you wish.
// When distributing it in source form, it might still be desirable
// When distributing it in source form for others to use, please
// to include a note where it is from and what you have changed.
// include a note where it is from and what you have changed.
 
////////// VARIABLES ///////////////////
////////// VARIABLES ///////////////////
 
list schedule = [];
list schedule = [];
 
////////// FUNCTIONS ///////////////////
////////// FUNCTIONS ///////////////////
 
string get(list d, string k, string r) {
list sched(float time, list data) {
    integer i = llListFindList(llList2ListStrided(d, 0, -1, 2), [k]);
     integer LEN = 4;           // Max data length
    if (~i) return llList2String(d, 2*i+1);
     integer idx = -1;         // Selected timeout
    return r;
     float  now = llGetTime(); // The current time
}
    list    res;              // Result data list
 
list sched(key base, list cond, integer time, list data) {
    // If data is given, select first matching timeout.
     list    res;
    // If data is empty, select next timeout that is due.
     integer idx = -1;
     if (data) {
     integer now = llGetUnixTime();
         idx = llListFindList(schedule, data);
     } else {
     if (base) { // Select by conditions
         float til = llList2Float(schedule, 0);
         idx = llListFindList(schedule, [base] + cond);
         if ((til != 0.0) && (til <= now)) idx = 1;
     } else { // Select next due timeout
         integer til = llList2Integer(schedule, 0);
         if (til && til <= now) idx = 1;
     }
     }
 
     if (~idx) { // There already exists a timeout
     // If a timeout was selected, fetch its data.
        res = llParseStringKeepNulls(llList2String(schedule, idx+3), ["\n"], []);
    // If time == 0.0, delete it afterwards.
        if (llGetListEntryType(schedule, idx+2) != TYPE_INTEGER) res = ["_2", llList2String(schedule, idx+2)] + res;
    if (~idx) {
        if (llGetListEntryType(schedule, idx+1) != TYPE_INTEGER) res = ["_1", llList2String(schedule, idx+1)] + res;
        res = llList2List(schedule, idx, idx+LEN-1);
        res = ["_0", llList2String(schedule, idx)] + res;
         if (time == 0.0) schedule = llDeleteSubList(schedule, idx-1, idx+LEN-1);
       
         if (!time) { // Delete existing timeout only if time == 0
            schedule = llDeleteSubList(schedule, idx-1, idx+3);
        }
     }
     }
 
     if (time > 0) { // If positive time is given, insert new timeout
     // If time > 0.0, insert a new timeout event.
         while (llGetListLength(cond) < 2) cond += [FALSE];
    // Fill data list to make its length consistent.
         schedule = llListSort([now+time, base] + cond + [llDumpList2String(data, "\n")] + schedule, 5, TRUE);
    if (time > 0.0) {
         integer len = llGetListLength(data); while (len++ < LEN) data += [0.0];
         schedule = llListSort([now+time] + data + schedule, LEN+1, TRUE);
     }
     }
 
     // Adjust timer if necessary
     // If next timeout is in the future, (re)start timer.
     integer til = llList2Integer(schedule, 0);
    // If there are no timeouts left, stop timer.
     if (!til || til > now) llSetTimerEvent((!!til) * (til-now));
    // Otherwise, just leave the timer as it is.
     float til = llList2Float(schedule, 0);
     if     (til-now > 0.0) llSetTimerEvent(til-now);
    else if (til == 0.0)   llSetTimerEvent(0.0);
 
     return res;
     return res;
}
}
 
////////// STATES //////////////////////
////////// STATES //////////////////////
 
default {
default {
     state_entry() {
     state_entry() {
        sched("timeout", [], 3, []);
 
     }
     }
 
     timer() {
     timer() {
         list tData;
         list data;
         while (tData = sched("", [], 0, [])) {
        // Fetch and handle all timeouts that are due and delete them.
             llOwnerSay("Base: "      + get(tData, "_0", ""));
         while (data = sched(0, [])) {
            llOwnerSay("Cond1: "    + get(tData, "_1", "(not set)"));
             llOwnerSay("Timeout: " + llDumpList2String(data, ", "));
            llOwnerSay("Cond2: "     + get(tData, "_2", "(not set)"));
            llOwnerSay("SomeData0: " + get(tData, "SomeData0", "(not set)"));
            llOwnerSay("SomeData1: " + get(tData, "SomeData1", "(not set)"));
         }
         }
     }
     }
}
}
</lsl>
</lsl>

Revision as of 08:28, 4 December 2010

KBwarning.png Warning: This page and the script are currently under development and are not yet finished. Please use it with caution and check back later for updates.

What it is

Timeout-Scheduler is a function which provides a simple yet versatile tool for handling multiple independent timer events.

Some key ideas

  • Easy handling of multiple independent, asynchronous timeout events (for example dialog timeouts for multiple users).
  • One central function (in conjunction with a timer event) for getting, setting, modifying and removing timeout events.
  • The data associated with the timeout event can be fetched manually or when the timeout is due.
  • Hierarchical conditional selection of events, offering many possibilities to do things with very few function calls.

Short overview of the function

The sched() function takes two arguments:

  • time can take three different kinds of values with different meanings:
    • Positive values are used for setting new timeouts. The value is the time in seconds after which the timeout is triggered from within the timer event.
    • A value of 0 is used to remove an existing event (and fetching that event's data).
    • Any negative value may be used to retrieve the data associated with an existing event without deleting the event.
  • data is a list of data associated with the timeout event. It is used in two ways: Firstly, it is used to look up existing timeout events matching that data. Secondly, the given data is stored in the schedule when a new timeout event is created.

Creating simple timeout events

Try this to create a few simple timeout events:

<lsl> sched(1.8, ["Some other timeout"]); sched(2.5, ["Yet", "Another", "Timeout", "Event"]); sched(0.6, ["First timeout"]); </lsl>

This should trigger the three timeouts in the correct chronological order.

Implementation

<lsl> ////////// NOTES ///////////////////////

// Timeout-Scheduler v0.1.2-Dev (2010-12) by Ochi Wolfe

// You may use, change and distribute this code as you wish. // When distributing it in source form for others to use, please // include a note where it is from and what you have changed.

////////// VARIABLES ///////////////////

list schedule = [];

////////// FUNCTIONS ///////////////////

list sched(float time, list data) {

   integer LEN = 4;           // Max data length
   integer idx = -1;          // Selected timeout
   float   now = llGetTime(); // The current time
   list    res;               // Result data list
   // If data is given, select first matching timeout.
   // If data is empty, select next timeout that is due.
   if (data) {
       idx = llListFindList(schedule, data);
   } else {
       float til = llList2Float(schedule, 0);
       if ((til != 0.0) && (til <= now)) idx = 1;
   }
   // If a timeout was selected, fetch its data.
   // If time == 0.0, delete it afterwards.
   if (~idx) {
       res = llList2List(schedule, idx, idx+LEN-1);
       if (time == 0.0) schedule = llDeleteSubList(schedule, idx-1, idx+LEN-1);
   }
   // If time > 0.0, insert a new timeout event.
   // Fill data list to make its length consistent.
   if (time > 0.0) {
       integer len = llGetListLength(data); while (len++ < LEN) data += [0.0];
       schedule = llListSort([now+time] + data + schedule, LEN+1, TRUE);
   }
   // If next timeout is in the future, (re)start timer.
   // If there are no timeouts left, stop timer.
   // Otherwise, just leave the timer as it is.
   float til = llList2Float(schedule, 0);
   if      (til-now > 0.0) llSetTimerEvent(til-now);
   else if (til == 0.0)    llSetTimerEvent(0.0);
   return res;

}

////////// STATES //////////////////////

default {

   state_entry() {
   }
   timer() {
       list data;
       // Fetch and handle all timeouts that are due and delete them.
       while (data = sched(0, [])) {
           llOwnerSay("Timeout: " + llDumpList2String(data, ", "));
       }
   }

} </lsl>