AdvancedNotecardReader

// Multi-file configuration, v1a
// Lear Cale, 2007
// Public Domain. Feel free to delete header comments.
// This script reads all configuration notecards that match a given suffix,
// storing the settings in 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 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.
// Replace these lines with your globals and try to save: you'll get
// errors in the places you need to edit.
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().
// [" = ", " = ", " = ", " = ", " =", "= ", "="]
//
// Example config file contents:
// foo=Lowell George=1979
// foo=Freddy Murcury=1991
// foo=Robert Johnson=1938
// bar=1.5
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);
 // %%% Add more lines such as the above as needed for more arguments.
 // %%% 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)) {
 ConfigCards = [];
 return (FALSE);
 }
 
 ConfigLineIndex = 0;
 ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);
 ConfigCardIndex++;
 ConfigRequestID = llGetNotecardLine(ConfigCardName, ConfigLineIndex);
 say("Reading " + ConfigCardName);
 return (TRUE);
}
States:
// 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);
 // Note: for simplicity, read cards with the "suffix" anywhere in the name
 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; }
 }
}
Category:LSL Library