Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Memory Management During Run-time

Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-09-2007 13:47
Summary: I have a script that runs out of memory as it runs.

The script [let's call it Script A] looks up 40 terms from a notecard, assigns them to 40 variables, bundles them up and 11MessageLinked's them off to other scripts.

If someone changes language on the object (using a menu provided by another script), Script A then looks up 40 different terms from the notecard as appropriate for that language choice, gives the 40 variables those fresh values, and they then get sent off. [1]

I'm using llGetFreeMemory() to report on memory which I'm given to undertand is, er, as reliable as anything else in SL (grin.)

The script starts off running with 2421 of memory free. It's far less than I'd like, but I'm fine with that, I guess, considering the amount of information it has to handle, and that it doesn't have to do any other function than supply terms to other scripts. But when someone changes to German, the memory goes down to 1073. Change to French, the memory drops down to 771, change to Italian, memory is 561, back to English, 397, and you can see that eventually there is going to be 0 memory, and presumably, kaboom.

I would have thought (no particular reason, I suppose), that the memory would have stayed somewhere in the range of the initial value because the values assigned initially to the 40 variables were merely being replaced by other values. And though I use a few lists during lookups, etc, to reduce the amount of written code required to keep the compiler memory end happy, when I am done with them, I toss all the lists away (I think) by going myList = [].

There must be a mistake I'm making somewhere.

Would post script, but won't do so unless asked as it's very long and dull.


__________________________________

[1] Because I'm seeing from results elsewhere that the variable for hello, let's say banana, is indeed switching from "Hello" to "bonjour" then to "ciao", I'm assuming that I'm not keeping "hello" and "bonjour" around. Perhaps I'm wrong in that assumption, and need to do something to flush them somehow first.
RJ Source
Green Sky Labs
Join date: 10 Jan 2007
Posts: 272
08-09-2007 13:50
Posting would help.

When appending to lists, use the trick:

ListA = (ListA = []) + ListA + NewItem;

instead of:

ListA += NewItems;

Also, any chance you could just llResetScript() for a language change?
Meade Paravane
Hedgehog
Join date: 21 Nov 2006
Posts: 4,845
08-09-2007 13:53
From: RJ Source
Also, any chance you could just llResetScript() for a language change?

The trick is remembering the new language after the reset.. I guess having another script just to track that would work, though.

Chaz, does the initial free memory change if you force it to start with German?
_____________________
Tired of shouting clubs and lucky chairs? Vote for llParcelSay!!!
- Go here: http://jira.secondlife.com/browse/SVC-1224
- If you see "if you were logged in.." on the left, click it and log in
- Click the "Vote for it" link on the left
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-09-2007 14:05
Actually, one thing I thought I could do is.

(1) have the menu tell the translator script to reset;
(2) on restart, have translator script check with menu script, hi, i'm back now, before I load the default language set in the config notecard, did you have anything pending for me?
(3) menu says back actually yes I did, here it is.

But I haven't tried it yet as I thought I should first learn if I'm doing something wrong with memory management.
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-09-2007 14:18
Okay, criticize away (Chaz cringes at the thought of everything he's doing wrong -- I've only been at this scripting stuff really for about a month.)


[1] NOTECARD
referred to in script below as "glossary"

looks like this. Each line has same term in 4 different languages.

[CODE]
AdminNo=You are not an administrator, Vous n'etes pas administrateur, Sie sind nicht ein Verwalter, Non siete un amministratore
AdminMode=Open in maintenance mode. Click to close when done, Ouvert pour travail. Clicquer quand fini., Offnet fur Arbeit. Linksklicken zu schliessen, Aperto per lavoro. Clica per chiudere
Admins=Administrators, Administrateurs, Verwaltern, Amministratori
[/CODE]

etc.



[2] SCRIPT



[PHP]
//___________________ Variables

string CardNameLang = ".Language";
string CardName = "~glossary";
key LangRequest;
key Request;
integer tI;
integer x;
integer i;


string Action;
string Lang;
string LangParam = "Language=";
integer linkmsgnum;
string linkmsgstr;
key linkmsgkey;
list TempList;
string linksender;
string LangProper;
string msgtmp;
list ParamsList;
integer ParamsListCount;
string Resend;
list TermsList;
string tmp02;
list tmp03;
string tmp04;

string AccessNo;//for buttons
string AdminAsk; //~for menu
string AdminNo; //~for menu
string AdminMode;
string Admins; //~for menu, doors
string BaseNo; //for ~config
string BtnDone;//for buttons
string BtnGo; //for buttons
string BtnPressed; //for buttons
string BtnSound; //for buttons
string BtnSoundNo; //for buttons
string CarDone;//for ~cpu
string CfgCard; //for ~cpu, ~config
string CpuDone; //for ~cpu, ~config
string CpuGo; //for ~cpu, ~config
string DataDone; //for ~config
string DefaultSpeed; //for ~config
string DoorDone; //for doors
string DoorSound; //for doors
string ElvCome; //for buttons
string ElvFlrDsp;
string ElvID; //~for menu
string FlrHeightMatch; //for ~config
string FlrHere; //for ~move
string FlrNo;//for ~cpu
string FlrNumNo; //for buttons & doors
string FlrReq;//for ~cpu
string ForFlr; //for buttons & doors
string GoBtnCfg;//for ~cpu
string GoCar; //for ~config
string GoCfg; //for ~config
string GoDoorCfg;//for ~cpu
string HeightsNo; //for ~config
string Here; //for ~move
string MemoryAdded;//for ~memory
string MenuConfirm; //for ~menu
string MenuDone; //for ~menu
string MenuHeader; //~for menu
string PermsDone; //for ~cpu
string PermsGo; //for ~config
string Pos; //for ~menu
string RightGrp;//for ~cpu
string SetFor;//for doors & buttons
string TimeOut; //~for menu
string TransDone; //~for here

string tmpac;
string tmpbtnonboard;
string tmpconfig;
string tmpcpu;
string tmpcpudoors;
string tmpcpubtnremote;
string tmpelevatorfloordisplay;
string tmpmemory;
string tmpmenu;
string tmpmove;


//had to do separate and concatenate later, even tho was nowhere near theoretical list max.

list ParamsListDefault1 = ["AccessNo=","AdminAsk=","AdminNo=", "AdminMode=", "Admins=", "BaseNo=", "BtnDone=", "BtnGo=", "BtnPressed=", "BtnSound=", "BtnSoundNo=", "CarDone=", "CfgCard=", "CpuDone=", "CpuGo=","DataDone=", "DefaultSpeed=", "DoorDone=", "DoorSound=", "ElvCome=", "ElvFlrDsp=", "ElvID=", "FlrHeightMatch=", "FlrHere=", "FlrNo=", "FlrNumNo=", "FlrReq=", "ForFlr="];

list ParamsListDefault2 = ["GoBtnCfg=", "GoCar=", "GoCfg=", "GoDoorCfg=", "HeightsNo=", "Here=" , "MemoryAdded=", "MenuConfirm=", "MenuDone=" , "MenuHeader=", "PermsDone=", "PermsGo=","Pos=", "RightGrp=", "SetFor=", "TimeOut=", "TransDone=" ];

//___________________ Functions

string GetValue( string sString )
{
integer iGo;
string sValue = "";
string sbValue = "";

iGo = llSubStringIndex( sString, "=" ) + 1;
if( iGo )
{
sValue = llGetSubString( sString, iGo, llStringLength(sString) - 1 );
if( sValue )
{
sbValue = llToLower( sValue );
if( sbValue == "true" )
sValue = "1";
if( sbValue == "false" )
sValue = "0";
return( sValue );
}
}
return(NULL_KEY);
}



integer StringLeftICompare( string sLeftMatch, string sLongString )
{
integer iLength;

iLength = llStringLength( sLeftMatch ) - 1;
if( llToLower(llGetSubString( sLongString, 0, iLength ) ) == llToLower(sLeftMatch) )
return( TRUE );
return( FALSE );
}

string str_replace(string source, string test, string replace)
{
list list_source = llParseString2List(source, [], [test]);

integer i;
string return_str = "";
for (i = 0; i < llGetListLength(list_source); i += 1) {
if (llList2String(list_source, i) == test) {
return_str += replace;
} else {
return_str += llList2String(list_source, i);
}
}
return return_str;
}


Translation () {

//tried to loop and virtualize all the following, but not possible to put Variables in a list
//http://forums.secondlife.com/showthread.php?t=202864

AccessNo=llList2String(TermsList, 0);
AdminAsk= llList2String(TermsList, 1);
AdminNo=llList2String(TermsList, 2);
AdminMode=llList2String(TermsList, 3);
Admins=llList2String(TermsList, 4);
BaseNo=llList2String(TermsList, 5);
BtnDone=llList2String(TermsList, 6);
BtnGo=llList2String(TermsList, 7);
BtnPressed=llList2String(TermsList, 8);
BtnSound=llList2String(TermsList, 9);
BtnSoundNo=llList2String(TermsList, 10);
CarDone=llList2String(TermsList, 11);
CfgCard=llList2String(TermsList, 12);
CpuDone=llList2String(TermsList, 13);
CpuGo=llList2String(TermsList, 14);
DataDone=llList2String(TermsList, 15);
DefaultSpeed=llList2String(TermsList, 16);
DoorDone=llList2String(TermsList, 17);
DoorSound=llList2String(TermsList, 18);
ElvCome=llList2String(TermsList, 19);
ElvFlrDsp=llList2String(TermsList, 20);
ElvID=llList2String(TermsList, 21);
FlrHeightMatch=llList2String(TermsList, 22);
FlrHere=llList2String(TermsList, 23);
FlrNo=llList2String(TermsList, 24);
FlrNumNo=llList2String(TermsList, 25);
FlrReq=llList2String(TermsList, 26);
ForFlr=llList2String(TermsList, 27);
GoBtnCfg=llList2String(TermsList, 28);
GoCar=llList2String(TermsList, 29);
GoCfg=llList2String(TermsList, 30);
GoDoorCfg=llList2String(TermsList, 31);
HeightsNo=llList2String(TermsList, 32);
Here=llList2String(TermsList, 33);
MemoryAdded=llList2String(TermsList, 34);
MenuConfirm=llList2String(TermsList, 35);
MenuDone=llList2String(TermsList, 36);
MenuHeader=llList2String(TermsList, 37);
PermsDone=llList2String(TermsList, 38);
PermsGo=llList2String(TermsList, 39);
Pos=llList2String(TermsList, 40);
RightGrp=llList2String(TermsList, 41);
SetFor=llList2String(TermsList, 42);
TimeOut=llList2String(TermsList, 43);
TransDone=llList2String(TermsList, 44);


tmpac = Lang + "##" + CfgCard + "##" + PermsGo + "##" + PermsDone;

tmpbtnonboard = Lang + "##" + BtnSound + "##" + BtnSoundNo + "##" + BtnGo + "##" + BtnPressed + "##" + BtnDone + "##" + CfgCard + "##" + ElvCome + "##" + FlrNumNo + "##" + ForFlr;

tmpconfig = Lang + "##" + DataDone + "##" + DefaultSpeed + "##" + FlrHeightMatch + "##" + GoCfg + "##" + BaseNo + "##" + HeightsNo + "##" + CfgCard;

tmpcpu = Lang + "##" + CpuGo + "##" + CpuDone + "##" + FlrNo + "##" + FlrReq + "##" + GoDoorCfg + "##" + RightGrp + "##" + GoBtnCfg + "##" + AccessNo;


//don't add lang to these 2 following remote, cpu adds it
tmpcpubtnremote = BtnSound + "##" + BtnSoundNo + "##" + BtnGo + "##" + BtnPressed + "##" + BtnDone + "##" + CfgCard + "##" + ElvCome + "##" + FlrNumNo + "##" + ForFlr;

tmpcpudoors = Admins + "##" + CfgCard + "##" + SetFor + "##" + DoorSound + "##" + DoorDone + "##" + ForFlr + "##" + FlrNumNo + "##" + AdminMode;

tmpelevatorfloordisplay = Lang + "##" + ElvFlrDsp + "##" + "";

tmpmenu = Lang + "##" + AdminNo + "##" + Admins + "##" + AdminAsk + "##" + ElvID + "##" + MenuHeader + "##" + Pos + "##" + TimeOut + "##" + MenuDone + "##" + MenuConfirm;

tmpmemory = Lang + "##" + MemoryAdded;

tmpmove = Lang + "##" + GoCar + "##" + Here + "##" + FlrHere + "##" + CarDone;


if (Resend == "Yes";) {
//llMessageLinked(LINK_SET, 200, Lang, llGetKey());
//llSay(0,"trans resend " + Lang);
llMessageLinked(LINK_THIS, 1300, tmpac, llGetKey());
llMessageLinked(LINK_THIS, 1503, tmpmenu, llGetKey());
llMessageLinked(LINK_THIS, 107, "Send Updated Info to Remotes", llGetKey()); //command to ~cpu
llMessageLinked(LINK_THIS, 101, tmpmemory, llGetKey());
llMessageLinked(LINK_THIS, 1415, tmpcpu, llGetKey());
llMessageLinked(LINK_THIS, 1418, tmpcpudoors, llGetKey());
llMessageLinked(LINK_THIS, 1419, tmpcpubtnremote, llGetKey());
llMessageLinked(LINK_THIS, 1628, tmpmove, llGetKey());
llMessageLinked(LINK_ALL_CHILDREN,1733,linksender + "##" + tmpbtnonboard, llGetKey());
llMessageLinked(LINK_ALL_CHILDREN, 1743, tmpelevatorfloordisplay, llGetKey());
llMessageLinked(LINK_THIS, 150, tmpconfig, llGetKey());
Resend = "";
//llSay(0,"translator sent out new translations " + Lang);
}

llWhisper(0,TransDone);
TermsList = [];

}

LangSwitch() {
if (Lang == "French";) {
LangProper = "Francais";
x = 1;
}
else if (Lang == "German";) {
LangProper = "Deutsch";
x = 2;
}
else if (Lang == "Italian";) {
x = 3;
LangProper = "Italiano";
}
else {
//English or default
LangProper = "English";
x = 0;
}

}

analyse_linkmsg(integer linkmsgnum, string linkmsgstr,key linkmsgkey) {

linksender = (string)linkmsgkey;
integer tmp01;
integer dest;
//this script sends out messages in the 1000s to identify it, thus 1000 added to request channel

list channels = [1, 300, 415, 418, 419, 503, 628, 743, 832];
list msgs = [tmpmemory, tmpac, tmpcpu, tmpcpudoors, tmpcpubtnremote, tmpmenu, tmpmove, tmpelevatorfloordisplay, linksender + "##" + tmpbtnonboard];

for (i = 0; i <= llGetListLength( channels ) + 1; i++) {
tmp01 = llList2Integer(channels, i);
if (tmp01 == linkmsgnum) {
tmp02 = llList2String(msgs,i);
if (linkmsgnum == 1) {
dest = 101;
}
else {
dest = 1000 + linkmsgnum;
}
llMessageLinked(LINK_SET, dest, tmp02, llGetKey());
}
}
tmp02 = "";
msgs = [];


}

//___________________States


default
{
state_entry()
{
if(llGetInventoryType(CardNameLang) == INVENTORY_NONE) {
llWhisper(0,CfgCard + ": " + CardNameLang);
return;
}
else {
LangRequest = llGetNotecardLine(CardNameLang, tI = 0 );
}
}

on_rez(integer p) {
llResetScript();
}


dataserver( key myLangQuery, string sData ) {
if ( LangRequest == myLangQuery ) {
//llSay(0, "starting language data query";);
if ( sData == EOF ) {
LangRequest = "";
sData = "";
state translate;
}

else {
if (llGetSubString(sData, 0 ,0) != "#" && llStringTrim(sData, STRING_TRIM) != "";) {
sData = llStringTrim(sData, STRING_TRIM);
if( StringLeftICompare(LangParam, sData) ) {
Lang = llStringTrim((string)GetValue( sData ),STRING_TRIM);
//being cautious here in case user type francais vs Francais. if they used an accent despite instructions not to, oh well.
if ((Lang == LangParam) || (Lang == "";)) {
Lang = "English";
}
else if (llToLower(Lang) == "deutsch";) {
Lang = "German";
}
else if (llToLower(Lang) == "francais";) {
Lang = "French";
}
else if (llToLower(Lang) == "italiano";) {
Lang = "Italian";
}
else{
Lang = "English";
}
}
}
LangRequest = llGetNotecardLine( CardNameLang, ++tI );
}
}
}
}





state translate {


state_entry() {
LangSwitch();
llWhisper(0,"Initialisation...." + LangProper);
if(llGetInventoryType(CardName) == INVENTORY_NONE) {
return;
}
else {
//from start. List of all the string=terms to be looked up
ParamsList = ParamsListDefault1 + ParamsListDefault2;
ParamsListCount = llGetListLength( ParamsList ) + 1;
Request = llGetNotecardLine(CardName, tI = 0 );
}
}


dataserver( key myQuery, string sData ) {
if ( Request == myQuery ) {
//llSay(0, "starting params query";);
//desperate attempt to release memory
if ( sData == EOF ) {
ParamsList = [];
Request = "";
tmp03 = [];
tmp04 = "";
sData = "";
state ready;
}

else {

for (i = 0; i <= ParamsListCount; i++) {
string tmp01 = llList2String(ParamsList, i);
//string tmp02 = llStringTrim(str_replace(tmp01, "=","";),STRING_TRIM);
//llSay(0,(string)i);
//llSay(0, "looking for" + tmp01);
if (llGetSubString(sData, 0 ,0) != "#" && llStringTrim(sData, STRING_TRIM) != "";) {
sData = llStringTrim(sData, STRING_TRIM);
if( StringLeftICompare(tmp01, sData) ) {
tmp03 = llCSV2List((string)GetValue( sData ));
tmp04 = llList2String(tmp03,x);
TermsList = (TermsList=[]) + TermsList + tmp04;
ParamsList = llDeleteSubList(ParamsList, i, i);
jump Next;
}
}
}

@ Next;
Request = llGetNotecardLine( CardName, ++tI );
}
}
}
}



state ready {

state_entry() {
//llSay(0,llDumpList2String(TermsList, " + ";));
Translation();
llSay(0, "trans memory at startup: " + (string)llGetFreeMemory());
//llSay(0,Lang);
if (Action != "Language Switch";) {
llMessageLinked(LINK_THIS,150,tmpconfig,NULL_KEY);
llMessageLinked(LINK_THIS,108,tmpmemory,NULL_KEY);
}
}

link_message(integer sender_num, integer num, string str, key id){
//llSay(0,(string)num + " from " + (string)sender_num + " saying " + str);
linkmsgnum = num;
linkmsgstr = str;
linkmsgkey = id;

if (linkmsgnum == 500) {
//from menu.
//all separate translation linkmsg deliveries need to be bundled as well
//at end of translation. Linkmsgnum of 500 means that someone has changed
//language via menu so fresh translations have to be sent out
TempList = llParseStringKeepNulls(str,["##"],[]);
Action = llList2String(TempList, 0);
//llSay(0, "Action" + Action);
Lang = llList2String(TempList, 1);
//llSay(0,"translator heard " + (string)100);
Resend = "Yes";
state translate;
}
else {
analyse_linkmsg(linkmsgnum,linkmsgstr,linkmsgkey);
}
}


}

[/PHP]
Qie Niangao
Coin-operated
Join date: 24 May 2006
Posts: 7,138
08-09-2007 14:18
The trick RJ mentions for lists also applies to strings (see ), so if stuff ever gets appended to strings, consider something like:

From: someone
myString = (myString="";) + myString + "new_item";
Pure speculation: What I suspect happens is not a leak, per se, but rather fragmentation of the heap. So, if llResetScript isn't an option, and if the heap fragmentation hypothesis is correct, then better results might be obtained by avoiding any operation that allocates on top of the block that will be freed when changing languages, so that everything language-related gets freed before any allocation for the new language. But this all may be superstition.

If there's a way the app can accommodate a script reset, though (and the posted ideas for that sound workable to me), then that's by far the simplest, surest approach.
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-09-2007 14:25
I'm okay with the superstitious approach. There's been many times in RL that I've been tempted to don a grass skirt and dance around servers and routers to see if that helps them.
RJ Source
Green Sky Labs
Join date: 10 Jan 2007
Posts: 272
08-09-2007 16:57
Also if all you need to retain during reset is the selected language, you could just write it to the object description, and have the script select the language that's there during initial state_entry.
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-09-2007 17:04
Yes, that's a very good idea. It actually just occurred to me 5 minutes ago walking home from the corner store with a fresh supply of caffeinated soft drinks (grin.)

So, the consensus seems to be to just give up on trying to figure out the memory loss, take it as just one of those things, and get on with it by doing a reset to recovery memory that way.

I can live with that.
RJ Source
Green Sky Labs
Join date: 10 Jan 2007
Posts: 272
08-09-2007 17:14
Its a quick fix. But you're so tight on memory, it sounds like if you have to make any other mods, you could wind up having more troubles. So at some point, you might want to split the script into pieces maybe.
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-09-2007 17:34
Yeah, i've thought of that. Mind you, the buggery thing is already a split off from other scripts for exactly that reason. At least it made sense (to me anyway), making one dedicated to vocabulary. Trying to think of some way to logically split it even further just isn't come with me. So I may have to fake a logical reason in my mind, and live with it.
Shadow Subagja
Registered User
Join date: 29 Apr 2007
Posts: 354
08-09-2007 18:48
Definitely try the null string and null list assignment pointers above and see how that affects your free memory output, even if just to report the results here for pure interest sake.

String and list assignments are legal that doesn't mean that the memory doesn't linger around waiting for garbage collection (As Qie put it, heap fragmentation) and still belong to the context of your script. I believe this is what those hacks address in a very empirically 'seems to work' way. What I mean is, your code can be just fine but still break due to the behavior of the scripting engine which is (almost) beyond your control. So its not necessarily a complete voodoo prayer, more like an educated guess with some voodoo for the locals thrown in! hehe
Kidd Krasner
Registered User
Join date: 1 Jan 2007
Posts: 1,938
08-10-2007 10:27
Turn tmp03, tmp04 into local variables. These two are only used in the translate dataserver event, so there's no reason to have them around all the time. This may be true of other variables as well.

Consider putting each language into a separate notecard. If you're really ambitious, consider putting each language into its own script, and then enable or disable the scripts as appropriate.

For that matter, get rid of the individual words and put each of the concatenated messages that you need into a separate line in the notecard. Why do all that work in lsl when it never changes?

Get rid of all those variables for all those individual strings. In that thread you cite, you were trying too hard to make it automated in a fancy way. Instead, take the advice given there by Qie in /54/ff/202864/1.html#post1623388 . (You could even be real extreme, and use a C preprocessor to get rid of the variables for the numeric positions.)
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-10-2007 10:40
From: Meade Paravane
Chaz, does the initial free memory change if you force it to start with German?


I thought it would might be useful to record for anyone else venturing down this path the following.

The application allows for language to be either changed on the fly, or for the language to be set in a config notecard to be read from there in a clean, fresh boot.

Here are what's left of memory in the translation script after a clean, fresh boot for each of the 4 language choices I am working with. As you might expect, it would seem that the "wordier" a language, the less memory left after immediate startup:

English: 2279
German: 2251
Italian: 2214
French: 1878

The more concise nordic languages come out on top; the wordier languages on bottom. (I was going to say latin-based languages, but that wouldn't be fair to Latin, as Latin is actually a very concise language.)

The clean boot in French, which produced the same terms for use, but in French, required fully 400k [1] more in memory. [2]

___________________________________

[1] k as opposed to kb, for the benefit of anyone who still shares the lingering confusion from early days as described here : k vs kb confusionl

[2] To be fair, the strength of French as mother-tongue speakers will no doubt point out, is that it allows for more preciseness and clarity of thought precisely because of all the nuances it is capable of handling with its, um, greater amount of words. grin.
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-10-2007 10:47
Kidd, thanks very much. Very good ideas. I'm clearly going to have to do something more drastic. The current scenario leaves no room for another language, really. If Spanish speakers come along and say what about us, then I'm stuffed, really. Going to do some RL work and ponder all this.
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-10-2007 10:55
From: Kidd Krasner
Turn tmp03, tmp04 into local variables. These two are only used in the translate dataserver event, so there's no reason to have them around all the time. This may be true of other variables as well.


I had made them global out of desperation as I wasn't sure I could count on them being discarded, so I made them global so I could then discard them manually, just to experiment.

But you'll be pleased (and no doubt, not surprised) to learn that your suggestion of taking them local as opposed to global resulted in increased boot-up memory.

I reported just a few minutes ago the following:

English: 2279

Your suggested simple change just increased that available memory. It is now:

English: 2314

Your suggested code change had a greater impact than my telling them to go away (the change also allowed me to get rid of the code telling them to go away.)
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-10-2007 16:44
From: Kidd Krasner
For that matter, get rid of the individual words and put each of the concatenated messages that you need into a separate line in the notecard. Why do all that work in lsl when it never changes?


Implemented Kidd's above idea, referring to:

list ParamsListDefault1 = ["AccessNo=","AdminAsk=","AdminNo=",

and available memory after load now up to

English: 2602

from earlier today:

English: 2314

I still have to chew over some of the bigger picture ideas, as having more memory to start won't stop original problem, I expect, of memory eventually disappearing altogether through language changes via menu. But that will have to wait a few hours, time for some RL patio and beer time.
Escort DeFarge
Together
Join date: 18 Nov 2004
Posts: 681
08-11-2007 05:11
Chaz - try a totally different approach!
Don't make your script dependent on the parameters you specify, that is why your code is verbose. Instead do something like this (which shouldn't give you memory problems and gives you a great deal of space to put in your specific code).

Copy/Paste the below into a text editor that works before attempting to read it LOL.
CODE

// Example of an approach to translations
// Escort DeFarge - Free to use
list languages = [];
list parameters = [];
list values = [];

string CONFIG_NOTECARD = "Config";
integer current_line = 0;
key query = NULL_KEY;

string current_config = "";

//
default {
state_entry() {
// First find out what language notecards are available
integer number = llGetInventoryNumber(INVENTORY_NOTECARD);
while (--number >= 0) {
string name = llGetInventoryName(INVENTORY_NOTECARD, number);
if (llSubStringIndex(name, CONFIG_NOTECARD) == 0) {
if (name == CONFIG_NOTECARD) {
languages += "English"; // Default language
} else {
string language = llGetSubString(name, llSubStringIndex(name, "."), -1);
if (llStringLength(language > 0)) {
languages += language;
}
}
}
}

// Set up default language (if available) or just use the first language found
if (llGetListLength(languages) > 0) {
current_config = CONFIG_NOTECARD;
if (llGetInventoryType(current_config) == INVENTORY_NONE) {
current_config = CONFIG_NOTECARD + "." + llList2String(languages, 0);
}
state load;

} else {
llOwnerSay("ERROR - No translations available");
}
}
}

// Load (or reload) the translation indicated by the *current* config card (language)
state load {
state_entry() {
parameters = [];
values = [];
current_line = 0;
query = llGetNotecardLine(current_config, current_line);
}

//
dataserver(key id, string info) {
if (id == query) {
query = NULL_KEY;
if (info != EOF) {
info = llStringTrim(info, STRING_TRIM); // Get rid of empty lines
if (llStringLength(info) > 0 && llGetSubString(info, "#") != 0) {
list args = llParseString2List(info, ["="], []);
if (llGetListLength(args) == 2) {
string parameter = llStringTrim(llList2String(args, 0), STRING_TRIM);
string value = llStringTrim(llList2String(args, 1), STRING_TRIM);
if (llToUpper(value) == "TRUE") {
value = TRUE;
} else if (llToUpper(value) == "FALSE") {
value = FALSE;
} else {
// leave if as it is? up to you :)
llOwnerSay("WARNING - Expected true or false for value of " + parameter);
}
parameters += parameter;
values += value;
} else {
llOwnerSay("ERROR - " + current_config + " [line "
+ (string)(current_line + 1) + "] " + info);
}
}
current_line++;
query = llGetNotecardLine(current_config, current_line);
} else {
state ready;
}
}
}
}

// I really have no idea what your system does or the command format you are
// using but here you can do your thing, as an example...
state ready {
link_message(integer sender, integer command, string message, key id){
if (command == 500) {
list args = llParseString2List(message, ["##"], []);

if (llList2String(args, 0) == "LANGUAGE") {
// Switching languages just means reloading whatever card you are using
integer language_index = llListFindList(languages, [llList2String(args, 1)]);
if (language_index != -1) {
current_config = CONFIG_NOTECARD + "." + llList2String(languages, language_index);
state load;
} else {
// ignore it or report an error - unsupported language
}
} else {
// Getting values just means searching on the language parameter names
// that are currently loaded.
integer command_index = llListFindList(parameters, [message]);
if (command_index != -1) {
// assuming only boolean values are stored/used
integer command_value = llList2Integer(values, command_index);
//
// etc
//
}
}
}
}
}


_____________________
http://slurl.com/secondlife/Together
Escort DeFarge
Together
Join date: 18 Nov 2004
Posts: 681
08-11-2007 05:35
I just tested using attachments - MUCH BETTER - so Chaz to get an ungarbled version see:

/54/5d/203505/1.html
_____________________
http://slurl.com/secondlife/Together
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-11-2007 07:26
Couplo questions, though I may figger them out eventually:

(1) just to clarify, what is the expected file naming convention of the various language notecards. Is it:
CONFIG_CARD.English
CONFIG_CARD.French
etc
?


(2) how exactly is the end-user setting the default language (or is that a tool a scripter needs to work in, if so, fair enough);

(3) just to confirm, here you are grabbing a string as an integer; you can do that?

integer command_value = llList2Integer(values, command_index);


(4) I think your example assumes that commands can be passed one at a time via link messages, on the fly as needed. I have remote associated devices that communicate via email, so on the fly commands for 1 value at a time won't really, well, er, fly. I think I'd still have to dedicate some code to predetermining lists of values that remote devices need, bundling them up and sending them off at the start so they have them ready to use. And that, I think, is where I might end up the same in terms of memory usage, perhaps. Though I might be a bit better off. Will ponder.

In any event, thank you very much! Looks great!
Escort DeFarge
Together
Join date: 18 Nov 2004
Posts: 681
08-11-2007 07:34
Oh yeah that's not clear without really reading the code... so..

1) Config, Config.Francais, Config.Deutsch etc

2) The default language (English) will be in Config and will load by default. If only Config.Francais exists then the default on startup would be French

3) The load state actually saves the value to an integer in the list. Conversions in lists are often implicit and can be useful (beware vectors and rotations tho)

4) I have no idea what your system does or how/why so you'll have to do the parsing in the "ready" state to something other than the example I gave.

Good luck Chaz and i hope it's the base of the solution you needed :)
_____________________
http://slurl.com/secondlife/Together
Kidd Krasner
Registered User
Join date: 1 Jan 2007
Posts: 1,938
08-13-2007 11:51
From: Chaz Longstaff

(4) I think your example assumes that commands can be passed one at a time via link messages, on the fly as needed. I have remote associated devices that communicate via email, so on the fly commands for 1 value at a time won't really, well, er, fly.

Are you saying that the clients of this code communicate with the translation server by email? If so, then it makes sense to have separate objects, one for each language.
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-13-2007 12:03
You would be right, if all the languages were flying back and forth at the same time.

Basically what it is, is an elevator. It has remote buttons and remote doors.

The setup info is only emailed to these "clients"

(a) at time of initial setup;

or

(b) when the language of operation is switched from one to another.


I know i could use Region Say or something, but I wanted not to use any listens on them. Plus, these clients really only need the setup info delivered once to them. And then they just use it over and over again.

If all the languages were happening all at once, at any given time, I totally agree about separate scripts and separate glossaries for each language.

It's currently performing very very well with one translation engine, drawing all its glossary from one glossary notecard. The resetting the language script upon language change did the free up memory trick. Just like rebooting Microsoft :}
Escort DeFarge
Together
Join date: 18 Nov 2004
Posts: 681
08-13-2007 13:44
From: Chaz Longstaff

(a) at time of initial setup;
or
(b) when the language of operation is switched from one to another.
I know i could use Region Say or something,

Chaz, are you aware that llGetNotecardLine() can accept a key to a notecard if you needed. In which case... you could just email that notecard key (or even llRegionSay() it on some obscure channel...)?
**edited addition: you have to ask yourself -would listening on an obscure channel be more effective than checking email on a timer? my bet is that it would **
regs,
/esc
_____________________
http://slurl.com/secondlife/Together
Chaz Longstaff
Registered User
Join date: 11 Oct 2006
Posts: 685
08-13-2007 14:00
really? So you can remote read a notecard??? (and if so must be in same sim, I presume??)
1 2