2 // huffware script: data cow, by fred huffhines.
4 // a data cow is a script that manages a list of text items. it allows an object
5 // to offload the memory burden of managing a list of items, since LSL is very tight
6 // on memory, even when compiled to mono.
8 // this script is licensed by the GPL v3 which is documented at: http://www.gnu.org/licenses/gpl.html
9 // do not use it in objects without fully realizing you are implicitly accepting that license.
15 // the main list that we manage here. we support adding to it, retrieving it
16 // and modifying it via our API.
18 // the short names for each item in our list. we are basically supporting a
19 // symbol table data structure here.
21 // link message API for the data cow library.
23 // do not redefine these constants.
24 integer DATA_COW_HUFFWARE_ID = 10017;
25 // the unique id within the huffware system for the jaunt script to
26 // accept commands on. this is used in llMessageLinked as the num parameter.
27 string HUFFWARE_PARM_SEPARATOR = "{~~~}";
28 // this pattern is an uncommon thing to see in text, so we use it to separate
29 // our commands in link messages.
30 ///string HUFFWARE_ITEM_SEPARATOR = "{|||}";
31 // used to separate lists of items from each other when stored inside a parameter.
32 // this allows lists to be passed as single string parameters if needed.
33 integer REPLY_DISTANCE = 100008; // offset added to service's huffware id in reply IDs.
36 string RESET_LIST_COMMAND = "reset_L";
37 // flushes out the currently held list. does not send a reply.
38 string ADD_ITEM_COMMAND = "add_I";
39 // adds items to the list. this is a list of pairs of name/value, where the name is
40 // how the item will be looked up from the list, and the value is the contents to be stored.
41 // this command has no reply.
42 string REMOVE_ITEM_COMMAND = "rm_I";
43 // accepts a list of names for items. all the mentioned ones will be removed from the list.
44 // this command also has no reply.
45 string GET_COW_LENGTH = "get_Lc";
46 // returns a single integer which is the length of the cow's list currently.
47 string GET_ITEM_COMMAND = "get_I";
48 // retrieves the item's contents for a given name. first parm is the name. if there
49 // are other parameters, then they are taken as other items to return also.
50 // the return data will be pairs of <name, entry> for each of the names in the request
51 // that is not empty. note that you can use an integer index by prefacing it with a
52 // '#' sign. for example, asking for item "#32" will return index 32 in the list of items.
53 string TAGGED_GET_ITEM_COMMAND = "get_T";
54 // like get item, except the first parameter is an identifier that the caller wants to
55 // use to tag this request. the other parameters are still taken as names. the response
56 // will contain the tag as the first item, then the <name, entry> pairs that were found.
59 // sets up the data cow for business.
62 // flush all contents.
67 // find a named item in our list, if it exists.
68 integer lookup_item(string name)
71 if (llGetSubString(name, 0, 0) == "#") {
72 // our special sign for just using an index.
73 indy = (integer)llGetSubString(name, 1, -1);
74 if ( (indy >= 0) && (indy < llGetListLength(the_names)) ) return indy;
76 for (indy = 0; indy < llGetListLength(the_names); indy++) {
77 if (name == llList2String(the_names, indy)) return indy; // we know where it is now.
80 return -1; // never found it.
83 // places a named item in our list. if there's an existing item with
84 // that name, then it's replaced.
85 integer global_mem_complained = FALSE;
86 add_item(string name, string content)
88 if (llGetFreeMemory() < 2000) {
89 if (!global_mem_complained) {
90 global_mem_complained = TRUE;
91 llOwnerSay("out of memory at " + name);
95 integer current = lookup_item(name);
97 //llOwnerSay("new item: " + name);
98 // this is a new item.
102 //llOwnerSay("reusing slot " + (string)current + " for " + name);
103 // this is an old item to be replaced.
104 the_list = chop_list(the_list, 0, current - 1)
106 + chop_list(the_list, current + 1, llGetListLength(the_list) - 1);
108 //log_it("mem=" + (string)llGetFreeMemory());
111 // deletes an existing item if possible. TRUE is returned on success.
112 integer remove_item(string name)
114 integer indy = lookup_item(name);
115 if (indy < 0) return FALSE; // not present.
116 // found our sad whackee. we know the name and content should be
117 // stored at the exact same index in the two lists.
118 the_names = chop_list(the_names, 0, indy - 1)
119 + chop_list(the_names, indy + 1, llGetListLength(the_names) - 1);
120 the_list = chop_list(the_list, 0, indy - 1)
121 + chop_list(the_list, indy + 1, llGetListLength(the_list) - 1);
122 return TRUE; // all done here.
125 // supports our link message API by answering requests from clients.
126 handle_link_message(integer sender, integer num, string msg, key id)
128 if (num != DATA_COW_HUFFWARE_ID) return; // not for us.
129 //log_it("hlm--mem free=" + (string)llGetFreeMemory());
130 //log_it("rcvd: " + msg + " " + (string)num + " " + (string)id);
131 if (msg == RESET_LIST_COMMAND) {
133 } else if (msg == ADD_ITEM_COMMAND) {
134 list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
136 for (indy = 0; indy < llGetListLength(parms); indy += 2) {
137 add_item(llList2String(parms, indy), llList2String(parms, indy + 1));
139 } else if (msg == REMOVE_ITEM_COMMAND) {
140 list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
142 for (indy = 0; indy < llGetListLength(parms); indy++) {
143 remove_item(llList2String(parms, indy));
145 } else if (msg == GET_COW_LENGTH) {
146 list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
147 string tag = llList2String(parms, 0);
149 if (tag != "") to_reply = [ tag ];
150 to_reply += [ llGetListLength(the_list) ];
151 send_reply(LINK_THIS, to_reply, msg);
152 } else if ( (msg == GET_ITEM_COMMAND) || (msg == TAGGED_GET_ITEM_COMMAND) ) {
153 list parms = llParseString2List(id, [HUFFWARE_PARM_SEPARATOR], []);
156 if (msg == TAGGED_GET_ITEM_COMMAND) {
157 // extract the tag, if they want that type of command handling.
158 tag = llList2String(parms, 0);
159 parms = llDeleteSubList(parms, 0, 0);
162 // make sure we return the tag if they gave us one.
163 if (tag != "") to_reply = [ tag ];
164 for (indy = 0; indy < llGetListLength(parms); indy++) {
165 // iterate through the parameters and reply about any that we find.
166 integer found_posn = lookup_item(llList2String(parms, indy));
167 if (found_posn >= 0) {
168 // this item did exist.
169 to_reply += [ llList2String(the_names, found_posn), llList2String(the_list, found_posn) ];
172 send_reply(LINK_THIS, to_reply, msg);
179 //integer debug_num = 0;
181 // a debugging output method. can be disabled entirely in one place.
182 //log_it(string to_say)
185 // // tell this to the owner.
186 // llOwnerSay(llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
187 // // say this on open chat, but use an unusual channel.
188 //// llSay(108, llGetScriptName() + "[" + (string)debug_num + "] " + to_say);
191 // returns the portion of the list between start and end, but only if they are
192 // valid compared with the list length. an attempt to use negative start or
193 // end values also returns a blank list.
194 list chop_list(list to_chop, integer start, integer end)
196 integer last_len = llGetListLength(to_chop) - 1;
197 if ( (start < 0) || (end < 0) || (start > last_len) || (end > last_len) ) return [];
198 return llList2List(to_chop, start, end);
201 // a simple version of a reply for a command that has been executed. the parameters
202 // might contain an outcome or result of the operation that was requested.
203 send_reply(integer destination, list parms, string command)
205 llMessageLinked(destination, DATA_COW_HUFFWARE_ID + REPLY_DISTANCE,
206 command, llDumpList2String(parms, HUFFWARE_PARM_SEPARATOR));
215 state_entry() { if (llSubStringIndex(llGetObjectName(), "huffotronic") < 0) state real_default; }
216 on_rez(integer parm) { state rerun; }
218 state rerun { state_entry() { state default; } }
222 state_entry() { initialize(); }
224 link_message(integer sender, integer num, string msg, key id)
225 { handle_link_message(sender, num, msg, id); }