Don't click here unless you want to be banned.

LSL Wiki : LibraryCBInboundDataParser

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are ia360925.us.archive.org
// InboundDataParser
// This script is responsible for parsing data sent from the server
// via inbound XMLRPC. It invokes received* events to convey this 
// data to other modules in the object.

// Copyright (C) 2005-2006  Francisco V. Saldana
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
// 
// Francisco V. Saldana can be contacted using his email account 
//  username: dressedinblue, at domain: gmail.com
// and in Second Life by IMming Christopher Omega


list replaceListSlice(list dest, list src, integer start) {
    integer srcLen = llGetListLength(src);
    return llListInsertList(llDeleteSubList(dest, start, start + srcLen - 1), src, start);
}

list list2ListStrided(list src, integer start, integer end, integer stride) {
    return llList2ListStrided(llList2List(src, start, end), 0, end, stride);
}

list chopString(string src, integer elementLength) {
    list stringParts = [];
    integer stringIndex = 0;
    integer length = llStringLength(src);
    for (stringIndex = 0; stringIndex < length; stringIndex += elementLength) {
        stringParts += llGetSubString(src, stringIndex, stringIndex + elementLength - 1);
    }
    return stringParts;
}

list wordWrap(string src, integer lineMaxLength) {
    if (lineMaxLength < 2)
        lineMaxLength = 2;
    list lines;
    list words = llParseString2List(src, [" "], []);
    
    string curLine = "";
    integer wordIndex;
    // I use llGetListLength in the iteration because the length of words 
    // may increase.
    // Quick speedup tip: Make the llGetListLength call update a variable
    // when the list is mutated.
    for (wordIndex = 0; wordIndex < llGetListLength(words); wordIndex++) {
        string  word       = llList2String(words, wordIndex);
        integer wordLength = llStringLength(word);
        if (wordLength > lineMaxLength) {
            // Get the chunk of the word that will fit on the screen
            // minus one character, for the dash.
            string truncated = llGetSubString(word,    0, lineMaxLength - 2) + "-";
            string rest      = llDeleteSubString(word, 0, lineMaxLength - 2);
            // Add them to the list after the current index, we want them 
            // to be handled like normal words.
            words = llListInsertList(words, [truncated], wordIndex + 1);
            words = llListInsertList(words, [rest], wordIndex + 2);
        } else {
            integer curLineLength = llStringLength(curLine);
            integer lenAfterAdd   = curLineLength + wordLength;
            if (lenAfterAdd > lineMaxLength) {
                lines += curLine;
                curLine = "";
                words = llListInsertList(words, [word], wordIndex + 1);
            } else if (lenAfterAdd == lineMaxLength) {
                lines += curLine + word;
                curLine = "";
            } else {
                curLine += word;
                curLine += " ";
            }
        }
    }
    if (curLine != "")
        lines += curLine;
    return lines;
}

// ========== For method invocation ==========
string randomSeperator(integer len) {
    integer firstChar = (integer)llFrand(60) + 20;  // Range of printable chars = 0x20 to 0x7E
    if (len <= 1)
        return llUnescapeURL("%"+(string)firstChar);
    integer lastChar;
    do { // Last char must not equal first char.
        lastChar = (integer)llFrand(60) + 20; 
    } while (lastChar == firstChar);
    string ret = llUnescapeURL("%"+(string)firstChar);
    for (len -= 2; len > 0; --len)
        ret += llUnescapeURL("%" + (string)((integer)llFrand(60) + 20));
    return ret + llUnescapeURL("%"+(string)lastChar);
}

string listToString(list src) {
    string chars = (string) src; // Squashes all elements together.
    string seperator;
    do { // Find a seperator that's not in the list's string form
        seperator = randomSeperator(3); // so we dont kill data.
    } while (llSubStringIndex(chars, seperator) != -1);
    return seperator + llDumpList2String(src, seperator);
}

list stringToList(string src) { // First 3 chars is seperator.
    return llParseStringKeepNulls(llDeleteSubString(src, 0, 2),
        [llGetSubString(src, 0, 2)], []);
}

callMethod(integer identifyer, string methodName, list parameters) {
    llMessageLinked(LINK_THIS, identifyer, // ID only necessary for return value.
        listToString(parameters), methodName);
}

returnValue(integer identifyer, string methodName, list value) {
    callMethod(identifyer, methodName + "_ret", value);
}
// =============================================

trigger_getMenuTextAreaSize() {
    callMethod(0, "getMenuTextAreaSize", []);
}

// Events triggered by script:
trigger_receivedMenuCategory(string menu, string categoryTitle, integer categoryID) {
    callMethod(0, "receivedMenuCategory", [menu, categoryTitle, categoryID]);
}

trigger_receivedMenuContent(string menu, string contentName, integer contentID) {
    callMethod(0, "receivedMenuContent", [menu, contentName, contentID]);
}

trigger_receivedMenuKeywordSearch(string menu, string displayText) {
    callMethod(0, "receivedMenuKeywordSearch", [menu, displayText]);
}

trigger_receivedMenuText(string menu, string text) {
    callMethod(0, "receivedMenuText", [menu, text]);
}

trigger_receivedMenuSummary(string menu, string summaryTitle, integer summaryID) {
    callMethod(0, "receivedMenuSummary", [menu, summaryTitle, summaryID]);
}

trigger_receivedMenuBegin(string menuName, string menuTitle) {
    callMethod(0, "receivedMenuBegin", [menuName, menuTitle]);
}

trigger_receivedMenuEnd(string menuName) {
    callMethod(0, "receivedMenuEnd", [menuName]);
}

trigger_receivedTerminalReset() {
    callMethod(0, "receivedTerminalReset", []);
}

trigger_receivedServerError(string details) {
    callMethod(0, "receivedServerError", [details]);
}

trigger_pong(string moduleName) {
    callMethod(0, "pong", [moduleName]);
}

// ====================== XMLRPC request constants: ====================== \\
    // Sent by the client to the server when the server polls
    // and the client has no data to send.
    integer NO_REQUESTS     = 5000;
    
    // Sent by the server to the client when a poll occurs.
    integer SERVER_POLL     = 6000;
    
    // Sent by client to server requesting that the server stop polling.
    integer STOP_POLLING    = 6001;
    
    // Sent by server to client that tells client that the RPC string contains multiple command seperated
    // by COMMAND_SEPERATOR.
    integer MULTI_COMMAND   = 6002;
    
    // Sent by server to client requesting that the client restart.
    integer TERMINAL_RESET  = 6003;

    // Sent by the client to the server (or external entity) requesting a 
    // piece of content:
    integer GET_CATEGORY        = 7001; // String value: catagory requested
    integer GET_NOTECARD        = 7002; // String value: Notecard name requested.
    integer GET_PREV            = 7003; // String value: ""
    integer GET_NEXT            = 7004; // String value: ""
    integer GET_KEYWORD_SEARCH  = 7005; // String value: Keyword requested.
    integer GET_SUMMARY         = 7007;
    integer GET_BUTTON          = 7008;
    
    // Sent by the server to the client, between MENU_BEGIN and MENU_END
    // requesting that it display text.
    integer SET_CATEGORY        = 8001; // String value: Catagory name to display.
    integer SET_NOTECARD        = 8002; // String value: Notecard name to display.
    integer SET_PREV            = 8003; // String value ignored
    integer SET_NEXT            = 8004; // String value ignored.
    integer SET_KEYWORD_SEARCH  = 8005; // String value ignored.
    integer SET_TEXT            = 8006; // String value: any text.
    integer SET_SUMMARY         = 8007;
    integer SET_BUTTON          = 8008;
    
    // Sent by the server to the client specifying the start 
    // and end of a menu.
    integer MENU_BEGIN  = 9001; // String value: Menu title
    integer MENU_END    = 9002;
// ====================== /XMLRPC request constants ====================== \\

// For use with the MULTI_COMMAND RPC integer.
string COMMAND_SEPERATOR = "]^[";

// Notecard title parameter seperator:
    string NOTECARD_SEPERATOR = " || ";

// Category name and key seperator:
    string CATEGORY_SEPERATOR = "]_[";


// For wordwrap:
integer charsPerLine;

string curMenuName = "";
triggerAppropriateEventFor(integer commandConstant, string commandData) {
    if (commandConstant == TERMINAL_RESET) {
        trigger_receivedTerminalReset();
    } else if (commandConstant == MENU_BEGIN) {
        list parsedCommandData = llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        string menuTitle = llList2String(parsedCommandData, 0);
        string menuName  = llList2String(parsedCommandData, 1);
        // TODO: Differentiate between menu names and titles.
        trigger_receivedMenuBegin(menuName, menuTitle);
        curMenuName = menuName;
    } else if (commandConstant == MENU_END) {
        list parsedCommandData = llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        //string menuTitle = llList2String(parsedCommandData, 0);
        string menuName  = llList2String(parsedCommandData, 1);
        trigger_receivedMenuEnd(menuName);
    } else if (commandConstant == SET_CATEGORY) {
        list    parsedCommandData =           llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        string  categoryName      =           llList2String(parsedCommandData, 0);
        integer categoryID        = (integer) llList2String(parsedCommandData, 1);
        trigger_receivedMenuCategory(curMenuName, categoryName, categoryID);
    } else if (commandConstant == SET_NOTECARD) {
        list    parsedCommandData =           llParseStringKeepNulls(commandData, [NOTECARD_SEPERATOR], []);
        string  contentName       =           llList2String(parsedCommandData, 0);
        integer contentID         = (integer) llList2String(parsedCommandData, 1);
        trigger_receivedMenuContent(curMenuName, contentName, contentID);
    } else if (commandConstant == SET_KEYWORD_SEARCH) {
        list    parsedCommandData = llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        string  displayText       = llList2String(parsedCommandData, 0);
        trigger_receivedMenuKeywordSearch(curMenuName, displayText);
    } else if (commandConstant == SET_TEXT) {
        if (llStringLength(commandData) > charsPerLine) {
            // Note, this is basically the same as the wordwrap function above
            // but to make everything appear to run faster, I unrolled it and
            // stuck it here.
            integer lineMaxLength = charsPerLine;
            list words = llParseString2List(commandData, [" "], []);
            
            string curLine = "";
            integer wordIndex;
            // I use llGetListLength in the iteration because the length of words 
            // may increase.
            // Quick speedup tip: Make the llGetListLength call update a variable
            // when the list is mutated.
            for (wordIndex = 0; wordIndex < llGetListLength(words); wordIndex++) {
                string  word       = llList2String(words, wordIndex);
                integer wordLength = llStringLength(word);
                if (wordLength > lineMaxLength) {
                    // Get the chunk of the word that will fit on the screen
                    // minus one character, for the dash.
                    string truncated = llGetSubString(word,    0, lineMaxLength - 2) + "-";
                    string rest      = llDeleteSubString(word, 0, lineMaxLength - 2);
                    // Add them to the list after the current index, we want them 
                    // to be handled like normal words.
                    words = llListInsertList(words, [truncated], wordIndex + 1);
                    words = llListInsertList(words, [rest], wordIndex + 2);
                } else {
                    integer curLineLength = llStringLength(curLine);
                    integer lenAfterAdd   = curLineLength + wordLength;
                    if (lenAfterAdd > lineMaxLength) {
                        trigger_receivedMenuText(curMenuName, curLine);
                        curLine = "";
                        words = llListInsertList(words, [word], wordIndex + 1);
                    } else if (lenAfterAdd == lineMaxLength) {
                        trigger_receivedMenuText(curMenuName, curLine + word);
                        curLine = "";
                    } else {
                        curLine += word;
                        curLine += " ";
                    }
                }
            }
            if (curLine != "")
                trigger_receivedMenuText(curMenuName, curLine);
        } else {
            trigger_receivedMenuText(curMenuName, commandData);
        }
    } else if (commandConstant == SET_SUMMARY) {
        list    parsedCommandData =           llParseStringKeepNulls(commandData, [CATEGORY_SEPERATOR], []);
        string  summaryName       =           llList2String(parsedCommandData, 0);
        integer summaryID         = (integer) llList2String(parsedCommandData, 1);
        trigger_receivedMenuSummary(curMenuName, summaryName, summaryID);
    } else if (commandConstant == MULTI_COMMAND) {
        list commandsInData = llParseString2List(commandData, [COMMAND_SEPERATOR], []);
        list intDatas = list2ListStrided(commandsInData, 0, -1, 2);
        list strDatas = list2ListStrided(commandsInData, 1, -1, 2);
        
        integer i;
        integer numCommands = llGetListLength(intDatas);
        for(i = 0; i < numCommands; i++) {
            integer command = (integer) llList2String(intDatas, i);
            string  data    =           llList2String(strDatas, i);
            triggerAppropriateEventFor(command, data);
        }
    }
}

string this;
default {
    state_entry() {
        this = llGetScriptName();
        trigger_getMenuTextAreaSize();
    }
    
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "getMenuTextAreaSize_ret") {
            list paramList = stringToList(parameters);
            // Method signature:
            // getMenuTextAreaSize_ret(integer width, integer height)
            integer width  = (integer) llList2String(paramList, 0);
            integer height = (integer) llList2String(paramList, 1);
            charsPerLine = width;
            state main;
        }
    }
}
state main {
    link_message(integer sender, integer num, string parameters, key methodName) {
        if (methodName == "receivedRpcData") {
            list paramList = stringToList(parameters);
            // Method signature:
            // receivedRpcData(integer iData, string sData)
            integer iData = (integer)   llList2String(paramList, 0);
            string  sData =             llList2String(paramList, 1);
            // Work is done in triggerAppropriateEventFor because I can
            // use recursion there. Recursion is necessesary for the MULTI_COMMAND command.
            // The only way I can recurse the link_message event is by triggering
            // another link_message, further clogging the limited script2script message
            // space.
            triggerAppropriateEventFor(iData, sData);
        } else if (methodName == "receivedEmailData") {
            list paramList = stringToList(parameters);
            // Method signature:
            // receivedEmailData(integer timestamp, string sender, string subject, string message)
            integer timestamp   = (integer) llList2String(paramList, 0);
            string  sender      =           llList2String(paramList, 1);
            string  subject     =           llList2String(paramList, 2);
            string  message     =           llList2String(paramList, 3);
            
            if (subject == "ERROR") {
                trigger_receivedServerError(message);
            }
        } else if (methodName == "moduleReady") {
            list paramList = stringToList(parameters);
            string module = llList2String(paramList, 0);
            if (module == this)
                returnValue(num, methodName, [TRUE]);
        }
    }
}


LibraryContentBrowser
There is no comment on this page. [Display comments/form]