// RICP : Robot Information/Control Protocol
// Vendor Sample
//
// This script handles vendors with configuration files
// Example include JEVN and many other vendors
//
// You can configure the script by setting the variables
// below so that it can read most any format.
//
// It will also read configuration files that use multiple
// lines for a single product.
//
// Feel free to modify this script if it does not work
// for you.
//
// Created: 2007-08-18
// Authors: Diva Canto and Felix Wakmann
//
// Copyright (c) 2007 by The Regents of the University of California.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// struture of the config file
string CONFIG_FILE_LABEL = "_items" ;
string COMMENT_MARK = "#" ;
string SEPARATOR = ";" ;
// set MULTI_LINE to true for multi-line config files
integer MULTI_LINE = FALSE ;
// for single line
//-----------------------------------------
// each line is a list of items with a separator, as in
// price ; texture name ; item name ; item info ; description ; discounted price; category; limited; type
// 99; pic_Swallowtail -Small- Living Butterfly Wings ; Swallowtail -Large- Living Butterfly Wings ;; Large Living Butterfly Wings ;
//-----------------------------------------
integer PRICE_NUMBER = 1 ;
integer IMAGE_NUMBER = 2 ;
integer TITLE_NUMBER = 4 ;
integer ALT_TITLE_NUMBER = 3 ;
integer DESCRIPTION_NUMBER = 5 ;
integer LOCATION_NUMBER = -1 ;
// for multiline
// each entry is a series of tagged lines, for example:
//-----------------------------------------
// price:: 99
// image:: Swallowtail -Small- Living Butterfly Wings.image
// title:: Swallowtail -Large- Living Butterfly Wings
//-----------------------------------------
string MULTI_LINE_SEPARATOR = "" ;
string TITLE_LABEL = "title" ;
string ALT_TITLE_LABEL = "item" ;
string DESCRIPTION_LABEL = "description" ;
string IMAGE_LABEL = "image" ;
string PRICE_LABEL = "price" ;
string LOCATION_LABEL = "" ;
// the offset is used to pick a point in *front* of the vendor
vector gOffset = <0, -1.5, 0>;
// prints out location
integer gDebug = FALSE;
// ******** Internal Variables ********
// handling request
integer SLRCP_channel = 1020304;
key gTarget;
string gDefaultLocation;
string gParcel;
// reading notecards
string gUploadFileName;
integer gUploadLine;
key gLineRequestID;
// current values
string gTitle;
string gAltTitle;
string gDescription;
string gImage;
string gPrice;
string gLocation;
string gSecret;
// send information to the robot
publish()
{
// no description
string loc = gLocation;
if (loc == "")
loc = gDefaultLocation;
string title = gTitle;
if (title == "")
title = gAltTitle;
string image = llGetInventoryKey(gImage);
if (image == "" || image == NULL_KEY)
image = gImage;
string info =
"product?tag=" + gSecret +
"&title=" + llEscapeURL(title) +
"&id=" + (string)llGetInventoryKey(gAltTitle) +
"&description=" + llEscapeURL(gDescription) +
"&image=" + llEscapeURL(image) +
"&location=" + llEscapeURL(loc) +
"&price=" + llEscapeURL(gPrice);
llInstantMessage(gTarget, info);
gTitle = gAltTitle = gDescription = gImage = gPrice = gLocation = "";
}
// Data Handling
startScan(key target, string file)
{
gTarget = target;
gUploadFileName = file;
gUploadLine = 0;
vector pos = llGetPos() + gOffset * llGetRot();
gDefaultLocation = llGetRegionName() + "/" + ftos(pos.x) + "/" + ftos(pos.y) + "/" + ftos(pos.z);
if (gDebug)
llOwnerSay("secondlife://" + gDefaultLocation);
list details = llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_NAME]);
gParcel = llList2String(details, 0);
gSecret = (string)llFrand(1000000);
list detailParts = llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_OWNER]);
string owner = llList2String(detailParts, 0);
string msg = "verify?tag=" + gSecret + "&owner=" + owner;
// THis command sets the media URL *only* for the robot!
// It does not affect other users
llParcelMediaCommandList( [
PARCEL_MEDIA_COMMAND_AGENT, target,
PARCEL_MEDIA_COMMAND_URL, msg
]);
queryLine();
}
// read a line form the data server
queryLine()
{
gLineRequestID = llGetNotecardLine(gUploadFileName, ++gUploadLine);
}
// extract the fields the line (for multi-line, it is just one field)
translateLine(string data)
{
if (startsWith(data, COMMENT_MARK))
return;
list parts = llParseStringKeepNulls(data, [SEPARATOR], []);
data = ""; // save space
if (llGetListLength(parts) < 2)
return;
if (MULTI_LINE)
{
if (startsWith(data, MULTI_LINE_SEPARATOR))
publish();
string field = llStringTrim(llToLower(llList2String(parts, 0)), STRING_TRIM );
string value = llList2String(parts, 1);
if (field == TITLE_LABEL)
gTitle = value;
else if (field == ALT_TITLE_LABEL)
gAltTitle = value;
else if (field == IMAGE_LABEL)
gImage = value;
else if (field == PRICE_LABEL)
gPrice = value;
else if (field == DESCRIPTION_LABEL)
gDescription = value;
else if (field == LOCATION_LABEL)
gLocation = value;
}
else
{
gTitle = get(parts, TITLE_NUMBER, "");
gAltTitle = get(parts, ALT_TITLE_NUMBER, "");
gDescription = get(parts, DESCRIPTION_NUMBER, "");
gImage = get(parts, IMAGE_NUMBER, "");
gPrice = get(parts, PRICE_NUMBER, "");
gLocation = get(parts, LOCATION_NUMBER, gDefaultLocation);
publish();
}
}
// get a value from a list, or return a default value
string get(list parts, integer index, string defaultVal)
{
if (index < 1)
return defaultVal;
else
return llStringTrim(llList2String(parts, index - 1), STRING_TRIM );
}
// test if a string starts with a prefix
integer startsWith(string str, string prefix)
{
integer len = llStringLength(prefix);
if (len == 0)
return TRUE;
return llGetSubString(str, 0, len) == prefix;
}
string ftos(float n)
{
return (string)((integer) (n + 0.5));
}
default
{
// set up the listener
state_entry()
{
llListen(SLRCP_channel, "", "", "");
}
// listen for a robot
listen(integer chan, string name, key id, string msg)
{
// The incoming message msg looks like this:
// robot|
list parts = llParseStringKeepNulls(msg, ["|"], []);
key botKey;
if (llList2String(parts, 0) == "robot")
{
key bot = llList2String(parts, 1);
if (bot == "")
bot = id;
startScan(bot, CONFIG_FILE_LABEL);
}
}
// handle data server returns
dataserver(key qid, string data)
{
if (qid != gLineRequestID)
return;
if (data != EOF)
{
translateLine(data);
queryLine();
}
else if (gTitle != "" || gAltTitle != "" || gImage != "")
{
// if we have data after the last line, then publish it
publish();
}
}
}