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