Allen Kerensky"It seems you've been living two lives ..."

Myriad Lite Region Settings Server

// Myriad_Lite_Region_Setting_Server-v0.0.0-20130917.lsl
// Copyright (c) 2012-2013 by Allen Kerensky (OSG/SL) All Rights Reserved.
// This work is dual-licensed under
// Creative Commons Attribution (CC BY) 3.0 Unported
// http://creativecommons.org/licenses/by/3.0/
// - or -
// Modified BSD License (3-clause)
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright notice, 
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
// * Neither the name of Myriad Lite nor the names of its contributors may be
//   used to endorse or promote products derived from this software without
//   specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
// NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The Myriad RPG System was designed, written, and illustrated by Ashok Desai
// Myriad RPG System licensed under:
// Creative Commons Attribution (CC BY) 2.0 UK: England and Wales
// http://creativecommons.org/licenses/by/2.0/uk/
 
// FIXME
// DONE Input string sanitation
// PARTIAL Access controls for players, builders, region owners, scripters
// Strength Length check before SAVE
// Finish emulating P5 stat server, test client messaging/functions
// Convert PPMA name/desc/text to SLDB key,[fields],[values] format
// Parcel ID for Rendezvous?
// Formalize request message formats
// Formalize return value formats
// Prebuild loadable modules to populate Power, Grace, Intellect, Spirit in 1 click
// Notecard Loader?
// How to detect OSSL Notecard save option without crashing
// Use PPMA as local cache but flush to SLDB backend?
// Use PPMA as local cache but flush to Silo backend?
// BACKUP() function to write persistent prim stuff to backing store (notecard, SLDB, silo?)
// RESTORE() function to load persistent prim stuff from backing store (notecard, SLDB, silo?)
// How to put this tool on the HUD for region owners to call up
// Teach Me Tutorial wizard to make and manage stats
// Make the entire tool able to be used in mouselook (chat inputs alongside drop downs)
// HTML server to serve game data out to HTTP clients?
 
// VERSION CONTROL
string VERSION = "0.0.0"; // Allen Kerensky's script version
string VERSIONDATE = "20130917"; // Allen Kerensky's script yyyymmdd
 
string KEY_NOT_FOUND="[KEY_NOT_FOUND]";
 
// Runtimes
integer FLAG_DEBUG; // if true, send DEBUG messages while running
integer RENDEZVOUS1 = -999; // the server well known channel to listen on for client requests
integer RENDEZVOUS2; // the server well known channel to listen on for client requests
integer HANDLE1; // the server listener handle, see llListen() wiki page.
integer HANDLE2; // the server listener handle, see llListen() wiki page.
 
//============================================================================
DEBUG(string msg) {
    if ( FLAG_DEBUG == TRUE ) llSay(DEBUG_CHANNEL,"DEBUG: script=["+llGetScriptName()+"] owner=["+llKey2Name(llGetOwner())+"] message=["+msg+"]");
}
 
//============================================================================
DELETE_RECORD(string dbkey, string field) {
    DEBUG("DELETE_RECORD DBKEY=["+dbkey+"] FIELD=["+field+"] BEGINS");
    // scan index or prim names for empty, get number for update
    integer i;
    string name;
    string desc;
    for ( i = 2; i <= llGetNumberOfPrims(); i++) {
        name = llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0);        
        desc = llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0);
        if ( name == dbkey && desc == field ) { // record found, delete
            llSetLinkPrimitiveParamsFast(i,[PRIM_NAME,"",PRIM_DESC,"",PRIM_TEXT,"",ZERO_VECTOR,0.0,PRIM_COLOR,ALL_SIDES,<1,0,0>,1.0]); // save to database
            DEBUG("DELETE_RECORD DBKEY=["+dbkey+"] FIELD=["+field+"] DELETED");
        }
    }
    DEBUG("DELETE_RECORD DBKEY=["+dbkey+"] FIELD=["+field+"] ENDS");
}
 
//============================================================================
DUMP_RECORDS() {
    DEBUG("DUMP_RECORDS BEGINS");
    // scan index or prim names for empty, get number for update
    integer i;
    string name = "";
    string desc = "";
    string text = "";
    for ( i = 2; i <= llGetNumberOfPrims(); i++) {
        name = llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0);
        desc = llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0);
        if ( name != "" && desc != "" ) {
            text = llList2String(llGetLinkPrimitiveParams(i,[PRIM_TEXT]),0);
            DEBUG("RECORD=["+(string)i+"] NAME=["+name+"] DESC=["+desc+"] TEXT=["+text+"]");
        }
    }
    DEBUG("DUMP_RECORDS ENDS");
}
 
//============================================================================
DUMP_TYPE(integer chan,string type) {
    DEBUG("DUMP_TYPES CHAN=["+(string)chan+"] TYPE=["+type+"] BEGINS");
    type = llToUpper(type);
    // scan index or prim names for empty, get number for update
    integer i;
    string name = "";
    string desc = "";
    string text = "";
    for ( i = 2; i <= llGetNumberOfPrims(); i++) {
        name = llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0);
        if ( name == type ) {
            desc = llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0);
            text = llList2String(llGetLinkPrimitiveParams(i,[PRIM_TEXT]),0);
            if ( type == "STATISTIC" ) llSay(chan,"STATISTIC|NAME="+desc+"|"+text);
            if ( type == "SKILL" ) llSay(chan,"SKILL|NAME="+desc+"|"+text);
            if ( type == "EFFECT" ) llSay(chan,"EFFECT|NAME="+desc+"|"+text);
            if ( type == "RESILIENCE" ) llSay(chan,"RESILIENCE|NAME="+desc+"|"+text);
            if ( type == "BOON" ) llSay(chan,"BOON|NAME="+desc+"|"+text);
            if ( type == "FLAW" ) llSay(chan,"FLAW|NAME="+desc+"|"+text);
            if ( type == "CAMPAIGN" ) llSay(chan,"CAMPAIGN|NAME="+desc+"|"+text);
            if ( type == "SPECIE" ) llSay(chan,"SPECIE|NAME="+desc+"|"+text);
            if ( type == "BACKGROUND" ) llSay(chan,"BACKGROUND|NAME="+desc+"|"+text);
            if ( type == "CAREER" ) llSay(chan,"CAREER|NAME="+desc+"|"+text);        
            if ( type == "ITEM" ) llSay(chan,"ITEM|NAME="+desc+"|"+text);
        }
    }
    DEBUG("DUMP_TYPE ENDS");
}
 
//============================================================================
ERASE_TYPE(string type) {
    DEBUG("ERASE TYPE ["+type+"] BEGINS");
    // scan index or prim names for empty, get number for update
    integer i;
    string name = "";
    string desc = "";
    for ( i = 2; i <= llGetNumberOfPrims(); i++) {
        name = llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0);
        if ( name == type ) {
            desc = llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0);
            DELETE_RECORD(type,desc);
        }
    }
    DEBUG("ERASE_TYPE ENDS");    
}
 
//============================================================================
FORMAT_DB() {
    DEBUG("FORMATTING DATABASE BEGINS");
    llSay(PUBLIC_CHANNEL,"FORMATTING DATABASE");
    integer i;
    for ( i = 2; i <= llGetNumberOfPrims(); i++ ) {
        if ( llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0) != "" ) {
            llSetLinkPrimitiveParamsFast(i,[PRIM_NAME,"",PRIM_DESC,"",PRIM_TEXT,"",ZERO_VECTOR,0.0,PRIM_COLOR,ALL_SIDES,<1,0,0>,1.0]);
        }
    }
    DEBUG("DATABASE FORMAT ENDS");
    llSay(PUBLIC_CHANNEL,"DATABASE FORMAT ENDS");
}
 
//============================================================================
string GET_VAL(string dbkey,string field) {
    DEBUG("GET_VAL DBKEY=["+dbkey+"] FIELD=["+field+"]");
    string out = KEY_NOT_FOUND;
    integer i;
    string name;
    string desc;
    for ( i = 2; i <= llGetNumberOfPrims(); i++ ) {
        name = llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0);
        desc = llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0);
        if ( llToLower(name) == llToLower(dbkey) && llToLower(desc) == llToLower(field) ) {
            out = llList2String(llGetLinkPrimitiveParams(i,[PRIM_TEXT]),0);
            DEBUG("GET_VAL RETURN=["+out+"]");
            return out; // bail on first match?
        }
    }
    DEBUG("GET_VAL RETURN=["+out+"]");
    return out;
}
 
//============================================================================
LIST_TYPE(integer chan,string type) {
    DEBUG("LIST_TYPE CHAN=["+(string)chan+"] TYPE=["+type+"] BEGINS");
    type = llToUpper(type);
    // scan index or prim names for empty, get number for update
    integer i;
    string name = "";
    string desc = "";
    list out = [];
    for ( i = 2; i <= llGetNumberOfPrims(); i++) {
        name = llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0);
        if ( name == type ) {
            desc = llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0);
            out = out + [desc];
        }
    }
    out = llListSort(out,1,TRUE);
    if ( type == "STATISTIC" ) llSay(chan,"STATISTICS|"+llList2CSV(out));
    if ( type == "SKILL" ) llSay(chan,"SKILLS|"+llList2CSV(out));
    if ( type == "EFFECT" ) llSay(chan,"EFFECTS|"+llList2CSV(out));
    if ( type == "RESILIENCE" ) llSay(chan,"RESILIENCES|"+llList2CSV(out));
    if ( type == "BOON" ) llSay(chan,"BOONS|"+llList2CSV(out));
    if ( type == "FLAW" ) llSay(chan,"FLAWS|"+llList2CSV(out));
    if ( type == "CAMPAIGN" ) llSay(chan,"CAMPAIGNS|"+llList2CSV(out));
    if ( type == "SPECIE" ) llSay(chan,"SPECIES|"+llList2CSV(out));
    if ( type == "BACKGROUND" ) llSay(chan,"BACKGROUNDS|"+llList2CSV(out));
    if ( type == "CAREER" ) llSay(chan,"CAREERS|"+llList2CSV(out));
    if ( type == "ITEM" ) llSay(chan,"ITEMS|"+llList2CSV(out));
    DEBUG("LIST_TYPE CHAN=["+(string)chan+"] TYPE=["+type+"] ENDS");
}
 
//============================================================================
SET_VAL(string dbkey, string field, string val) {
    DEBUG("SET_VAL DBKEY=["+dbkey+"] FIELD=["+field+"] VAL=["+val+"] BEGINS");
    integer i;
    string name;
    string desc;
    integer written = FALSE;
    for ( i = 2; i <= llGetNumberOfPrims(); i++ ) {
        name = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0),STRING_TRIM);
        desc = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0),STRING_TRIM);
        if ( ( llToLower(name) == llToLower(dbkey) && llToLower(desc) == llToLower(field) ) && written == FALSE ) {
            DEBUG("SET_VAL UPDATE RECORD=["+(string)i+"] DBKEY=["+dbkey+"] DESC=["+field+"] VAL=["+val+"]");
            llSetLinkPrimitiveParamsFast(i,[PRIM_NAME,dbkey,PRIM_DESC,field,PRIM_TEXT,val,ZERO_VECTOR,0.0,PRIM_COLOR,ALL_SIDES,<0,1,0>,1.0]);
            written = TRUE; // we did an update, remember it
        }    
    }
    if ( written == TRUE ) {
        DEBUG("SET_VAL UPDATE COMPLETE.");
        return;
    }
    // data hasn't been written, scan for free prim
    for ( i = 2; i <= llGetNumberOfPrims(); i++ ) {
        name = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_NAME]),0),STRING_TRIM);
        desc = llStringTrim(llList2String(llGetLinkPrimitiveParams(i,[PRIM_DESC]),0),STRING_TRIM);
        if ( ( name == "" && desc == "" ) && written == FALSE ) {
            DEBUG("SET_VAL INSERT RECORD=["+(string)i+"] DBKEY=["+dbkey+"] DESC=["+field+"] VAL=["+val+"]");
            llSetLinkPrimitiveParamsFast(i,[PRIM_NAME,dbkey,PRIM_DESC,field,PRIM_TEXT,val,ZERO_VECTOR,0.0,PRIM_COLOR,ALL_SIDES,<0,1,0>,1.0]);
            written = TRUE; // we did an update, remember it
        }
    }
    if ( written == TRUE ) {
        DEBUG("SET_VAL INSERT COMPLETED.");
        return;
    }
    DEBUG("SET_VAL NO FREE RECORD FOUND TO INSERT INTO! DATA LOST! ENDS");
}
 
string STRING_FILTER(string in) {
    DEBUG("STRING_FILTER IN=["+in+"]");
    list charset = llCSV2List(" ,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z");
    string out = "";
    integer i;
    for ( i = 0; i < llStringLength(in); i++) {
        string char = llGetSubString(in,i,i);
        if ( llListFindList(charset,[char]) >= 0 ) {
            out += char;
        } else {
            out += " ";
        }
    }
    DEBUG("STRING_FILTER OUT=["+out+"]");
    return llStringTrim(out,STRING_TRIM);
}
 
string INTEGER_FILTER(string in) {
    DEBUG("INTEGER FILTER_FILTER IN=["+in+"]");
    list numbers = llCSV2List("1,2,3,4,5,6,7,8,9,0");
    string out = "";
    integer i;
    for ( i = 0; i < llStringLength(in); i++) {
        string char = llGetSubString(in,i,i);
        if ( llListFindList(numbers,[char]) >= 0 ) {
            out += char;
        } else {
            out += " ";
        }
    }
    DEBUG("INTEGER_FILTER OUT=["+out+"]");
    return llStringTrim(out,STRING_TRIM);    
}
 
//============================================================================
default {
 
    //------------------------------------------------------------------------
    state_entry() {
        FLAG_DEBUG=FALSE; // set default here to keep LSLint from complaining about the constant
        DEBUG("STATE_ENTRY BEGINS");
 
        // SETUP RENDEZVOUS1 CHANNEL (STATIC) - backward compatible for Preview 6 and earlier
        if ( HANDLE1 != 0 ) llListenRemove(HANDLE1);
        HANDLE1 = llListen(RENDEZVOUS1,"",NULL_KEY,"");
        llSay(PUBLIC_CHANNEL,"Rendezvous1 channel is "+(string)RENDEZVOUS1);
 
        // SETUP RENDEZVOUS2 CHANNEL (DYNAMIC) - new for Preview 7 and later
        list details = llGetParcelDetails(<0,0,0>,[PARCEL_DETAILS_ID]);
        string parcelid = llList2String(details,0);
        RENDEZVOUS2 = (integer)("0x"+llGetSubString(parcelid,0,7));
        if ( HANDLE2 != 0 ) llListenRemove(HANDLE2);
        HANDLE2 = llListen(RENDEZVOUS2,"",NULL_KEY,"");
        llSay(PUBLIC_CHANNEL,"Rendezvous2 channel is "+(string)RENDEZVOUS2);
 
        // Ready to serve...
        llSay(PUBLIC_CHANNEL,llGetScriptName()+" (version "+VERSION+"-"+VERSIONDATE+") ready.");
        DEBUG("STATE_ENTRY ENDS");
    }
 
    //------------------------------------------------------------------------
    listen(integer channel,string speaker,key id,string msg) {
        DEBUG("LISTEN channel=["+(string)channel+"] speaker=["+speaker+"] id=["+(string)id+"] msg=["+msg+"] BEGINS");
 
        integer replychannel = (integer)("0x"+llGetSubString(id,0,6)); // calculate dynamic channel of whoever talked to us so we can reply directly
 
        // Security Note - ALL users can *read* from database, only regeion owner can WRITE/CHANGE database
 
        //
        // DUMP FUNCTIONS to PUBLIC CHANNEL
        //
        if ( msg == "DUMP_RECORDS") { DUMP_RECORDS(); return; }
        if ( msg == "DUMP_STATISTICS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"STATISTIC"); return;}
        if ( msg == "DUMP_SKILLS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"SKILL"); return; }
        if ( msg == "DUMP_EFFECTS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"EFFECT"); return; }
        if ( msg == "DUMP_RESILIENCES" ) { DUMP_TYPE(PUBLIC_CHANNEL,"RESILIENCE"); return; }
        if ( msg == "DUMP_BOONS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"BOON"); return; }
        if ( msg == "DUMP_FLAWS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"FLAW"); return; }
        if ( msg == "DUMP_CAMPAIGNS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"CAMPAIGN"); return; }
        if ( msg == "DUMP_SPECIES" ) { DUMP_TYPE(PUBLIC_CHANNEL,"SPECIE"); return; }
        if ( msg == "DUMP_BACKGROUNDS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"BACKGROUND"); return; }
        if ( msg == "DUMP_CAREERS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"CAREER"); return; }
        if ( msg == "DUMP_ITEMS" ) { DUMP_TYPE(PUBLIC_CHANNEL,"ITEM"); return; }
 
        //
        // LIST FUNCTIONS
        //
        if ( msg == "LIST_STATISTICS" ) { LIST_TYPE(replychannel,"STATISTIC"); return;}
        if ( msg == "LIST_SKILLS" ) { LIST_TYPE(replychannel,"SKILL"); return;}
        if ( msg == "LIST_EFFECTS" ) { LIST_TYPE(replychannel,"EFFECT"); return;}
        if ( msg == "LIST_RESILIENCES" ) { LIST_TYPE(replychannel,"RESILIENCE"); return;}
        if ( msg == "LIST_BOONS" ) { LIST_TYPE(replychannel,"BOON"); return;}
        if ( msg == "LIST_FLAWS" ) { LIST_TYPE(replychannel,"FLAW"); return;}
        if ( msg == "LIST_CAMPAIGNS" ) { LIST_TYPE(replychannel,"CAMPAIGN"); return;}
        if ( msg == "LIST_SPECIES" ) { LIST_TYPE(replychannel,"SPECIE"); return;}
        if ( msg == "LIST_BACKGROUNDS" ) { LIST_TYPE(replychannel,"BACKGROUND"); return;}
        if ( msg == "LIST_CAREERS" ) { LIST_TYPE(replychannel,"CAREER"); return;}
        if ( msg == "LIST_ITEMS" ) { LIST_TYPE(replychannel,"ITEM"); return;}
 
        //
        // GET FUNCTIONS        
        //
        if ( llGetSubString(msg,0,13) == "GET_STATISTIC|" ) {
            string aname = llStringTrim(llGetSubString(msg,14,-1),STRING_TRIM);
            string stat = GET_VAL("STATISTIC",aname);
            if ( stat == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required statistic ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"STATISTIC|NAME="+aname+"|"+stat);
            return;
        }
        if ( llGetSubString(msg,0,9) == "GET_SKILL|" ) {
            string aname = llStringTrim(llGetSubString(msg,10,-1),STRING_TRIM);
            string skill = GET_VAL("SKILL",aname);
            if ( skill == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required skill ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"SKILL|NAME="+aname+"|"+skill);
            return;
        }
        if ( llGetSubString(msg,0,10) == "GET_EFFECT|" ) {
            string aname = llStringTrim(llGetSubString(msg,11,-1),STRING_TRIM);
            string effect = GET_VAL("EFFECT",aname);
            if ( effect == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required effect ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"EFFECT|NAME="+aname+"|"+effect);
            return;
        }
        if ( llGetSubString(msg,0,14) == "GET_RESILIENCE|" ) {
            string aname = llStringTrim(llGetSubString(msg,15,-1),STRING_TRIM);
            string resilience = GET_VAL("RESILIENCE",aname);
            if ( resilience == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required resilience ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"RESILIENCE|NAME="+aname+"|"+resilience);
            return;
        }
        if ( llGetSubString(msg,0,8) == "GET_BOON|" ) {
            string aname = llStringTrim(llGetSubString(msg,9,-1),STRING_TRIM);
            string boon = GET_VAL("BOON",aname);
            if ( boon == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required boon ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"BOON|NAME="+aname+"|"+boon);
            return;
        }
        if ( llGetSubString(msg,0,8) == "GET_FLAW|" ) {
            string aname = llStringTrim(llGetSubString(msg,9,-1),STRING_TRIM);
            string flaw = GET_VAL("FLAW",aname);
            if ( flaw == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required flaw ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"FLAW|NAME="+aname+"|"+flaw);
            return;
        }
        if ( llGetSubString(msg,0,12) == "GET_CAMPAIGN|" ) {
            string aname = llStringTrim(llGetSubString(msg,13,-1),STRING_TRIM);
            string campaign = GET_VAL("CAMPAIGN",aname);
            if ( campaign == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required campaign ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"CAMPAIGN|NAME="+aname+"|"+campaign);
            return;
        }
        if ( llGetSubString(msg,0,10) == "GET_SPECIE|" ) {
            string aname = llStringTrim(llGetSubString(msg,11,-1),STRING_TRIM);
            string specie = GET_VAL("SPECIE",aname);
            if ( specie == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required specie ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"SPECIE|NAME="+aname+"|"+specie);
            return;
        }
        if ( llGetSubString(msg,0,14) == "GET_BACKGROUND|" ) {
            string aname = llStringTrim(llGetSubString(msg,15,-1),STRING_TRIM);
            string background = GET_VAL("BACKGROUND",aname);
            if ( background == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required background ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"BACKGROUND|NAME="+aname+"|"+background);
            return;
        }
        if ( llGetSubString(msg,0,10) == "GET_CAREER|" ) {
            string aname = llStringTrim(llGetSubString(msg,11,-1),STRING_TRIM);
            string career = GET_VAL("CAREER",aname);
            if ( career == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required career ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"CAREER|NAME="+aname+"|"+career);
            return;
        }
        if ( llGetSubString(msg,0,8) == "GET_ITEM|" ) {
            string aname = llStringTrim(llGetSubString(msg,9,-1),STRING_TRIM);
            string item = GET_VAL("ITEM",aname);
            if ( item == KEY_NOT_FOUND ) {
                llRegionSay(replychannel,"RESPONSE=ERROR|Required item ["+aname+"] not found in table.");
                return;
            }
            llRegionSay(replychannel,"ITEM|NAME="+aname+"|"+item);
            return;
        }        
 
        //
        // REGION OWNER CHECK - REGION OWNER COMMANDS ONLY BELOW THIS POINT
        //
        if ( llGetOwner() != llList2Key(llGetObjectDetails(id,[OBJECT_OWNER]),0) ) {
            string err = "RESPONSE=ERROR|Region owner only function.";
            llRegionSay(replychannel,err);
            DEBUG(err);
            return;
        }
        //
        // ERASE FUNCTIONS - deletes all records of a type
        //        
        if ( msg == "FORMAT_DB" ) { FORMAT_DB(); return; }        
        if ( msg == "ERASE_STATISTICS" ) { ERASE_TYPE("STATISTIC"); return; }
        if ( msg == "ERASE_SKILLS" ) { ERASE_TYPE("SKILL"); return; }        
        if ( msg == "ERASE_EFFECTS" ) { ERASE_TYPE("EFFECT"); return; }        
        if ( msg == "ERASE_RESILIENCES" ) { ERASE_TYPE("RESILIENCE"); return; }        
        if ( msg == "ERASE_BOONS" ) { ERASE_TYPE("BOON"); return; }        
        if ( msg == "ERASE_FLAWS" ) { ERASE_TYPE("FLAW"); return; }        
        if ( msg == "ERASE_CAMPAIGNS" ) { ERASE_TYPE("CAMPAIGN"); return; }        
        if ( msg == "ERASE_SPECIES" ) { ERASE_TYPE("SPECIE"); return; }        
        if ( msg == "ERASE_BACKGROUNDS" ) { ERASE_TYPE("BACKGROUND"); return; }        
        if ( msg == "ERASE_CAREERS" ) { ERASE_TYPE("CAREER"); return; }        
        if ( msg == "ERASE_ITEMS" ) { ERASE_TYPE("ITEM"); return; }        
 
        // 
        // DELETE FUNCTIONS - delete one record of a type
        //
        if ( llGetSubString(msg,0,16) == "DELETE_STATISTIC|" ) {
            string aname = llStringTrim(llGetSubString(msg,17,-1),STRING_TRIM);
            DELETE_RECORD("STATISTIC",aname);
            LIST_TYPE(replychannel,"STATISTIC");
            return;
        }
        if ( llGetSubString(msg,0,12) == "DELETE_SKILL|" ) {
            string aname = llStringTrim(llGetSubString(msg,13,-1),STRING_TRIM);
            DELETE_RECORD("SKILL",aname);
            LIST_TYPE(replychannel,"SKILL");
            return;
        }
        if ( llGetSubString(msg,0,13) == "DELETE_EFFECT|" ) {
            string aname = llStringTrim(llGetSubString(msg,14,-1),STRING_TRIM);
            DELETE_RECORD("EFFECT",aname);
            LIST_TYPE(replychannel,"EFFECT");
            return;
        }
        if ( llGetSubString(msg,0,17) == "DELETE_RESILIENCE|" ) {
            string aname = llStringTrim(llGetSubString(msg,18,-1),STRING_TRIM);
            DELETE_RECORD("RESILIENCE",aname);
            LIST_TYPE(replychannel,"RESILIENCE");
            return;
        }
        if ( llGetSubString(msg,0,11) == "DELETE_BOON|" ) {
            string aname = llStringTrim(llGetSubString(msg,12,-1),STRING_TRIM);
            DELETE_RECORD("BOON",aname);
            LIST_TYPE(replychannel,"BOON");
            return;
        }
        if ( llGetSubString(msg,0,11) == "DELETE_FLAW|" ) {
            string aname = llStringTrim(llGetSubString(msg,12,-1),STRING_TRIM);
            DELETE_RECORD("FLAW",aname);
            LIST_TYPE(replychannel,"FLAW");
            return;
        }
        if ( llGetSubString(msg,0,15) == "DELETE_CAMPAIGN|" ) {
            string aname = llStringTrim(llGetSubString(msg,16,-1),STRING_TRIM);
            DELETE_RECORD("CAMPAIGN",aname);
            LIST_TYPE(replychannel,"CAMPAIGN");
            return;
        }
        if ( llGetSubString(msg,0,13) == "DELETE_SPECIE|" ) {
            string aname = llStringTrim(llGetSubString(msg,14,-1),STRING_TRIM);
            DELETE_RECORD("SPECIE",aname);
            LIST_TYPE(replychannel,"SPECIE");
            return;
        }
        if ( llGetSubString(msg,0,17) == "DELETE_BACKGROUND|" ) {
            string aname = llStringTrim(llGetSubString(msg,18,-1),STRING_TRIM);
            DELETE_RECORD("BACKGROUND",aname);
            LIST_TYPE(replychannel,"BACKGROUND");
            return;
        }
        if ( llGetSubString(msg,0,13) == "DELETE_CAREER|" ) {
            string aname = llStringTrim(llGetSubString(msg,14,-1),STRING_TRIM);
            DELETE_RECORD("CAREER",aname);
            LIST_TYPE(replychannel,"CAREER");
            return;
        }
        if ( llGetSubString(msg,0,11) == "DELETE_ITEM|" ) {
            string aname = llStringTrim(llGetSubString(msg,12,-1),STRING_TRIM);
            DELETE_RECORD("ITEM",aname);
            LIST_TYPE(replychannel,"ITEM");
            return;
        }
 
        //
        // CREATE FUNCTIONS
        //                
        list tokens = llParseString2List(msg,["|"],[]);
        integer tokencount = llGetListLength(tokens);                
 
        // process each attrib=value pair
        integer i;
        string request;
        string aspect;
        string name;
        string generator;
        string updater;
        string statmin;
        string statmax;
        string skillmin;
        string skillmax;
        string summary;
        string description;
        string genres;
        string type;
        string activation;
        string actionlist;
        string basestat;
        string action;
        string gpperlevel;
        string boonmax;
        string flawmax;
        string statpool;
        string skillpool;
        string perskills;
        string sfxpool;
        string healthpool;
        string gppool;
        string resources;
        string gpcost;
        string statlist;
        string boonlist;
        string flawlist;
        string skilllist;
        string sfxlist;
        string itemlist;
        string range;
        string damage;
        string rpcost;
        string period;
        string bonus;
        string rating;
        //list fields = []; // FIXME finish SLDB compatibility
        //list values = []; // FIXME finish SLDB compatibility
        for ( i = 0; i <= tokencount; i++) {
            string currenttoken = llList2String(tokens,i);
            list attribvalpair = llParseString2List(currenttoken,["="],[]);
            string attrib = llToLower(llList2String(attribvalpair,0));
            string sdata = llList2String(attribvalpair,1);
            //integer idata = llList2Integer(attribvalpair,1);
            if ( attrib == "request" ) request = sdata;
            if ( attrib == "aspect" ) aspect = sdata;
            if ( attrib == "name" ) name = sdata;
            if ( attrib == "generator" ) generator = sdata;
            if ( attrib == "updater" ) updater = sdata;
            if ( attrib == "statmin" ) statmin = sdata;
            if ( attrib == "statmax" ) statmax = sdata;
            if ( attrib == "skillmin" ) skillmin = sdata;
            if ( attrib == "skillmax" ) skillmax = sdata;
            if ( attrib == "summary" ) summary = sdata;
            if ( attrib == "description" ) description = sdata;
            if ( attrib == "genres" ) genres = sdata;
            if ( attrib == "type" ) type = sdata;
            if ( attrib == "activation" ) activation = sdata;
            if ( attrib == "actionlist" ) actionlist = sdata;
            if ( attrib == "basestat" ) basestat = sdata;
            if ( attrib == "action" ) action = sdata;
            if ( attrib == "gpperlevel" ) gpperlevel = sdata;
            if ( attrib == "boonmax" ) boonmax = sdata;
            if ( attrib == "flawmax" ) flawmax = sdata;
            if ( attrib == "statpool" ) statpool = sdata;
            if ( attrib == "skillpool" ) skillpool = sdata;
            if ( attrib == "perskills" ) perskills = sdata;
            if ( attrib == "sfxpool" ) sfxpool = sdata;
            if ( attrib == "healthpool" ) healthpool = sdata;
            if ( attrib == "gppool" ) gppool = sdata;
            if ( attrib == "resources" ) resources = sdata;
            if ( attrib == "gpcost" ) gpcost = sdata;
            if ( attrib == "statlist" ) statlist = sdata;
            if ( attrib == "boonlist" ) boonlist = sdata;
            if ( attrib == "flawlist" ) flawlist = sdata;
            if ( attrib == "skilllist" ) skilllist = sdata;
            if ( attrib == "sfxlist" ) sfxlist = sdata;
            if ( attrib == "itemlist" ) itemlist = sdata;
            if ( attrib == "range" ) range = sdata;
            if ( attrib == "damage" ) damage = sdata;
            if ( attrib == "rpcost" ) rpcost = sdata;
            if ( attrib == "period" ) period = sdata;
            if ( attrib == "bonus" ) bonus = sdata;                
            if ( attrib == "rating" ) rating = sdata;
 
            //fields = fields + [attrib]; // FIXME finish SLDB compatibility
            //values = values + [sdata]; // FIXME finish SLDB compatibility
        }
 
        // PROCESS Object now that we've broken down the message
        // REQUEST=CREATE|ASPECT=Statistic|NAME=Power|GENERATOR=Points|UPDATER=Level|SUMMARY=physical strength and resilience|DESCRIPTION=FIXME
        // CREATE,Statistic,Power,Points,Levels,physical strenghth and resilience,FIXME
        // { request: "create", aspect: "Statistic", name: "Power", generator: "Points", updater: "Level", summary: "stuff here", description: "blah blah" }
        // support alternate CREATE_<ASPECT> message format
        if ( llGetSubString(msg,0,6) == "CREATE_" ) {
            request = "CREATE";
            aspect = llGetSubString(llList2String(tokens,0),7,-1);
        }
 
        if ( request == "CREATE" ) {
            // 0. validate speaker id is region owner or object owned by region owner
            if ( llGetOwner() != llList2Key(llGetObjectDetails(id,[OBJECT_OWNER]),0) ) { // ignore create requests from IDs not owned by this server's owner
                string err = "RESPONSE=ERROR|Region owner only function.";
                llRegionSay(replychannel,err);
                DEBUG(err);
                return;
            }
            // 1. validate aspect
            list tmpaspects = ["STATISTIC","SKILL","EFFECT","RESILIENCE","BOON","FLAW","CAMPAIGN","SPECIE","BACKGROUND","CAREER","ITEM"];
            if ( llListFindList(tmpaspects,[aspect]) == -1 ) {
                string err = "RESPONSE=ERROR|Invalid aspect value ["+aspect+"]";
                llRegionSay(replychannel,err);
                DEBUG(err);
                return;
            }
            if ( llStringLength(aspect) > 63 ) { // max length 63 fits in prim name field
                string err = "RESPONSE=ERROR|ASPECT name ["+aspect+"] too long. Shorten to 63 characters or less.";
                llRegionSay(replychannel,err);
                DEBUG(err);
                return;
            }
            // 2. validate name
            name = STRING_FILTER(name);
            if ( name == "" || llStringLength(name) < 3 || llStringLength(name) > 127 ) { // max length 127 fits in prim description field
                string err = "RESPONSE=ERROR|Invalid name value ["+name+"]";
                llRegionSay(replychannel,err);
                DEBUG(err);
                return;
            }
            // 3. validate generator
            if ( aspect == "STATISTIC" || aspect == "SKILL" ) {
                if ( generator != "POINTBUY" && generator != "TEMPLATE" && generator != "RANDOM" ) {
                    string err = "RESPONSE=ERROR|Invalid generator value ["+generator+"]";
                    llRegionSay(replychannel,err);
                    DEBUG(err);
                    return;
                }
            }
            // 4. validate updater
            if ( aspect == "STATISTIC" || aspect == "SKILL" ) {
                if ( updater != "RANDOM" && updater != "GRADUAL" && updater != "LEVEL" ) {
                    string err = "RESPONSE=ERROR|Invalid updater value ["+updater+"]";
                    llRegionSay(replychannel,err);
                    DEBUG(err);
                    return;
                }
            }
            if ( aspect == "STATISTIC" ) {
                // 5. validate statmin
                statmin = INTEGER_FILTER(statmin);
                if ( (integer)statmin < 0 || (integer)statmin > 100 ) { // FIXME what is a realistic minimum?
                    string err = "RESPONSE=ERROR|Invalid statistic minimum value ["+statmin+"]";
                    llRegionSay(replychannel,err);
                    DEBUG(err);
                    return;
                }
                // 6. validate statmax
                statmax = INTEGER_FILTER(statmax);
                if ( (integer)statmax < 0 || (integer)statmax > 100 || (integer)statmin > (integer)statmax ) { // FIXME what is a realistic maximum?
                    string err = "RESPONSE=ERROR|Invalid statistic maximum value ["+statmax+"]";
                    llRegionSay(replychannel,err);
                    DEBUG(err);
                    return;
                }
            }
            if ( aspect == "SKILL" ) {
                // 7. validate skillmin
                skillmin = INTEGER_FILTER(skillmin);
                if ( (integer)skillmin < 0 || (integer)skillmin > 100 ) { // FIXME what is a realistic minimum?
                    string err = "RESPONSE=ERROR|Invalid skill minimum value ["+skillmin+"]";
                    llRegionSay(replychannel,err);
                    DEBUG(err);
                    return;
                }
                // 8. validate statmax
                skillmax = INTEGER_FILTER(skillmax);
                if ( (integer)skillmax < 0 || (integer)skillmax > 100 || (integer)skillmin > (integer)skillmax ) { // FIXME what is a realistic maximum?
                    string err = "RESPONSE=ERROR|Invalid skill maximum value ["+skillmax+"]";
                    llRegionSay(replychannel,err);
                    DEBUG(err);
                    return;
                }
            }
            // 9. validate summary
            if ( aspect == "STATISTIC" || aspect == "SKILL" ) {
                summary = STRING_FILTER(summary);
                if ( summary == "" || llStringLength(summary) < 3 || llStringLength(summary) > 63 ) { // FIXME what is a realistic max length?
                    string err = "RESPONSE=ERROR|Invalid summary value ["+summary+"]";
                    llRegionSay(replychannel,err);
                    DEBUG(err);
                    return;
                }
            }
            // 10. validate description
            if ( aspect == "STATISTIC" || aspect == "SKILL" ) {
                description = STRING_FILTER(description);
                if ( description == "" || llStringLength(description) < 3 || llStringLength(description) > 127 ) { // FIXME what is a realistic max length?
                    string err = "RESPONSE=ERROR|Invalid description value ["+description+"]";
                    llRegionSay(replychannel,err);
                    DEBUG(err);
                    return;
                }
            }
            // 7. create new or update existing
            string record ="";
            if ( aspect == "STATISTIC" )  record = "GENERATOR="+generator+"|UPDATER="+updater+"|STATMIN="+statmin+"|STATMAX="+statmax+"|SUMMARY="+summary+"|DESCRIPTION="+description; 
            if ( aspect == "SKILL" )      record = "GENERATOR="+generator+"|UPDATER="+updater+"|SKILLMIN="+skillmin+"|SKILLMAX="+skillmax+"|SUMMARY="+summary+"|GENRES="+genres+"|DESCRIPTION="+description;
            if ( aspect == "EFFECT" )     record = "TYPE="+type+"|ACTIVATION="+activation+"|ACTIONLIST="+actionlist+"|DESCRIPTION="+description;
            if ( aspect == "RESILIENCE" ) record = "BASESTAT="+basestat+"|TYPE="+type+"|ACTION="+action+"|DESCRIPTION="+description;
            if ( aspect == "BOON" )       record = "TYPE="+type+"|GPPERLEVEL="+gpperlevel+"|BOONMAX="+boonmax+"|DESCRIPTION="+description;
            if ( aspect == "FLAW" )       record = "TYPE="+type+"|GPPERLEVEL="+gpperlevel+"|FLAWMAX="+flawmax+"|DESCRIPTION="+description;
            if ( aspect == "CAMPAIGN" )   record = "STATPOOL="+statpool+"|SKILLPOOL="+skillpool+"|PERSKILLS="+perskills+"|SFXPOOL="+sfxpool+"|HEALTHPOOL="+healthpool+"|GPPOOL="+gppool+"|STATMAX="+statmax+"|SKILLMAX="+skillmax+"|RESOURCES="+resources;
            if ( aspect == "SPECIE" )     record = "GPCOST="+gpcost+"|STATLIST="+statlist+"|BOONLIST="+boonlist+"|FLAWLIST="+flawlist+"|SFXLIST="+sfxlist+"|SKILLLIST="+skilllist+"|ITEMLIST="+itemlist+"|DESCRIPTION="+description;
            if ( aspect == "BACKGROUND" ) record = "GPCOST="+gpcost+"|STATLIST="+statlist+"|BOONLIST="+boonlist+"|FLAWLIST="+flawlist+"|SFXLIST="+sfxlist+"|SKILLLIST="+skilllist+"|ITEMLIST="+itemlist+"|DESCRIPTION="+description;
            if ( aspect == "CAREER" )     record = "GPCOST="+gpcost+"|STATLIST="+statlist+"|BOONLIST="+boonlist+"|FLAWLIST="+flawlist+"|SFXLIST="+sfxlist+"|SKILLLIST="+skilllist+"|ITEMLIST="+itemlist+"|DESCRIPTION="+description;
            if ( aspect == "ITEM" )       record = "TYPE="+type+"|RANGE="+range+"|DAMAGE="+damage+"|RPCOST="+rpcost+"|PERIOD="+period+"|BONUS="+bonus+"|RATING="+rating+"|ACTIVATION="+activation+"|ACTIONLIST="+actionlist+"|DESCRIPTION="+description;
 
            if ( llStringLength(record) > 254 ) { // max length 254 fits in prim text field
                string err = "RESPONSE=ERROR|Total record length too long. Shorter summary or description may help.";
                llRegionSay(replychannel,err);
                DEBUG(err);
            }
            SET_VAL(llToUpper(aspect),name,record);
            // 8. notify region of new record and list
            if ( aspect == "STATISTIC" ) { LIST_TYPE(replychannel,"STATISTIC"); LIST_TYPE(PUBLIC_CHANNEL,"STATISTIC"); }
            if ( aspect == "SKILL" ) { LIST_TYPE(replychannel,"SKILL"); LIST_TYPE(PUBLIC_CHANNEL,"SKILL"); }
            if ( aspect == "EFFECT" ) { LIST_TYPE(replychannel,"EFFECT"); LIST_TYPE(PUBLIC_CHANNEL,"EFFECT"); }
            if ( aspect == "RESILIENCE" ) { LIST_TYPE(replychannel,"RESILIENCE"); LIST_TYPE(PUBLIC_CHANNEL,"RESILIENCE"); }
            if ( aspect == "BOON" ) { LIST_TYPE(replychannel,"BOON"); LIST_TYPE(PUBLIC_CHANNEL,"BOON"); }
            if ( aspect == "FLAW" ) { LIST_TYPE(replychannel,"FLAW"); LIST_TYPE(PUBLIC_CHANNEL,"FLAW"); }
            if ( aspect == "CAMPAIGN" ) { LIST_TYPE(replychannel,"CAMPAIGN"); LIST_TYPE(PUBLIC_CHANNEL,"CAMPAIGN"); }
            if ( aspect == "SPECIE" ) { LIST_TYPE(replychannel,"SPECIE"); LIST_TYPE(PUBLIC_CHANNEL,"SPECIE"); }
            if ( aspect == "BACKGROUND" ) { LIST_TYPE(replychannel,"BACKGROUND"); LIST_TYPE(PUBLIC_CHANNEL,"BACKGROUND"); }
            if ( aspect == "CAREER" ) { LIST_TYPE(replychannel,"CAREER"); LIST_TYPE(PUBLIC_CHANNEL,"CAREER"); }
            if ( aspect == "ITEM" ) { LIST_TYPE(replychannel,"ITEM"); LIST_TYPE(PUBLIC_CHANNEL,"ITEM"); }
            return;
        }
 
        DEBUG("LISTEN ENDS");
    }    
}
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
DokuWiki