====== 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_ 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"); } }