AdvancedNotecardReader

From Second Life Wiki
Revision as of 19:40, 24 January 2015 by ObviousAltIsObvious Resident (talk | contribs) (<lsl> tag to <source>)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
// 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; }
    }
}