Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Menus and language

Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
01-31-2010 21:01
ok, I've hit a roadblock...

currently I have a menuing system that can handle multiple users, and can also work with multiple languages (uses a different notecard for each language).... what I'd LIKE to do, is be able to handle multiple users AND multiple languages at once... but so far all I can come up with is preloading ALL languages (takes up a lot of space), or running dataserver on almost ever menu call (slow and I have to track individual users, which can get pretty big).

can anyone think of other possibilities?

I'm don't want to show how I'm currently handling it, so that I don't poison the well on new ideas, but I eventually want to release it publicly.
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Cerise Sorbet
Registered User
Join date: 8 Jun 2008
Posts: 254
01-31-2010 22:23
Maybe a cache can work, lists with message number, language, text, last time used, llListStatistics on the time used can make LRU work? That will be piggy, maybe it is less piggy than the others? Maybe it is not?
Twisted Pharaoh
if ("hello") {"hey hey";}
Join date: 24 Mar 2007
Posts: 315
01-31-2010 22:40
You want to say the message #303 in english you need a command like sendmessage(303, "en";) ;

Now how you write it is up to you I see at least 4 different ways, you could use states, scripts, you could use a web server, or an object server, every method would have pros and cons.

At first glance, I would use one script per language, responsible of handling it's notecard and answer to sendmessage() calls.

The script could have a timer and flush its datas when not used for say 10 minutes.

Since we are talking about menus the language scripts would listen to a different channel each and send the commands back to the main script.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
02-01-2010 00:00
From: Cerise Sorbet
Maybe a cache can work, lists with message number, language, text, last time used, llListStatistics on the time used can make LRU work? That will be piggy, maybe it is less piggy than the others? Maybe it is not?

les piggy than loading them all... I think, although it's going to require some user tracking, and probably some language load pending tracking... I'll have to see how much extra code that generates.


From: Twisted Pharaoh
You want to say the message #303 in english you need a command like sendmessage(303, "en";) ;

Now how you write it is up to you I see at least 4 different ways, you could use states, scripts, you could use a web server, or an object server, every method would have pros and cons.

At first glance, I would use one script per language, responsible of handling it's notecard and answer to sendmessage() calls.

The script could have a timer and flush its datas when not used for say 10 minutes.

Since we are talking about menus the language scripts would listen to a different channel each and send the commands back to the main script.

states probably aren't going to work as I'll be continually dumping the listens, never mind tracking simultaneous users... web server is a thought although it'll require tracking each user separately, and would require a webserver and might run a bit slow... trying to keep the solution simple enough for people to easily incorporate into their own existing products. I can't believe I didn't consider multiple scripts, at least for a moment although increasing script counts is something I'd rather avoid.

at this point I can encode the language into a hidden space on the dialog button, so selection of language to scan for commands from isn't an issue, mostly it's just how to retrieve language lists dynamically for multiple users without taking up a ton of space.

so far storage of a single language takes up ~50-75%+ of the total menu space (this is just the localized text labels, commands won't vary between languages) so each language is going to add roughly that to basic menu memory size (thankfully the code to switch between them shouldn't be much at all)
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Pete Olihenge
Registered User
Join date: 9 Nov 2009
Posts: 315
02-01-2010 02:06
From: Void Singer
at this point I can encode the language into a hidden space on the dialog button, so selection of language to scan for commands from isn't an issue, mostly it's just how to retrieve language lists dynamically for multiple users without taking up a ton of space.
Could you encode the label's significance, rather than the language, here?

list buttons = ["Bonjour #1", "Au revoir #2", "Oui #3", "Non #4", "Arrêter #5", "Aller #6"];

(The forum software has stripped the padding spaces from my example.)
Ron Khondji
Entirely unlike.
Join date: 6 Jan 2007
Posts: 224
02-01-2010 04:47
I think I would use several copies of the same script and use one script per language. This script would preload the language files and use the users key to create listeners so that each language script can handle several users.
And have a masterscript to assign users to the language scripts.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
02-01-2010 04:54
I could, althought the current paradigm already abstracts the button label separately from the actual command, so I don't know that it'll save much space wise... although now that I think about it I could probably store the button labels in a separate card, or at least ignore the command structure when reading them (just treat it as a label list)... might make it a pain to edit them though...

ETA:
@Ron, something along those line was how I was thinking Twisted's line of thought might work... could even go with a single listen, pass the button label return text out to the sub scripts and let them parse it either as an absolute, or with a language ID...

looks like I need to write up several examples and see what I can get out of them.
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Pete Olihenge
Registered User
Join date: 9 Nov 2009
Posts: 315
02-01-2010 06:44
I'm wondering if for each language notecard, it had the button labels arranged one line per menu, so each time a menu was needed you'd only have to read that one particular line from that one particular notecard - I don't see anything in the wiki about having to access notecard lines sequentially to get to the one you want, so I wouldn't have thought this would take all that much time.

The notecard might end up with some duplicated words, and it'd have to be put together in the right order, but doing so would be a one-off task.

For the initial language choice menu you'd just keep a list of available laguage notecard names - assuming it'd be ok to call the notecards "English", "Français", "Español", etc.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
02-01-2010 10:33
From: Pete Olihenge
I'm wondering if for each language notecard, it had the button labels arranged one line per menu, so each time a menu was needed you'd only have to read that one particular line from that one particular notecard - I don't see anything in the wiki about having to access notecard lines sequentially to get to the one you want, so I wouldn't have thought this would take all that much time.

The notecard might end up with some duplicated words, and it'd have to be put together in the right order, but doing so would be a one-off task.

For the initial language choice menu you'd just keep a list of available laguage notecard names - assuming it'd be ok to call the notecards "English", "Français", "Español", etc.

you can request any line you want, assuming you know the line number... which means all notecards would need identical formatting and a very specific layout.

the basic layout looks like this (might as well show what we're working with, I don't think it'll change anything at this point)

CODE

//-- menu format, main menu goes first, buttons go after menus, max twelve,
//-- text | entry type | value assigned/sent
//-- menu
This is the Main Menu | MNU | MNU-0
Menu A... | CMD | MNU-1
Menu B... | CMD | MNU-2

//-- menu
This is Menu A | MNU | MNU-1
Prim Msg | MSG | Do Some Menuy Thing

//-- menu
This is Menu B | MNU | MNU-2
Obj Msg | LNK | Trigger Some Other script
Main Menu... | CMD | MNU-0



CODE

//-- Static Heirarchical Dialogs with Localization

string gStrNcd;
integer gIntCnt;
integer gIntChn = -42;
key gKeyChck;

list gLstVMsg = ["Loading..."]; //-- space for future improvement
list gLstMTxt; // = ["This is the Main Menu", "This is Menu A", "This is Menu B"];
list gLstMBgn; // = [0, 2, 3];
list gLstBTxt; // = ["Menu A...", "Menu B...", "Prim Msg", "Obj Msg", "Main Menu..."];
list gLstBAct; // = [0, 0, -4, -1, 0];
list gLstBMsg; // = [1, 2, "Do Some Menuy Thing", "Trigger Some Other script", 0];

uDialog( integer vIntMnu, key vKeyUsr ){
llSetTimerEvent( 90.0 );
if (gIntCnt){
llListen( gIntChn, "", "", "" );
gIntCnt = !gIntCnt;
}
llDialog( vKeyUsr,
llList2String( gLstMTxt, vIntMnu ),
llList2List( gLstBTxt, llList2Integer( gLstMBgn, vIntMnu ), ~-llList2Integer( gLstMBgn, -~vIntMnu ) ),
gIntChn );
}

default{
state_entry(){
gIntChn = (integer)("0xF" + llGetSubString( (string)llGetLinkKey( LINK_THIS ), 0, 6 ));
gStrNcd = llGetScriptName() + ".cfg";
string vStrLng = "." + llGetAgentLanguage( llGetOwner() );
if (~llGetInventoryType( gStrNcd + vStrLng )){
gStrNcd += vStrLng;
}
llSetTimerEvent( 0.01 ); //-- try not to dump changed events
}

changed( integer vBitChg ){
if (CHANGED_OWNER & vBitChg){
llResetScript();
}
}

timer(){
if (gLstMBgn != []){
state sReady;
}else{
state sLoadMenu;
}
}
}

state sLoadMenu{
state_entry(){
llSetTimerEvent( 10.0 );
gKeyChck = llGetNotecardLine( gStrNcd, (gIntCnt = 0) );
}

changed( integer vBitChg ){
if (CHANGED_OWNER & vBitChg){
llResetScript();
}
}

dataserver( key vKeyChk, string vStrDta ){
if (gKeyChck == vKeyChk){
if (vStrDta == EOF){
/*//-- Map Menu Names to Indexes For Smaller Size --//*/
integer vIntCnt = ~-(gLstMBgn != []);
while (vIntCnt > 0){
list vLstTmp = (list)llList2String( gLstMBgn, vIntCnt );
while (~(gIntCnt = llListFindList( gLstBMsg, vLstTmp ))){
gLstBMsg = llListReplaceList( gLstBMsg, (list)(vIntCnt / 2), gIntCnt, gIntCnt );
}
vIntCnt -= 2;
}
gLstMBgn = llList2ListStrided( gLstMBgn, 0, -1, 2 );
gKeyChck = "";
//-- still need to resequence button orders, here or in in the dialog build
llSetTimerEvent( 0.01 ); //-- try not to dump changed events on state change
}else{
vStrDta = llStringTrim( llDeleteSubString( vStrDta + " ", llSubStringIndex( vStrDta, "//" ), -1 ), STRING_TRIM );
if (vStrDta != "" && (~llSubStringIndex( vStrDta, "|" ))){
list LstDta = llParseString2List( vStrDta, ["|"],[] );
if ((LstDta != []) > 2){
integer act = llSubStringIndex( "MNUCMDLNK MSG", llStringTrim( llList2String( LstDta, 1 ), STRING_TRIM ) ) / 3;
if (act){
gLstBTxt += (list)llStringTrim( llList2String( LstDta, 0 ), STRING_TRIM );
gLstBAct += (list)(-act + 1);
gLstBMsg += (list)llStringTrim( llList2String( LstDta, 2 ), STRING_TRIM );
}else{
gLstMTxt += (list)llStringTrim( llList2String( LstDta, 0 ), STRING_TRIM );
gLstMBgn += [(gLstBAct != []), llStringTrim( llList2String( LstDta, 2 ), STRING_TRIM )];
}
}
}
gKeyChck = llGetNotecardLine( gStrNcd, ++gIntCnt );
}
}
}

timer(){
if (gKeyChck){
llOwnerSay( llList2String( gLstVMsg, 0 ) ); //-- improve later
}else{
state sReady;
}
}
}

state sReady{
state_entry(){
llSetTimerEvent( 0.0 ); //-- testing shows this is safe
gIntCnt = TRUE;
}
/*//-- Touch Handler for Menu Triggering --//*/
touch_end( integer vIntTch ){
do{
uDialog( 0, llDetectedKey( --vIntTch ) );
}while (vIntTch);
}
/*//-- Link Handler for Menu Triggering --//*/
link_message( integer vIntSrc, integer vIntNul, string vStrCmd, key vKeyUsr ){
if (llGetScriptName() == vStrCmd){
uDialog( 0, vKeyUsr );
}
}
/*//-- Menu Response Handler --//*/
listen( integer vIntChn, string vStrNul, key vKeyUsr, string vStrMsg ){
integer vIdxFnd = llListFindList( gLstBTxt, (list)vStrMsg );
if (~vIdxFnd){
integer vIdxAct = llList2Integer( gLstBAct, vIdxFnd );
if (vIdxAct){
llMessageLinked( vIdxAct, 0, llList2String( gLstBMsg, vIdxFnd ), vKeyUsr );
}else{
uDialog( llList2Integer( gLstBMsg, vIdxFnd ), vKeyUsr );
}
}
}

changed( integer vBitChg ){
if (CHANGED_OWNER & vBitChg){
llResetScript();
}
}

/*//-- Timeout For Open Listen --//*/
timer(){
state sListenKill;
}
}
/*//-- State Reflector Cancels Listens --//*/
state sListenKill{
state_entry(){
state sReady;
}
}
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Talarus Luan
Ancient Archaean Dragon
Join date: 18 Mar 2006
Posts: 4,831
02-01-2010 10:48
I suppose it depends on the requirements of the design.

Will you need to support more than one language concurrently?

If not, then load one language set at a time and give the users the opportunity to switch to another language initially, and whenever they want.

Otherwise, loading all languages is probably not an option; however, some kind of cache system with a precaching mechanism might work best. IE, load all the main menus from all the languages into memory, then loading others dynamically and reusing the ones from the cache.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
02-01-2010 11:58
From: Talarus Luan
I suppose it depends on the requirements of the design.

Will you need to support more than one language concurrently?

If not, then load one language set at a time and give the users the opportunity to switch to another language initially, and whenever they want.

Otherwise, loading all languages is probably not an option; however, some kind of cache system with a precaching mechanism might work best. IE, load all the main menus from all the languages into memory, then loading others dynamically and reusing the ones from the cache.

the version above will allow for loading languages one at time, and will handle multiple simultaneous users... the hope is to improve it to handle those same users in multiple simultaneous languages as well. all that with minimal script overhead and easy setup/integration.

there's still several improvements to be made on the above base script, to handle dynamic sub menus, some additional actions like user variables, and special inputs from open chat.... in that version those can be done by an outside script.
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Pete Olihenge
Registered User
Join date: 9 Nov 2009
Posts: 315
02-01-2010 14:20
I'd be inclined to consider seperating the two aspects: define the menu system structure in one notecard, and make part of that definition the line number to look at for the button labels in the seperate language-specific notecards.

And though that would mean formatting all the the language notecards so that the equivalent labels were all in the correct position on the correct line I can't really see why that should be a problem; if someone is capable of defining a menu system's structure, then surely they'd be capable of arranging some text labels in the right order.

At least I think that's the approach I'd try if I were starting from scratch.
Twisted Pharaoh
if ("hello") {"hey hey";}
Join date: 24 Mar 2007
Posts: 315
02-02-2010 21:28
OK I have looked a little bit.

Main script would do something like that:

CODE

/*//-- Touch Handler for Menu Triggering --//*/
touch_end( integer vIntTch ){
key k ;
string lang ;
do{
k = llDetectedKey( vInTch--) ;
lang = getUserLanguage(k) ;
llMessageLinked(LINK_THIS, NLINK_UDIALOG, lang, k);
}while (vIntTch);
}


And then the language scripts this:

CODE

link_message(integer sender_num, integer num, string str, key id)
{
if ((num == NLINK_UDIALOG) && (str == MYLANG))
{ // hey, it's for me!
uDialog(....
}

}


The language script would do the listen send return the result with another linked message. That way you could have several language users playing with the menu simultenaously.

You would need to find a way to fall back to default language if the lang is not found.

So instead of calling getAgentLanguage, you need a user function getUserLanguage() wich will return the default language.

You could also further improve by shutting down the inactive language scripts then call llGetScriptState() and restart them if inactive.
_____________________
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
02-02-2010 22:16
From: Twisted Pharaoh
You could also further improve by shutting down the inactive language scripts then call llGetScriptState() and restart them if inactive.

hah, so simple... I was getting caught up on avoiding the extra scripts I forgot that I could just shut em down.... ::facepalm::
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -