AdvancedNotecardReader

From Second Life Wiki
Revision as of 08:37, 25 August 2008 by Lear Cale (talk | contribs)
Jump to navigation Jump to search

<lsl> // Multi-file configuration, v1a // Learjeff Innis, 2007 // Public Domain. Feel free to delete header comments.

// This script reads any number of notecards that match a given suffix, // reading the contents into global variables. It rereads the notecards // on any inventory change.

// It does not require a script reset on any event, but one is done for // owner change on general principles. If your object needs not to be // reset on owner change, you may delete those lines from the code.

// ============================================================== // Places where you need to change the code are marked with "%%%" // ==============================================================


// Example config variables -- replace these with meaningful stuff! %%%

// For this example, we allow the user to configure any number of "foos", // with an integer parameter for each foo. There's also a float "bar" // parameter.

list Foos; // %%% all foos configured list FooVals; // %%% value for each foo configured float Bar; // %%% a configurable parameter


// Static parameters for reading card config: you may change these, but don't have to. integer ConfigRequired = FALSE; // whether to fail if no config cards string ConfigNotecardSuffix = ".cfg"; // string identifying config notecards float ConfigTimeout = 60.0; // seconds to wait for slow server


// Globals for reading card config integer ConfigLineIndex; // line number in notecard we're reading key ConfigRequestID; // request we're waiting for list ConfigCards; // list of names of config notecards string ConfigCardName; // name of card being read integer ConfigCardIndex; // index of next card to read

integer Debug; // Whether to print debug text


config_init() {

   // Initialize all configurable global variables here %%%
   // Don't initialize your configurable globals at their
   // declarations (above).  Doing it here provides default
   // behavior if the user deletes a config line from a notecard
   // rather than retaining the previous value.
   Foos = [];          // %%%
   Bar  = 20.0;        // %%%

}

// print the configuration, handy for debugging.

config_dump() {

   // Replace this example code with your config %%%.
   say("Foos: "       + llList2CSV(Foos));      // %%%
   say("FooVals: "    + llList2CSV(FooVals));   // %%%
   say("Bar: "        + (string) Bar);          // %%%     

}


// Example notecard line parser. // This function is called for each configuration line. // It treats all config notecards the same. // // For this example (which you can modify to suit your purposes) // Comments lines begin with a slash // Each line begins with a command followed by optional arguments // An Equals-sign ("=") separates command and arguments (and args from each other) // Unrecognized commands are ignored -- good for forwards-backwards notecard compatibility // cardName and lineNum are in case you want to print error messages. // This example doesn't allow spaces around the "=". If you want to allow // and ignore them, use this instead of ["="] in llParsStringKeepNulls(). // [" = ", " = ", " = ", " = ", " =", "= ", "="]

config_parse(string str, string cardName, integer lineNum) {

   str = llStringTrim(str, STRING_TRIM_HEAD);  // delete leading spaces
   // lines beginning with slash are comments -- ignore them
   if (llGetSubString(str,0,0) == "/") {
       return;
   }
   list ldata  = llParseStringKeepNulls(str, ["="], [""]);
   string cmd  = llList2String(ldata,0);
   string arg1 = llList2String(ldata,1);
   string arg2 = llList2String(ldata,2);
   // Process example commands -- replace this code with meaningful stuff! %%%
   if (cmd == "foo") {
       // another Foo configured: remember it
       Foos    += [arg1];
       FooVals += [(integer) arg2];
   } else if (cmd == "bar") {
       Bar = (float) arg1;
   }
       
   // this one is a good one to keep
   else if (cmd == "debug") {
       Debug = (integer) arg1;
   }

}


// Post-process any config, if necessary config_done() {

   if (Debug) {
       config_dump();
   }
   say("Config done");

}


// ==== Utilities ====

// Say something, in this case to owner (%%% modify to whisper or whatever)

say(string str) {

   llOwnerSay(str);

}


// Say something if debug is enabled debug(string str) {

   if (Debug) {
       say(llGetScriptName() + ": " + str);
   }

}

// Get the next notecard name, and // return TRUE if there is one

integer next_card() {

   if (ConfigCardIndex >= llGetListLength(ConfigCards)) {
       return (FALSE);
   }
   
   ConfigLineIndex = 0;
   ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);
   ConfigCardIndex++;
   ConfigRequestID = llGetNotecardLine(ConfigCardName, ConfigLineIndex);
   say("Reading " + ConfigCardName);
   return (TRUE);

} </lsl>

States:

<lsl> // Default state can do any init you need that doesn't require configuration.

default {

   state_entry() {
       llSetText("", <1.0,1.0,1.0>, 1.0);
       state s_config;
   }

}

// This state is only used to get into s_config, because going from // s_config to s_config won't redo it's state_entry. But we might // not want to redo anything we might have put in default state entry.

state s_reconfig {

   state_entry() {
       state s_config;
   }

}

// Read card config // Multiple notecard version - read all cards with the given extension

state s_config {

   state_entry() {
       config_init();
       string item;
       ConfigCards = [];
       integer n = llGetInventoryNumber(INVENTORY_NOTECARD);
       while (n-- > 0) {
           item = llGetInventoryName(INVENTORY_NOTECARD, n);
           if (llSubStringIndex(item, ConfigNotecardSuffix) != -1) {
               ConfigCards += [item];
           }
       }
       ConfigCardIndex = 0;
       if (next_card()) {
           llSetTimerEvent(ConfigTimeout);
       } else if (ConfigRequired) {
           say("Configuration notecard missing.");              
           state s_configRetry;
       } else {
           state s_active;
       }
   }
   dataserver(key query_id, string data) {
       if (query_id == ConfigRequestID) {
           if (data == EOF) {
               if (! next_card()) {
                   config_done();
                   state s_active;
               }
           } else {
               config_parse(data, ConfigCardName, ConfigLineIndex);
               ConfigRequestID = llGetNotecardLine(ConfigCardName, ++ConfigLineIndex);
               llSetTimerEvent(ConfigTimeout);
           }
       }
   }
   timer() {
       say("Dataserver time out: touch to retry");
       state s_configRetry;
   }
   on_rez(integer num) { state s_reconfig; }
   changed(integer change) {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_reconfig; }
   }
   state_exit() {
       llSetTimerEvent(0);
   }

}

state s_configRetry {

   touch_start(integer tot) {
       if (llDetectedKey(0) == llGetOwner()) {
           state s_config;
       }
   }
   
   changed(integer change) {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_config; }
   }

}

// State to go into if notecard is required but missing. // You can delete this and the code above that refers to it, // or just set ConfigurationRequired to FALSE. state s_unconfigured {

   state_entry() {
       llSetText("Configuration missing", <1.0,1.0,1.0>, 1.0);
   }
   changed(integer change) {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_reconfig; }
   }
   
   state_exit() {
       llSetText("", <1.0,1.0,1.0>, 1.0);
   }

}


// The fun starts here!

state s_active {

   // %%% Your code goes here!
   // Every state should usually have this, or something like it.
   changed(integer change) {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_reconfig; }
   }

} </lsl>