Basically I ran into the memory limit with a new product I'm working on, it's an advanced vendor item. It produces zero lag when it's idle, and does very little work when in use (some light list-manipulation on click or payment).
But there's an absolute ton of options and it's mainly loading this vendor (ie after having changed its settings file to add inventory) that takes the most code due to error-checking all the way through to avoid invalid items, and the fact it holds entries in several lists before compressing them into strings to store.
Anyway, by the end of it I had room for a whopping 4 "full-fat" items (ie all options enabled and given settings). This is bad, because I still wanted to add a few more features in there (one being quite important, namely the ability to click on buttons in order to select an item in the first place!). Not to mention a vendor which can handle 4, maybe 5 items isn't that attractive, especially if it means the "Advanced Vendor" is out-stripped by the "Simple Vendor" I also made which has manipulated around 70 items quite happily!
So, I came up with the solution of having another script contain the list that I want, if I need an entry then I request it using link messages.
Without further ado, I present you with the code:
CODE
list data;
integer stride = 6; // Create a strided list, 6 is what I'm working with. 1 works just fine
string sep = "#$%^&~"; // The seperator to use when sending/receiving chunks of list
sendReply(string msg) {
llMessageLinked(LINK_THIS,1024,msg,NULL_KEY); // Message our parent (which only pays attention to messages with the number 1024)
}
default {
state_entry() { sendReply("@@RESET@@"); } // We've been reset, inform our parent in-case this list is important!
link_message(integer x, integer y, string z, key id) {
if (y == 999) { // We only look for message with number 999
list pieces = llParseStringKeepNulls(z,[sep],[]); // Break up the info
z = llList2String(pieces,0); // What's the first part? (usually a command-word)
if (z == "@@FETCH@@") { // We are to retrieve data
integer x = llList2Integer(pieces,1) * stride; // Work out the start point
list chunk = llList2List(data,x,(x + stride - 1)); // Fetch the info
sendReply(llDumpList2String(chunk, sep)); // Send the resultant string
} else if (z == "@@CLEAR@@") { data = []; stride = llList2Integer(pieces,1); } // Clear our list and set our stride variable
else if (z == "@@COUNT@@") sendReply((string)llGetListLength(data)); // Return the list length
else if (z == "@@STRIDECOUNT@@") sendReply((string)(llGetListLength(data) / stride)); // Return the list length in terms of strided entries
else {
if (llGetFreeMemory() < 512) sendReply("@@FULL@@"); // We've got too little memory left to work with! Return an error reply!
else if (llGetListLength(pieces) == stride) data += pieces; // We're okay, add in all pieces received
}
}
}
}
Basically you communicate with the list by sending it messages with integer 999.
To initialise or empty the list you send the message "@@CLEAR@@" plus the stride width to use, joined by the seperator string.
To add information, simply take the whole lot, join it using the seperate (a call to llDumpList2String(list,sep); will do this) and send to the list. It will add it in if it is of length stride.
To count the items you use @@COUNT@@ or @@STRIDECOUNT@@ to which the list will reply with either the number of elements, or the number of strided entries as appropriate.
It also contains checks to see if the list is becoming full, in many cases 512 is probably far too much free room to leave, but then in some it might be far too little, basically it's the minimum amount of memory that the list can work within, so a full list would require 512 bytes of freedom in order to function.
The script currently doesn't support deleting/replacing etc. and I currently don't need it to.
One interesting item of note is that unless your parent script resets this list on state_entry, it will always contain the same data (ie it will continue to store it's list even if the parent script forgets everything). It's also fairly easy to create copies of the list, by simply calling the @@STRIDECOUNT@@ value and then going through requesting each item in turn, each time sending it to another list to store. This actually allows you to manage MASSIVE lists without running out of data in your main script, as it can happily take one entry, pass it on and clear the memory it used while taking another.
As a note also, the two count functions don't return a keyword with their reply, which I will probably change as otherwise the parent script has to keep track of what's coming or else it could easily misinterpret the answer.
And as a conclusion, I don't like how the script limit is 16k INCLUDING the script. I think it would make much more sense to have 16k for the script, and 16k for the memory (since 16k is a tiny amount anyway). If you don't use all the script space then it could maybe be used as more free-space, but the main point is that a complicated script that takes up 12k of bytecode isn't likely to need only 4k of memory!
It doesn't help that we're using 32-bit integers as flags with no easy way to place several flags into them. I'm frequently putting flags into a single integer then using various LSL function constants as my own flags so I can use bitwise operators on them without wasting much more memory overall defining my own constants.