[Wiki] Les 1001 Trucs et Astuces des scripteurs PART I

Répondre
Partager Rechercher
Ce message est en mode wiki. Tous les comptes de plus de 30 jours avec plus de 30 messages peuvent le modifier.
Je propose de mettre en commun tous les trucs et astuces connus et moins connus qui remplissent vos scripts, qu'ils soient issus de vos lectures pertinentes ou de votre propre imagination et expérience qu'importe! Partagez les ...
Citation :
  • Ce wiki ne doit pas être un lieu de messages, afin de qu'il garde une structure lisible et bien organisée.
  • Chaque entrée devra commencer par petite explication.
  • Un exemple de code possible, clair et commenté facilitant la lecture et la compréhension est de rigueur.
  • Les ligne de codes LSL présentées doivent utiliser les balises PHP pour garantir leur structure de code LSL.
  • Un petit commentaire final sur l'utilisation ou les limites de l'astuce sera le bien venu.
Merci de respecter ces petites clauses pour le bien de tous.

Seb,

PS: Si un astuce / truc ne fonctionne pas ou plus, présente des limites non énoncées etc... déposez une alerte chez le dépositaire si pas de réaction intervenez ici.


-Pour insérer un article-
Code HTML:
1. Sélectionner modifier le wiki.
2. Entrer vote titre entre les balises [titre]Votre titre[/titre].
3. Laisser 1 ligne blanche.
4. Entrer votre nom en gras entre les balises [B]Votre Nom[/B]. 
5. Laisser 3 lignes blanches.
6. Insérer votre article.
7. Entrer le motif de modification.
8. Enregistrer.







Générer un numéro de canal


Par Seb_01



Il est souvent assez pénible d'affecter un numéro de channel dans les coms.


Cette petite routine devrait vous aidez quelques fois...


Code PHP:

integer channel(key uuid// Retourne un channel (integer)
{
    return (integer)(
"0x"+llGetSubString((string)uuid,-8,-1)); // utilisation du codage hexa  ("0x")

Génère un nombre entier issu des 8 derniers caractères codés hexa d'un UUID passé en paramètre de fonction.


Simple exemple:

Code PHP:

integer     Channel1;  
integer     Channel2;  
integer     Channel3;  

integer channel(key uuid// Retourne un channel (integer)
{
    return (integer)(
"0x"+llGetSubString((string)uuid,-8,-1)); // utilisation du codage hexa  ("0x")
}


default
{
    
on_rez (integer param)
    {
        
Channel1 channel llGetKey() );   //  peu confidentiel  mais le - peut devenir un +++    
        
Channel3 channel llGetOwner());  //   si on veut 1 comm  bilatérale  
        
        
Channel2 channel llGetInventoryKeyllGetScriptName()));  // plus confidentiel
        // etc ...
          
        
llOwnerSay ((string) Channel1 " " +  (string) Channel2 " " +  (string) Channel3 );
    }

}
//  Peut afficher: 563253999 -1886078597 1456048149 
Citation :
Remarque:

* Suivant besoin de confidentialité on peut renforcer la routine en ajoutant des caractères par exemple...
* On peut forcé un integer négatif ...
* Un UUID peut être connu et...partageable (pas le FRAND)
* etc...

Cleanup sélectif et rapide.


Par Seb_01



Basé sur les avertissements de l'instruction "state"

On state change:

* All listens are released.
* The event queue is cleared.
* Repeating sensors are released.
* The timer event clock is not cleared.

Code PHP:

default
{
    ...
    {
         ...
       
// n'importe où dans le code //
        
state Cleanup;
         ...
    }
   ...
}

state Cleanup
{
    
state_entry()   // fermera tous les listens / les sensors /  events en cours sauf le timer
    
{                      
        
state defaut;   // ou autre state
    
}

Citation :
Remarques:

Fermerture ciblée ultra rapide ...

Passer des paramètres à un script


Par Seb_01



Tous les programmes que l'on lance en informatique offre la possibilité de "passer" des paramètres à leur lancement...

exemple au hasard :
Citation :
"C:\Program Files\GreenLife Emerald Viewer\GreenLife.exe" --channel "GreenLife Emerald Viewer" --settings settings_emerald.xml -set SystemLanguage en-us -multiple
Quand j'ai commencé à scripter j'ai été très vite désorienté par cette lacune LSL et fait sans.

Puis un jour cette option est devenu incontournable pour mes projets alors j'ai cogité une paire d'heure...

Je vous livre une de mes actuces figurant au TOP 5, je suis sûr qu'elle tiendra toutes ses promesses.



Mode 1: Setup par le script lui même

Code PHP:

string  Save_Desc;

string  Message " Merci d'avoir acheter ce produit etc...";


Verify_Option_Rez ()
// on teste si une option a été programmée
    
    
string  obj_desc llGetObjectDesc();  
    
    if ( 
obj_desc == Save_Desc)
        return;
    else if ( 
obj_desc == Save_Desc "-reset" )
        
llResetScript ();
    else if ( 
obj_desc == Save_Desc "-vente" )
        
llOwnerSay Message );
        
    
llSetObjectDescSave_Desc );   // on remet la valeur originale
}

default
{
    
on_rezinteger start_params )
    {
        
Verify_Option_Rez();   // on verifie les options de redémarrage
        
    
}
    
...
        
        
Save_Desc llGetObjectDesc(); //  uniquement si  la description peut être modifiée par le owner
        
        
llSetObjectDescSave_Desc "-vente" ); // L'objet est prêt pour la vente ;)
        
        
        //llSetObjectDesc( Save_Desc + "-reset" ); // Le script doit être reseter!
...      
    }

Dans le script lui même on définit si besoin l'option du prochain rez par l'utilisation du champs Description de l'objet.



Mode 2: on passe le paramètre (ou plusieurs) par l'éditeur de l'objet dans le champs Description.

Code PHP:

integer Debug_Mode;


Verify_Option_Rez ()
// on teste si une option a été programmée
    
    
string  obj_desc llGetObjectDesc();  
    
    if ( 
obj_desc == save_Desc)
        return;
    else if ( 
obj_desc == save_Desc "-reset" )
        
llResetScript ();
    else if ( 
obj_desc == save_Desc "-debug" )
        
Debug_Mode TRUE;
        
    
llSetObjectDescsave_Desc );   // on remet la valeur originale
}




default
{
    
on_rezinteger start_params )
    {
        
Verify_Option_Rez();   // on verifie les options de redémarrage
    
}

    ....
    if (
Debug_Mode)
        
llOwnerSay ( )
    ....

    if (
Debug_Mode)
        
llOwnerSay ( )
    ....

    if (
Debug_Mode)
        
llOwnerSay ( )
    ....

On peut activer le mode debug sans modifier le script..



Dernier exemple:

Vous avez vendu un objet mais malheureusement il présente un bug et vous passez votre temps à chercher à comprendre etc..

Mais comme vous ne faites PLUS de reset intempestifs suite à la lecture de:

https://forums.jeuxonline.info/showthread.php?t=1023670

vous demandez à votre gentil client le retour de cet objet et vous ajoutez -debug dans la description de l'objet ( Dans VOTRE inventaire option propriétés) et votre écran affiche par magie le contexte du plantage ( si vous avez pris le temps de prévoir l'utilisation de cette option)

Code PHP:

integer Debug_Mode;
string  Var1 "valeur1";
string  Var2" valeur2";
string  Save_Desc "V1.0";

Verify_Option_Rez ()
// on teste si une option a été programmée
    
    
string  obj_desc llGetObjectDesc();  
    
    if ( 
obj_desc == Save_Desc)
        return;
    else if ( 
obj_desc == Save_Desc "-reset" )
        
llResetScript ();
    else if ( 
obj_desc == Save_Desc "-debug" )
    
Dump_Mode ();

        
    
llSetObjectDescSave_Desc );   // on remet la valeur originale
}

Dump_Mode ()
{
    
Debug_Mode TRUE;
    
    
llOwnerSay "Var1: " Var1 );
    
llOwnerSay "Var2: " Var2 );
 
    
// etc..
    // etc..
    // etc..

}

default
{
    
on_rezinteger start_params )
    {
        
Verify_Option_Rez();   // on verifie les options de redémarrage
    
}

    
state_entry()
    {
        
llSetObjectDescSave_Desc );      
    }

//....
    
if (Debug_Mode)
        
llOwnerSay "");
    
//....

    
if (Debug_Mode)
        
llOwnerSay ("" );
    
//....

    
if (Debug_Mode)
        
llOwnerSay ("" );
    
//....


Citation :
Remarques:

Attention il y a 1 bug concernant le llSetObjectDesc() concernant les attachments
La description est modifiable par script mais ... pas pris en compte de retour dans l'invent!
http://jira.secondlife.com/browse/SVC-3429

Optimiser vos requêtes


Par Seb_01

Tous les accès au serveurs Dataserver (llGetNotecardLine llGetNumberOfNotecardLines llRequestAgentData llRequestInventoryData llRequestSimulatorData), HTTPserver etc... se font traditionnellement à 99,99% de la façon suivante:

J'envoie une requète , j'attends (sagement) la réponse du serveur je teste si la id réponse = id requête et je renvois une nouvelle requêtes etc...

ce qui se code ainsi

exemple 1
Code PHP:

key http_request_id;
 
default
{
....
    
http_request_id = ( requète 1)
....

    
http_response(key request_idinteger status, list metadatastring body)
    {
        if (
request_id == http_request_id)
        {
              
// sauvegarde des datas / test/ décision etc... 
              
http_request_id = (requête 2)      
              ....
        }
    } 
exemple 2

Code PHP:

key IdQuery1;
key IdQuery2;
default
{
    
touch_start(integer total_number)
    {
        
IdQuery1(requête 1)  ;

    }
    
dataserver(key queryIdstring data
    {
        if (
queryId == IdQuery1
        {
              
// sauvegarde des datas / test/ décision etc... 
             
IdQuery2= (nouvelle requête) ;
        } 
        else if (
queryId == IdQuery2
        {
              
// sauvegarde des datas / test/ décision etc...
        
}
    }

Certainement dù aux avertissements du wiki:

Citation :
* Dataserver answers do not necessarily come in the order they were requested.
o If there are multiple pending requests, always use the queryid key to determine which answer is being received.
* Dataserver requests will trigger dataserver events in all scripts within the same prim where the request was made.
o If there are multiple scripts with dataserver events in the same prim, always use the queryid key to determine which answer is being received.
o dataserver events will not be triggered in scripts contained in other prims in the same linked object.
* If more data is requested using the same query_id before the dataserver event dealing with that query_id has resolved the data will be trashed. To avoid the trashing do not ask if(query_key == query_id). Though (see above caveats) this has its own drawbacks.

* Requesting data from within the dataserver event is quite valid. However, be aware not to make your own loop inside the event. It will lock the event open until the loop is finished then send all the requests made from within, with the data from the first retrieval.
Toujours est il que les serveurs fonctionnent toujours de la même façon sensors + messages linked + listens et de façon plus général tous les events, ils utilisent
une file d'attente d'une profondeur de 64 sauf avis contraire ou restriction clairement établis ( HTTPrequest par exemple).

Les dataservers et Httpservers n'échappent pas à cette règle, et la petite omission de Linden est un peu compréhensive ceci dit...

Partant de ce principe les handles de ces serveurs peuvent alors être gérés complètement différemment.

1 . On empile la file d'attente (dans les limites indiquées) et on continue son code..
2. On recoit un évènement du serveur on dépile la file d'attente.


Style de code utilisé:

on empile...
Code PHP:

    for (0nrequetes1)
        
List_Handles += (list)(requete); 
on dépile...
Code PHP:

     dataserver(key query_idstring data
    {
        
integer index llListFindList(List_Ids, (list)query_id);

        if ( 
index  != -)  
            ...   
save/test/decision...
    } 
J'utilise cela depuis des mois et vous aussi à travers certains de mes produits ...


Je vous donne un petit code à titre d'exemple qui permet de lire une notecard en deux coups de cuillère à pot.
Code PHP:

string  NoteCard "note_test";
key     Qline;
list    
List_Ids;

read_data (integer nlignes)
{
// MAXIMUM 63 lignes blanches comprises (limite queue dataserver) //
    
integer i;
    
key id;
    
List_Ids = [];
    for (
0nlignes1)
        
List_Ids += (list)llGetNotecardLine(NoteCardi);   
}


default {

    
touch_start integer number
    {
        
Qline  llGetNumberOfNotecardLines(NoteCard);
    }
     
dataserver(key query_idstring data
    {
        
integer index llListFindList(List_Ids, (list)query_id);
         
        if (
query_id == Qline
            
read_data ( (integer) data);
        else if ( 
index  != -)  
            
llOwnerSay (data);
    }

Copier ce script dans un script et aussi dans une notecard appelée NoteCard

vous obtiendrez cela
Code PHP:

[3:46]  Objectstring  NoteCard "note_test";
[
3:46]  Objectkey     Qline;
[
3:46]  Object: list    List_Ids;
[
3:46]  Object
[
3:46]  Objectread_data (integer nlignes)
[
3:46]  Object: {// MAXIMUM 63 lignes blanches comprises (limite queue dataserver) //
[3:46]  Object:     integer i;
[
3:46]  Object:     key id;
[
3:46]  Object:     List_Ids = [];
[
3:46]  Object:     for (0nlignes1)
[
3:46]  Object:         List_Ids += (list)llGetNotecardLine(NoteCardi);   
[
3:46]  Object: }
[
3:46]  Object
[
3:46]  Object
[
3:46]  Object: default {
[
3:46]  Object
[
3:46]  Object:     touch_start integer number
[
3:46]  Object:     {
[
3:46]  Object:         Qline  llGetNumberOfNotecardLines(NoteCard);
[
3:46]  Object:     }
[
3:46]  Object:      dataserver(key query_idstring data
[
3:46]  Object:     {
[
3:46]  Object:         integer index llListFindList(List_Ids, (list)query_id);
[
3:46]  Object:          
[
3:46]  Object:         if (query_id == Qline
[
3:46]  Object:             read_data ( (integer) data);
[
3:46]  Object:         else if ( index  != -)  
[
3:46]  Object:             llOwnerSay (data);
[
3:46]  Object:     }
[
3:46]  Object: } 
Je posté ICI une version plus aboutie basée exactement sur principe.


remarques
Citation :
Respecter le max des files d'attente toutes requêtes confondues
Pensez aux limites données ( ex dataserver 255 caractères max...)
Pour le Http respecter cela
http://forums.secondlife.com/showthread.php?t=109571

Opérateurs de bits


Par BlackShade Nightfire



Il arrive souvent de vouloir envoyer plusieurs paramètres que ce soit à un objet ou un autre script. Il est possible d'échanger ces "paramètres" grâce aux opérateurs de bits que nous stockons dans un seul integer. Nous allons utiliser ceux-là :
Citation :
& Opérateur ET
| Opérateur OU
~ Opérateur NON
^ Opérateur OU Exclusif
Un bon exemple pour vous montrer la simplicité de ces opérateurs :

Code PHP:

Testinteger p ) {

    
// test des valeurs paramètres avec l'opérateur ET &
    
list pl;
    if( 
PARAM_01 pl += ["PARAM_01"];
    if( 
PARAM_02 pl += ["PARAM_02"];
    if( 
PARAM_03 pl += ["PARAM_03"];
    if( 
PARAM_04 pl += ["PARAM_04"];
    if( 
PARAM_05 pl += ["PARAM_05"];
    if( 
PARAM_06 pl += ["PARAM_06"];

    if( 
pl != [] )
        
llSay0"Parametres : " llList2CSV(pl) );
    else
        
llSay0"aucun parametre !" );

}




integer PARAM_01 0x01;
integer PARAM_02 0x02;
integer PARAM_03 0x04;
integer PARAM_04 0x08;
integer PARAM_05 0x10;
integer PARAM_06 0x20;
// etc...


integer params;

default
{

    
state_entry() {

    
// Assignation des paramètres avec l'opérateur OU |
    
params PARAM_01 PARAM_02 PARAM_06 PARAM_03;
    
Testparams );
    
// Parametres : PARAM_01, PARAM_02, PARAM_03, PARAM_06


    //-------------------------------------------------------------------------------
    
    
    // On ajoute un autre paramètre encore avec l'opérateur OU |
    
params  params |PARAM_04;
    
Testparams );
    
// Parametres : PARAM_01, PARAM_02, PARAM_03, PARAM_04, PARAM_06


    //-------------------------------------------------------------------------------
    
    
    // On enlève un paramètre avec l'opérateur NON ~ et ET &
    
params params & ~PARAM_03;
    
Testparams );
    
// Parametres : PARAM_01, PARAM_02, PARAM_04, PARAM_06


    //-------------------------------------------------------------------------------

    

    // On enlève 2 paramètres avec l'opérateur NON ~ , ET & et OU |
    
params params & ~( PARAM_01 PARAM_02 );
    
Testparams );
    
// Parametres : PARAM_04, PARAM_06


    //-------------------------------------------------------------------------------



    // On utilise le XOR (OU exclusif) ^ pour inverser les paramètres (si le paramètre est présent il l'enleve, sinon il l'ajoute)
    
params =  params^PARAM_04;
    
Testparams );
    
// Parametres : PARAM_06
    
params =  params^PARAM_04;
    
Testparams );
    
// Parametres : PARAM_04, PARAM_06


    //-------------------------------------------------------------------------------



    // Ajout de plusieurs paramètres avec l'opérateur OU |
    
params params PARAM_01 PARAM_02 PARAM_05;
    
Testparams );
    
// Parametres : PARAM_01, PARAM_02, PARAM_04, PARAM_05, PARAM_06


    //-------------------------------------------------------------------------------


    // On test plusieurs valeurs avec l'opérateur OU | et ET &
    
if( params & (PARAM_02 PARAM_05) )
        
llSay0"PARAM_02 et PARAM_05 sont bien presents !" );
    
// PARAM_02 et PARAM_05 sont bien presents !
    
        
   
}



Edition des liens dans un linkset


Par Ahuri Serenity

Un linkset est un ensemble de prims liées. Pour faire communiquer des scripts au sein d'un linkset, le mieu est d'utiliser les messages liés à savoir le couple llMessageLinked / link_message :

Citation :
llMessageLinked( integer linknum, integer num, string str, key id );

Déclenche un évènement link_message avec les paramètres num, str et id dans le groupe ou le lien linknum.
Citation :
link_message( integer envoyeur_num, integer num, string car, key id ){ ; }

Déclenché lorsqu'un script reçoit un message lié par un appel à la fonction llMessageLinked. llMessageLinked est utilisé pour envoyer des informations entre scripts
Le souci majeur lorsqu'on utilise la fonction llMessageLinked est le suivant :
Il faut que le(s) destinataire(s) du message se reconnaissent. Pour cela, il existe différentes méthodes plus ou moins bonnes. La majorité des gens ne veulent pas se prendre la tête et envoi leur message sur LINK_ALL_OTHERS, LINK_SET, ... quand ca n'a pas lieu d'etre. Autant dire que si vous avez pleins de scripts qui communiquent dans tous les sens c'est le chaos, et niveau lag on trouvera mieux

Alors, j'entend déjà certains me dire "Oui t'es bien gentil, mais je connais pas les linknumber de mes destinataires je veux pas m'embeter avec ca !", et bien ils ont raisons de rouspeter car cela nous amène à la suite : l'édition des liens.

Voici donc une solution super simple pour alleger vos communications et rendre votre système beaucoup plus efficace !

Principe :
Il s'agit de donner a chaque module les linknumbers des modules avec lesquels il communique. Cela se fait de manière automatique et autonome. On fait donc ce que j'appel une édition des liens. Cela évite d'aller déranger les modules voisins pour rien par contre si vous devez envoyer un message groupé au linkset utilisez directement LINK_*.

Regles :
Répartir votre linkset en modules. Chaque module possède un nom clé (unique dans le linkset) sauf pour les modules dans une même prim qui doivent avoir le même nom clé.

Ensuite dans chaque module :

Ajouter la variable globale NOM_OBJET qui contient le nom du module :

Code PHP:

string NOM_OBJET "Gamer"
Toujours en global, définir les couples de variables de liaison :

Code PHP:

integer    iLINK_TARGET_SAVE;    // link number du module
string    sLINK_TARGET_SAVE "Loader";    // nom module

integer    iLINK_TARGET_PVRAFF;    // link number du module
string    sLINK_TARGET_PVRAFF "PvrAff";        // nom module

integer    iLINK_TARGET_HPAFF;    // link number du module
string    sLINK_TARGET_HPAFF "HpAff";        // nom module

// etc ... 
A chaque couple de variables correspond un module destinataire.

Il faut maintenant définir nos fonctions d'édition de liens :

Code PHP:

// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' //
// ''                                                                   '' //
// ''     getLinkNumber : Obtenir le link number d'un module ayant un   '' //
// ''                     nom donné.                                    '' //
// ''                     Envoi LINK_SET si rien n'est trouvé pour      '' //
// ''                     envoyer le message a toutes les prims.        '' //
// ''                                                                   '' //
// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' //
integer getLinkNumber(string name)
{
    
integer i;
    
integer max llGetNumberOfPrims();
    
    for(
i=2max 2; ++i)
        if (
llGetLinkName(i) == name)
            return 
i;

    return 
LINK_SET;
}

// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' //
// ''                                                                   '' //
// ''     setLinks : Faire l'edition des liens entre les différents     '' //
// ''                modules pour les communication en linkmessage.     '' //
// ''                                                                   '' //
// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' //
setLinks()
{
    
llSetObjectNameNOM_OBJET);

    
iLINK_TARGET_SAVE getLinkNumber(sLINK_TARGET_SAVE);
    
iLINK_TARGET_PVRAFF getLinkNumber(sLINK_TARGET_PVRAFF);
    
iLINK_TARGET_HPAFF getLinkNumber(sLINK_TARGET_HPAFF);

La première fonction retourne le linknumber du module dont le nom est passé en parametre. La seconde fonction permet de faire l'édition des liens entre le nom du module et son linknumber grâce aux couples de variables. Cette dernière fonction est à appeller au tout début du fonctionnement de votre script (ex : state_entry de l'etat default, on_rez ... ) et a chaque fois que le link est amené a changer.

Voila pour la démarche de construction. Maintenant vous pouvez utiliser vos linknumbers comme bon vous semble, exemple de focntion l'utilisant :

Code PHP:

// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' //
// ''                                                                   '' //
// ''     updateJaugeHp : Mettre a jour l'affichage de la jauge de vie  '' //
// ''                                                                   '' //
// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' //
updateJaugeHp()
{
    
llSetLinkPrimitiveParamsiLINK_TARGET_HPAFF, [PRIM_SIZE, < 0.02, (fHp fDimJaugePleine) / fHp_max0.02> ]);

Citation :
llMessageLinked( iLINK_TARGET_SAVE, -5, "exemple", NULL_KEY);
Le message sera envoyé directement au module concerné sans gener les autres.

Intérets :
  • communication allegée
  • clarification
  • pas besoin de se soucier de la valeur des linknumbers, ca se fait tout seul, pratique

defauts : Implémentation statique et nécéssité d'appeler setLinks a chaque changement de linkset.



Edition des liens dans un linkset (Alternative)


Par Seb

L'idée d'Ahuri sur l'adressage des llMessageLinked directement aux prims concernées n'est pas discutable si nous avons un nombre conséquent de prims.

Le code si dessous présente une alternative qui peut être intéressante dans certains cas :


Principe:


Utilisation d'une liste de paires ( nom de prim, numéro de link)


Code PHP:

Build_Index_Prims()
{
    
integer i;
    
Index_Prims = [];
    
integer max llGetNumberOfPrims()+1;
    
    for(
i=1max; ++i)
    {    
        
Index_Prims += [llGetLinkName(i),i]; // chargement du linkset dans l'index
    
}

Surveillance du linkset

Code PHP:

   changed(integer change)
    {
        if (
change CHANGED_LINK// si modification du linkset on reconstruit le tableau
            
Build_Index_Prims();
    } 

Obtenir le numéro de link à partir d'un nom

Code PHP:

integer Name2Link string name_link)
{
// on recherche dans la liste index du nom et on prend la valeur à l'index + 1
    
return llList2Integer (Index_PrimsllListFindListIndex_Prims, [name_link]) +1);

Pour récupérer un numéro

Citation :
Name2Link ("nom de la prim destinataire");
Voilà c'est tout.

Un exemple complet avec 7 prims

Si l'objet avait 2 ou 40 prims le code du script ne changerait pas puise que le script se
charge de construire l'index du link_set.
Et que le nom des modules n'ont pas à être déclarés



Changement de couleur de 3 prims

Code PHP:

string  RC "\n";
list    
Index_Prims;


Build_Index_Prims()
{
    
integer i;
    
Index_Prims = [];
    
integer max llGetNumberOfPrims()+1;
    
    for(
i=1max; ++i)
    {    
        
Index_Prims += [llGetLinkName(i),i];
    }
    
llSay(0RC llDumpList2String(Index_Prims,RC));
// has returned  Root,1,Child funny,2,Child shy,3,Child angry,4,Child sad,5,Child happy,6,Child nice,7
}

integer Name2Link string name_link)
{
    return 
llList2Integer (Index_PrimsllListFindListIndex_Prims, [name_link]) +1);
}

default
{
    
state_entry()
    {
        
Build_Index_Prims();
        
llSetLinkColor LINK_SET, <1.0,1.0,1.0>, ALL_SIDES );

    }

    
touch_start(integer total_number)
    {
        
llSetLinkColor Name2Link ("Child sad"), <1.0,0.0,0.0>, ALL_SIDES );
        
llSetLinkColor Name2Link ("Child funny"), <0.0,1.0,0.0>, ALL_SIDES );
        
llSetLinkColor Name2Link ("Child angry"), <0.0,0.0,1.0>, ALL_SIDES );
    }
    
   
changed(integer change)
    {
        if (
change CHANGED_LINK)
            
Build_Index_Prims();
    }


Remarques:

Citation :
* Code fixe quelque soit le nombre de prims,
* Plus de paramétrage de noms de module à déclarer.
* Utilisation simple (fonctions).


Créer des menus dynamiques


Par Bestmomo

Je vois presque systématiquement des menus (boites bleus) qui proposent des options qui comprennent le choix déjà en cours, ce qui est vraiment illogique. Voici un exemple standard de menu avec 3 options :

Code PHP:

list MENU = ["un","deux","trois"];
integer CANAL = -65214;
integer listen_hd;
default
{
    
touch_start(integer total_number)
    {
        
listen_hd llListen(CANAL""NULL_KEY"");
        
llDialog(llDetectedKey(0), "Faites votre choix"MENUCANAL);
        
llSetTimerEvent(30.0);
    }
    
listen(integer channelstring namekey idstring message)
    {
        
llSetTimerEvent(.0);
        
llListenRemove(listen_hd);
        if(
message == "un")
            
llOwnerSay("un");
        if(
message == "deux")
            
llOwnerSay("deux");
        if(
message == "trois")
            
llOwnerSay("trois");
    }
    
timer()
    {
        
llSetTimerEvent(.0);
        
llListenRemove(listen_hd);
    }

Vous remarquez que chaque fois qu'on clique on obtient les 3 options dans le menu. Voici une façon de gérer cela plus efficacement :

Code PHP:

list MENU = ["un","deux","trois"];
integer CANAL = -65214;
integer listen_hd;
string memo "un";
list 
dynamic_menu()
{
    
integer i llListFindList(MENU, [memo]);
    return 
llDeleteSubList(MENUii);
}
default
{
    
touch_start(integer total_number)
    {
        
listen_hd llListen(CANAL""NULL_KEY"");
        
llDialog(llDetectedKey(0), "Faites votre choix"dynamic_menu(), CANAL);
        
llSetTimerEvent(30.0);
    }
    
listen(integer channelstring namekey idstring message)
    {
        
llSetTimerEvent(.0);
        
llListenRemove(listen_hd);
        
memo message;
        if(
message == "un")
            
llOwnerSay("un");
        if(
message == "deux")
            
llOwnerSay("deux");
        if(
message == "trois")
            
llOwnerSay("trois");
    }
    
timer()
    {
        
llSetTimerEvent(.0);
        
llListenRemove(listen_hd);
    }

C'est pratiquement le même code mais j'ai ajouté une fonction qui crée la list en fonction du choix mémorisé. Maintenant le menu n'affiche plus que les 2 options possibles. J'ai simplifié le code au maximum pour mettre l'accent uniquement sur ce point là.



Formatage de texte dans le Chat


Par Bestmomo

Il arrive souvent d’afficher dans le Chat du texte destiné à figurer ensuite dans une notecard, se pose alors la question du formatage de ces données avec comme problèmes : apparition du nom de l’objet, limitation à 1023 caractères, délimitation cohérente des données… Voici une façon de faire :

Code PHP:

// -----------------------------------------------
//    Variables globales
// -----------------------------------------------
string         total;
string        nom;
// -----------------------------------------------
//    Constantes
// -----------------------------------------------
string         LIGNE1         =     ":::::::::::::::  Debut de la note  ::::::::::::::::::::";
string         LIGNE2         =     ":::::::::::::::  Suite de la note  ::::::::::::::::";
string         LIGNE3         =     ":::::::::::::::  Fin de la note    ::::::::::::::::::::::";
// -----------------------------------------------
//    Affichage dans le Chat pour sauvegarde
// -----------------------------------------------
affiche(string s) {
    if(
== "start") {
        
total "";
        
nom llGetObjectName();
        
llSetObjectName(LIGNE1);}
    else if(
== "end") {
        if(
llStringLength(total) > 0) {
            
llOwnerSay(total);
            
llSetObjectName(LIGNE3);
            
llOwnerSay(" ");}
        
llSetObjectName(nom);}
    else {
        if((
llStringLength(total) + llStringLength(s)) < 1024)
            
total += "\n" s;
        else {
            
llOwnerSay(total);
            
total "\n" s;
            
llSetObjectName(LIGNE2);}
    }
}
// -----------------------------------------------
//    Etat par défaut
// -----------------------------------------------
default
{
    
touch_start(integer total_number)
    {
        
// Création d'une valeur de test
        
string s "01234567890123456789012345678901234567890123456789";
        
// Envoi dans le Chat
        
affiche("start");
        
integer c;
        for(
040; ++c)
            
affiche(s);
        
affiche("end");
    }

Le seul élément qui subsiste sans qu’on puisse agir dessus par code est l’affichage de l’heure.

Astuce d'adressage et de communication entre scripts


Par BlackShade Nightfire


Dans des projets qui deviennent complexes et/ou complets, nous sommes amenés à utiliser une solution de partage des ressources et des fonctions dans plusieurs scripts. C'est tout à fait normal à cause de la limitation extrême du LSL. Nous en arrivons donc à utiliser toute sorte de techniques pour "détourner" les limites de ce langage.

Le thème abordé ici est la communication interne entre des scripts qui sont placés dans le même objet (Petite précision : J'appelle un objet un ensemble de prims. Si un prim est seul, ce n'est donc pas un prim mais un objet). Pour arriver à nos fins, nous utilisions une fonction prévue pour ça ainsi qu'un event associé à cette fonction.

La fonction (le script envoie) : llMessageLinked( integer numero_link, integer nombre, string data, key uuid );
L'event (l'autre script reçoit) : link_message( integer numero_link_envoyeur, integer nombre, string data, key uuid );


Pour mettre en ordre nos communications et leur permettre d'avoir une logique simple et facile à maintenir, nous allons utiliser un système d'adressage entre scripts. Un script peut posséder une ou plusieurs adresses. C'est grâce à ce système que nous pourrons demander une fonction spécifique à un autre script ainsi que recevoir une réponse.

Il y a donc :

  • Un script qui envoit un message avec une adresse par llMessageLinked
  • Un autre script qui reçoit ce message (ce script est celui ciblé par l'envoyeur avec son adresse).

Il se pose directement un autre problème : Comment le script receveur va pouvoir envoyer une réponse au script envoyeur qui ne serait seulement destinée qu'à lui seul ?

La solution : Le script envoyeur va devoir indiquer non seulement l'adresse de destination de l'autre script mais aussi une adresse à lui pour recevoir une possible réponse.

Nous avons vu précédemment que la fonction llMessageLinked se constituait de 4 paramètres :

  • [1] integer numero_link : Qui permet de définir le numéro du link d'un objet ou une famille de link(s) [LINK_THIS, LINK_SET, LINK_ALL_OTHERS etc]
  • [2] integer nombre : Ici nous pouvons indiquer un nombre que nous souhaitons communiquer
  • [3] string data : Ici nous pouvons indiquer une chaine de caractères que nous souhaitons communiquer
  • [4] key uuid : Ici nous pouvons indiquer une key ou une autre chaine de caractères que nous souhaitons également communiquer

Nous avons également vu que l'event link_message se constituait de 4 paramètres :

  • [1] integer numero_link_envoyeur : Le numéro du link de l'envoyeur
  • [2] integer nombre : Le nombre envoyé dans le paramètre [2] de llMessageLinked
  • [3] string data : La chaine de caractères envoyée dans le paramètre [3] de llMessageLinked
  • [4] key uuid : La key ou la chaine de caractère envoyée dans le paramètre [4] de llMessageLinked


Comment procéder ? Nous allons utiliser 3 paramètres pour une architecture de communication avec système d'adressage :

Pour llMessageLinked (script envoyeur) :

  • Le paramètre [2] nous servira à définir l'adresse de destination
  • Le paramètre [3] nous servira à envoyer une donnée quelconque
  • Le paramètre [4] nous servira à envoyer l'adresse de retour (celle pour la réponse destinée au script envoyeur)

Pour link_message (script receveur) :

  • Le paramètre [2] va nous permettre de vérifier si l'adresse indiquée par le script envoyeur sera bien destinée à ce script
  • Le paramètre [3] sera la donnée envoyée que nous pourrons traiter par la suite
  • Le paramètre [4] sera l'adresse de retour pour que le script receveur envoie une réponse au script envoyeur


Constitution d'une adresse ?

Quand je parle d'une adresse d'un script, il s'agit un nombre unique destiné à un script ou à une fonction d'un script (comme je l'ai précisé plus haut, un script peut avoir plusieurs adresses).




Exemple détaillé

Nous allons mettre en place une communication très simple. Nous avons 2 scripts (nommés respectivement A et B). Le script A voudrait utiliser une fonction qui retourne une valeur du script B. Pour cela, nous avons besoin de 2 adresses :

  • L'adresse de destination du script B pour atteindre sa fonction
  • L'adresse de réception de la valeur retournée par la fonction du script B (qui sera destinée au script A)

- Pour l'adresse de destination nous allons prendre : 45000
- Pour l'adresse de réception nous allons prendre : 120
[Note : ce sont des valeurs que j'ai pris au hasard, vous pouvez mettre l'adresse que vous voulez]



Script A :

Code PHP:

//...

touch_startinteger n ) {
     
      
llMessageLinkedLINK_THIS45000"test""120" );
}

link_messageinteger linkinteger adressstring datakey data2 ) {
        
        if( 
adress == 120 ) {
              
// ici nous reçevons la valeur de retour
              
llSay0"valeur fonction script B retournee : " data );
        }
}

// ... 

Script B :

Code PHP:

//...


link_messageinteger linkinteger adressstring datakey data2 ) {
        
        if( 
adress == 45000 ) {
              
llMessageLinkedlink, (integer)((string)data2), data "-ok!"NULL_KEY );
        }
}

// ... 

Pour le script A, dans l'event touch_start nous envoyons un message linked avec la fonction llMessageLinked en spécifiant :

  • 45000 : Qui est l'adresse du script B pour la fonction
  • "test" : Qui est une donnée que nous faisons parvenir à la fonction du script B
  • "120" : Qui est l'adresse de réception du script A de la valeur retournée de la fonction du script B

Toujours dans le script A, nous voyons l'event link_message. Pourquoi ? Parce dans cet exemple, l'event link_message du script A va nous permettre de recevoir la réponse du script B qui sera envoyée à l'adresse 120 (spécifiée dans la fonction llMessageLinked).

Nous utilisons donc une condition sur le paramètre [2] pour savoir si l'adresse envoyée est bien 120.



Pour le script B, nous avons seulement besoin de l'event link_message qui va vérifier si le paramètre [2] est bien l'adresse 45000 qui correspond à une de ses fonctions. Il va ensuite envoyer lui-même un llMessageLinked à l'adresse de retour (soit 120 dans cet exemple) pour faire parvenir la donnée.

Les points positifs de cette architecture : :

  • Très facile à faire et à maintenir
  • Un script peut recevoir plusieurs demandes à la fois sans risque de "collisions"
  • Ce système n'est pas une usine à gaz =)

Les points négatifs de cette architecture : :

J'en vois pas o.o


Pensez à céer vos propres fonctions et constantes.


Par Seb_01

Cet exemple de code n'est pas une astuce dans le sens usuel, simplement un exemple quand définissant VOS constantes et VOS fonctions vous gagnerez en temps et dans cette exemple en présentation plus agréable pour le lecteur.

Le code dans le bloc defaut n'est pas à prendre en considération juste le style de fonctions et les constantes

Je donnerai un deuxième exemple basé sur le premier pour rendre les données plus "maintenables".

Code PHP:

// Globales Constantes //

string    NL        "\n";      // Nouvelle ligne 
string    NLT      "\n\t";    // Nouvelle ligne + Tabulation 
string    NL2T    "\n\n\t"// 2 Nouvelles lignes + Tabulation 
string    NL2      "\n\n";   // 2 Nouvelles lignes + Tabulation  
string    TAB      "\t";      // Tabulation
string    TAB2    "\t\t";    //  Double tabulation
string    ARR      " => "//  Explicite
string    SUP      ">";
string    INF       "<";

// Globales Fonctions //
Screen (string text)
{
    
llOwnerSay NL text NL);
}

string Si integer i){
    return 
"  " + (string) "  ";}

string Ss string s){
    return 
" " " ";}

string Sf float f){
    return 
" " + (string) " ";}

string Sl ( list f){
    return 
" " llDumpList2String(f," - ");}

string Smc integer nombrestring char)
{
    
integer i;
string  final_char;

    for (
i=0<nombre; ++i)
    {
        
final_char += char;        
    }
    return 
final_char;
}


default
{
    
touch_start(integer total_number)
    {
        
integer i 32562362;
        
float    f  PI 0.1254;
        
         list 
= ["Integer",i,"Float",f];
        
        
string    string1 "Entrer votre nom,";
        
string    string2 "votre prénom,";
        
string    string3 "et votre age";

        
string output Smc(35,"_") + NL2T  "Ceci est une texte formaté qui présente" NL +  "la particularité d'offrir une vision plus propre" NL +
                        
"voire plus profesionnelle." NL Smc(35,"_") + NL2 "Prenons un exemple:" NL2 "Affichons la valeur PI:" NL2T TAB2 +  
                        
"PI" ARR Sf(PI) + NL2;

        
output +=     "Les integers sont faciles à présentés si on utilise " NL "la fonction Si(integer)" NL2T "Valeur de l'intéger" ARR Si(i) + NL2 
                    
"Que dire des décimaux si on utilise " NL "la fonction Sf(float)" NL2T "Valeur de du décimal" ARR Sf(f) + NL2 +
                    
"Que dire des listes si on utilise " NL "la fonction Sl(list)" NL2T "Liste" ARR Sl(l) + NL2 ;
        
        
output += "La fonction Ss(string) est utilisée pour formater les variables 'string' " NL2T SUP +string1 string2 string3 "< est peu lisible. " NLT 
                  
" avec Ss(string)" NLT SUP Ss(string1) + Ss(string2) + Ss(string3)+ "< cela le devient."NL2 TAB2 Smc(15,"=+");
        
        
Screen (output);
    }

Résultat sur l'écran.

Propre non?

Citation :
[8:16] Object:
___________________________________

Ceci est une texte formaté qui présente
la particularité d'offrir une vision plus propre
voire plus profesionnelle.
___________________________________

Prenons un exemple:

Affichons la valeur PI:

PI => 3.141593

Les integers sont faciles à présentés si on utilise
la fonction Si(integer)

Valeur de l'intéger => 32562362

Que dire des décimaux si on utilise
la fonction Sf(float)

Valeur de du décimal => 25.052570

Que dire des listes si on utilise
la fonction Sl(list)

Liste => Integer - 32562362 - Float - 25.052572

La fonction Ss(string) est utilisée pour formater les variables 'string'

>Entrer votre nom,votre prénom,et votre age< est peu lisible.
avec Ss(string)
> Entrer votre nom, votre prénom, et votre age < cela le devient.

=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

Le code mieux présenté avec des routines de mise en forme.


Code PHP:

// Globales Constantes //

string    NL       "\n";
string    NLT     "\n\t";
string    NL2T   "\n\n\t";
string    NL2T2 "\n\n\t\t";
string    NL2     "\n\n";
string    TAB     "\t";
string    TAB2   "\t\t";
string    TAB4   "\t\t\t\t";
string    ARR    " => ";
string    SUP    ">";
string    INF     "<";

// Globale Variables //

integer i 312457;

// Globales Fonctions //
Screen (string text)
{
    
llOwnerSay NL text NL);
}

string header (string line1string line2string line3 ){
    return 
Smc(35,"_") + NL2T  line1 NL +  line2 NL line3 NL Smc(35,"_") + NL2 ;}

string footer (string line1){
    return 
Smc(35,"_") + NL2T2 TAB4  line1 +  NL Smc(35,"_") + NL2 ;}

string demo1 (string line1string line2string line3 ){
    return 
line1 NL line2 NL2T line3 ARR Si(i) + NL2; }

string demo2 (string line1string line2string line3 ){
    return 
line1NL line2 NL2T line3  +  ARR  ;}

string Si integer i){
    return 
"  " + (string) "  ";}

string Ss string s){
    return 
" " " ";}

string Sf float f){
    return 
" " + (string) " ";}

string Sl ( list f){
    return 
" " llDumpList2String(f," - ");}

string Smc integer nombrestring char)
{
    
integer i;

string  final_char;

    for (
i=0<nombre; ++i)
    {
        
final_char += char;        
    }
    return 
final_char;
}


default
{
    
touch_start(integer total_number)
    {
        
float  f  PI 0.1254;
        list 
= ["Integer",i,"Float",f];
        
        
string  string1 "Entrer votre nom,";
        
string  string2 "votre prénom,";
        
string  string3 "et votre age";
        
        
string line1    "Ceci est une texte formaté qui présente"
        
string line2    "la particularité d'offrir une vision plus propre";
        
string line3    "voire plus profesionnelle.";      
        
string line4    "Les integers sont faciles à présentés si on utilise "
        
string line5    "la fonction Si(integer)";
        
string line6    =  "Valeur de l'intéger" ;        
        
string line7    "Que dire des décimaux si on utilise "
        
string line8    "la fonction Sf(float)";
        
string line9    =  "Valeur de du décimal";    
        
string line10   "Que dire des listes si on utilise "
        
string line11   "la fonction Sl(list)";
        
string line12   =  "Liste" ;        
        
        
        
string output header line1,line2line3);
        
output +=   "Prenons un exemple:" NL2 "Affichons la valeur PI:" NL2T2 "PI" ARR Sf(PI) + NL2;
        
output +=  demo1(line4,line5,line6);
        
output +=  demo2 (line7,line8,line9) + Sf(f) + NL2;
        
output +=  demo2 (line10,line11,line12) + Sl(l) + NL2;
        
output +=  footer"Fin de demo" ); 
        
    
        
Screen (output);
    }

On aurait pu mettre les lignes de texte dans une liste pour économiser les variables.

style:

Code PHP:

list texte = [ "Ceci est une texte formaté qui présente"
                     
"la particularité d'offrir une vision plus propre",
                     ....
                     ];

        
string output header llList2String(texte,0), llList2String(texte,1), llList2String(texte,2) ); 




et le résultat:

Citation :
[12:05] Object:
___________________________________

Ceci est une texte formaté qui présente
la particularité d'offrir une vision plus propre
voire plus profesionnelle.
___________________________________

Prenons un exemple:

Affichons la valeur PI:

PI => 3.141593

Les integers sont faciles à présentés si on utilise
la fonction Si(integer)

Valeur de l'intéger => 312457

Que dire des décimaux si on utilise
la fonction Sf(float)

Valeur de du décimal => 25.052570

Que dire des listes si on utilise
la fonction Sl(list)

Liste => Integer - 312457 - Float - 25.052572

___________________________________

Fin de demo
___________________________________

Optimiser son code


Par Bestmomo



Ce n’est pas une astuce mais plutôt un exemple d’optimisation de code.

Voici un code qui m’a été soumis par une amie pour avoir mon avis. Il s’agit de la commande d’une baie qui doit passer en fantôme pour être franchie en utilisant un clic de la souris :

Code PHP:

default
{
    
state_entry()
    {
        
llSay(0"Baie en action");    // information
        
state ferme;    // passe à l'état initial fermé
    
}
}

state ouvert    // état porte ouverte
{
    
state_entry()
    {
        
llSetTimerEvent(10.0);
        
llSay(PUBLIC_CHANNEL"Vous avez 10 secondes pour traverser la baie");
    }

    
timer()
    {
        
llSetTimerEvent(0.0);
        
integer c;
        for (
1<= llGetNumberOfPrims(); c++)
            
llSetLinkPrimitiveParams(c, [PRIM_PHANTOMFALSE]);
        
llSay(PUBLIC_CHANNEL"Baie fermee");
        
state ferme;
    }
}

state ferme        // état porte fermée
{
    
touch_start(integer total_number)
    {
        if (!
llSameGroup(llDetectedKey(0))){
            
llSay(PUBLIC_CHANNEL"Vous n'etes pas autorise a agir");
        }
        else{
            
integer c;
            for (
1<= llGetNumberOfPrims(); c++)
                
llSetLinkPrimitiveParams(c, [PRIM_PHANTOMTRUE]);
            
state ouvert;    // passage à l'état ouvert
        
}
    }

Je pense qu’il s’agit d’un bon exemple d’accumulation d’erreurs et maladresses :

Mauvaise utilisation d’un paramètre d’une fonction LSL :
La fonction llSetLinkPrimitiveParams réclame comme premier paramètre le numéro de la prim sur laquelle doit s’effectuer l’action. Mais il est possible d’utiliser la constante LINK_SET pour agir sur toutes les prims liées ce qui évite l’utilisation d’une boucle.

Mauvaise connaissance d’une propriété des objets :
La propriété phantom concerne un objet complet et l’action sur une seule prim suffit à modifier cet objet. D’autre part il suffit d’utiliser la fonction llSetStatus qui est plus simple et n’entraine pas le délai de 0,2 secondes imposé par llSetLinkPrimitiveParams.

Répétition de code :
Il faut éviter de répéter du code au sein d’un script parce que ça l’alourdit et ça rend difficile sa maintenance. Il est aussi toujours judicieux de factoriser son code au sein de fonctions que l’on peut généraliser, de cette façon les évolutions ultérieures du code sont favorisées.

Utilisation inadaptée d’un timer :
Il est souvent fait usage d’un timer alors qu’un simple llSleep serait suffisant.

Utilisation inadaptée d’un état :
L’utilisation d’états dans les scripts LSL me paraît logique et correspondre à ce langage mais cette utilisation doit être justifiée. Ici on n’a pas vraiment deux états puisque l’un d’entre eux (baie ouverte) est instable. L’utilisation d’un seul état dans ce cas simplifie le code.

Utilisation de valeurs numériques littérales dans le code :
La valeur de la temporisation est insérée dans ce code à l’emplacement où on en a besoin. Si on désire changer cette valeur il faut retrouver cet emplacement, il est plus judicieux de prévoir une constante en tête du script. Même si pour un script aussi simple ce n’est pas vraiment évident ça le devient vite lorsque le code s’allonge.

Mauvaise initialisation :
Au démarrage le script va dans l’état de fermeture sans prendre la précaution de rendre la baie non fantôme.

Voici une version corrigée qui tient compte de toutes ces réflexions et qui rejoint un peu l’exemple précédent de Seb sur les fonctions et constantes :

Code PHP:

// Constantes
integer   GROUPE     0;
integer   OWNER      1;
integer     ALL         2;
integer   OUVERTURE    1;
integer     FERMETURE    0;
float           DELAI        10.0;

// Affichage info dans le Chat
info(string s) {llSay(0s);}

// Commande de la baie
commande(integer etatstring s) {
    
llSetStatus(STATUS_PHANTOMetat);
    
info(s);}

// Test d'utilisateur
integer test(key idinteger mode) {
    if(
mode == GROUPE)
        return 
llSameGroup(id);
    else if(
mode == OWNER)
        return 
llGetOwner() == id;
    else
        return 
TRUE;}

// Etat par défaut
default
{
    
state_entry()
    {
        
commande(FERMETURE"Baie en action.\n Baie fermée.");
    }

    
touch_start(integer total_number)
    {
        if (
test(llDetectedKey(0), GROUPE))
        {
            
commande(OUVERTURE"Vous avez " + (string)DELAI " secondes pour traverser la baie");
            
llSleep(DELAI);
            
commande(FERMETURE"Baie fermée");
        }
        else
            
info("Vous n'êtes pas autorisé à ouvrir la baie, désolé.");
    }

Le code est devenu très lisible.


Sécuriser des données


Par Bestmomo



Quoi qu’on fasse il peut toujours arriver un crash de serveur avec perte des valeurs de variables dans les scripts. Les éléments persistants d’un prim ne sont pas nombreux. Parmi eux il y a les champs « nom » et « description » de dimensions relatives 63 et 127 caractères. Un objet est en général constitué de plusieurs prims et, si les deux champs en questions sont utilisés au niveau de la prim racine il n’en est pas de même au niveau des enfants. Du coup ils peuvent servir de dépôt de valeurs. Les problèmes :

• ils sont disséminés et ne permettent pas une sauvegarde de grandes valeurs
• ils ne sont accessibles en écriture qu’à partir d’un script situé dans la prim concernée.

A bien y regarder il est quand même intéressant de s’engager sur cette piste même si je sais qu’elle sera source de polémique à cause de la (relative) multiplication des scripts et du lag généré. Mais c’est une astuce alors je la livre

Pour tester ça constituez un objet de 4 prims et mettez ce script dans les 3 prims enfants :

Code PHP:

// Satellite

// Constantes
integer SAVE_NAME     1111;
integer SAVE_DESC     1113;

// Etat par défaut
default
{
    
link_message(integer sender_numberinteger numberstring messagekey id)
    {
        if(
number == SAVE_NAME)
            
llSetObjectName(message);
        else if(
number == SAVE_DESC)
            
llSetObjectDesc(message);
    }

Ce script est destiné à l’écriture des valeurs dans les champs « nom » et « description ».

Mettez ce script dans la prim racine :

Code PHP:

// Stockage
// Chaque prim enfant peut conserver 190 caractères (63 + 127)
// Présence d'au moins 2 prims enfants
// Valeur sauvée limitée à 9999 caractères

// Constantes
integer SAVE_NAME     1111;
integer SAVE_DESC     1113;

// Variables
integer nom;
integer desc;

// Détermination longueur string à traiter
integer length(string sinteger mod)
{
    
integer l llStringLength(s);
    if(
mod)
        return 
mod;
    else
        return 
l;
}

// Formatage nombre en string
string formatNumber(integer ninteger l)
{
    
string s = (string)n;
    while(
llStringLength(s) < l)
        
"0" s;
    return 
s;
}

//  Enregistrement de la valeur
integer save(string s)
{
    
integer links llGetNumberOfPrims() - 1;
    
integer n llStringLength(s) + 4;
    if(
> (127 255) * links)
        return 
FALSE;
    
formatNumber(n4) + s;
    
nom 2;
    
desc 2;
    
integer lg;
    
links += 2;
    while(
llStringLength(s))
    {
        if(
nom links)
        {
            
lg length(s63) - 1;
            
llMessageLinked(nomSAVE_NAMEllGetSubString(s0lg), NULL_KEY);
            
nom++;
        }
        else
        {
            
lg length(s127) - 1;
            
llMessageLinked(descSAVE_DESCllGetSubString(s0lg), NULL_KEY);
            
desc++;
        }
        if(
lg llStringLength(s))
            
llGetSubString(slg 1, -1);
        else
            
"";
    }
    return 
TRUE;
}

//  Récupération de la valeur
string restore()
{
    list 
llGetObjectDetails(llGetLinkKey(2), [OBJECT_NAME]);
    
integer n = (integer)llGetSubString(llList2String(l0), 03);
    if(
64)
        return 
llGetSubString(llList2String(l0), 43);
    
string st llGetSubString(llList2String(l0), 4, -1);
    
integer links llGetNumberOfPrims() + 1;
    
nom 3;
    
desc 2;
    while(
llStringLength(st) < 4)
    {
        if(
nom links)
        {
            
llGetObjectDetails(llGetLinkKey(nom), [OBJECT_NAME]);
            
nom++;
        }
        else
        {
            
llGetObjectDetails(llGetLinkKey(desc), [OBJECT_DESC]);
            
desc++;
        }
        
st += llList2String(l0);
    }
    return 
st;
}

// Etat par défaut
default
{
    
touch_start(integer total_number)
    {
        
string s "0123454678901234546789012345467890123454678901234546789";
        
string t;
        
integer c;
        for(
010c++)
            
+= s;
        if(!
save(t))                            // Enregistrement de la valeur
            
llOwnerSay("Valeur trop grande !");
        
llOwnerSay(restore());                    // Restauration de la valeur
    
}

C’est le script principal. Il montre comment stocker une valeur de type « string » dans les champs « nom » et « description » des prims enfants comme s’il s’agissait d’un seul champ global. Rien n’empêche de placer des signes séparateurs au niveau de la valeur pour la découper en plusieurs zones de stockage faciles à identifier.


Menu à suivre : BACK ... NEXT


Elenia Boucher



C'est la 1ére fonction que j'ai mise au point quand je me suis mise à scripter : présenter une liste longue par block de choix de 9 avec les touches NEXT et BACK pour naviguer dedans; cette version ne fonctionne qu'avec le owner : elle est faite pour les HUD. Modification facile pour utilisation tout le monde ou groupe. Je vous laisse un peu de travail quand même.


Code PHP:

list glChoix = ["un","deux","trois","quatre","cinq","six","sept","huit","neuf","dix","onze","douze"];
//attention à la longueur de chaque choix : ne pas dépasser 24 caractéres dont seulement environ la moitié paraitra dans le bouton
integer giNb ;
integer giIndexMenu;
integer giChannel;
integer giShiftChan = -786492;
integer giHandle;
key gkOwner;


Init(){
        
giNb llGetListLength(glChoix);
        
gkOwner llGetOwner();
        
giChannel = -(integer)("0x"+llGetSubString(gkOwner,-7,-1)) + giShiftChan;
        
//notez le calcul du canal sur la clé owner modifiée par un nombre arbitraire; on peut aussi utiliser la clé objet.
}


BuildMenu() {
    list 
menu;
    
    
giHandle  llListengiChannel""gkOwner"" );
    
    
string affich "Faites votre choix\ntime-out 30 secondes :";
    
    
llSetTimerEvent(30.0);

    if(
giIndexMenu 0)
        
menu += ["BACK"];
    else
        
menu += [" "];

    
menu += [" "];//touche centrale : vous pouvez y mettre MAIN si cela n'est pas le menu principal

    
if(giIndexMenu*8+giNb 1)
        
menu += ["NEXT"];
    else
        
menu += [" "];

    
menu += llList2List(glChoixgiIndexMenu*9giIndexMenu*9+8);
 
    
llDialog(gkOwneraffichmenugiChannel);
}


default{
        
state_entry(){
            
Init();
        }
        
        
changed(integer chg){ // si on change d'owner il faut recalculer le channel
            
if (chg CHANGED_OWNERllResetScript();
        }
        
        
touch_start(integer total_number){
        
giIndexMenu=0;BuildMenu();
        }
        
        
timer(){
            
llSetTimerEvent(0);
            
//llOwnerSay("time out");
            
llListenRemove(giHandle);
        }
        
        
listen(integer chanstring namekey idstring mess){
            
llSetTimerEvent(0);
            
llListenRemove(giHandle);

        if (
mess == "NEXT"){giIndexMenu++; BuildMenu();}
        else if (
mess == "BACK"){giIndexMenu--; BuildMenu();}
        
//else if (mess == "MAIN"){} //retour éventuel au menu principal 
        
else { // ici le traitement du choix
            
llOwnerSay("Vous avez choisi : " mess);
            
//éventuellement je traite comme ça
            
integer s llListFindList(glChoix,[mess]);
            
//en fonction de la variable s extraction d'une valeur dans une liste associée
            // action = llList2String(glActions, s); // ou key ou vector ou rotation ou integer ou float
        
}
            
    }

Une application : le testeur et giver d'animation : vous mettez ce script avec des animation dans un cube, n'importe qui clicquant dessus aura un menu de choix lui permettant de choisir et jouer une animation et de l'obtenir.
J'ai utilisé ici une variante : les choix sont proposé avec des numéros et une liste en regard : pratique si vos choix ne tiennent pas en 24 caractéres, et indispensable si vous ne maitrisez pas la longueur et qu'elle est donc succptible de dépasser les fatidiques 24 caractéres qui autrement provoqueraient une erreur de script.

Code PHP:

list glChoix;
integer giNb ;
string gsAnimCur;
integer giIndexMenu;
integer giChannel 786492;
integer giHandle;
key gkAvi;
string gsAvi;
integer gbPermAck;

ChargeAnim(){
        
integer i;
        
glChoix=[];
        
giNb llGetInventoryNumber(INVENTORY_ANIMATION);
            for ( 
0giNbi++) {
            
string item llGetInventoryName(INVENTORY_ANIMATIONi);
            
glChoix += [item];
            }
}


BuildMenu() {
    list 
menu;
    
integer i;
    
menu = [];
    
string affich "Choose a animation or GIVE/STOP :\nCurrent animation  : " gsAnimCur;
    
string num;
    
    
llSetTimerEvent(60.0);

    
giHandle  llListengiChannel""gkAvi"" );
    

    if(
giIndexMenu 0)
        
menu += ["BACK"];
    else
        
menu += [" "];

    
menu += ["GIVE/STOP"];

    if(
giIndexMenu*8+giNb 1)
        
menu += ["NEXT"];
    else
        
menu += [" "];

    for(
i=giIndexMenu*9; (<= giIndexMenu*9+8) && (<= giNb 1);i++) {
        
num = (string)(i+1);
        
affich += "\n"+num+":"+llList2String(glChoix,i);
        
menu += [num];
    }

    
llDialog(gkAviaffichmenugiChannel);
}

Popup(key idstring message){   //petite fonction très pratique; ne pas utiliser le canal pour autre chose
    
llDialog(idmessage, [], 298479);
}


default{
        
state_entry(){
            
ChargeAnim();
        }
        
        
changed(integer change){
            if (
change CHANGED_INVENTORYChargeAnim();
        }    
        
        
touch_start(integer tot){//vous remarquerez le traitement de plusieurs avatars qui clicque dessus en même temps
            
integer i;
            
key kavi;
            
string savi;
            for (
i=0;i<tot;i++){
                
kavi llDetectedKey(i);
                
savi llDetectedName(i);
                if (!
gbPermAck && !giHandle && == 0){
                    
gkAvi kavi;
                    
gsAvi savi;
                    
gbPermAck TRUE;
                    
llRequestPermissions(gkAviPERMISSION_TRIGGER_ANIMATION);
                }else 
                    
Popup(kavi,"Sorry " savi " but " gsAvi " uses the menu");
            }
        }
        
        
run_time_permissions(integer perm) {
            
gbPermAck FALSE;
            if(
perm PERMISSION_TRIGGER_ANIMATION){
                
gsAnimCur ""giIndexMenu=0BuildMenu();
            }
        }
        
        
timer(){
            if (
gsAnimCur !=""llStopAnimation(gsAnimCur);
            
llRequestPermissions(gkAviFALSE);
            
llSetTimerEvent(0);
            
llListenRemove(giHandle);
            
giHandle FALSE;
        }
        
        
listen(integer chanstring namekey idstring mess){
            
llSetTimerEvent(0);
            
llListenRemove(giHandle);
        
integer numanim = (integer)mess 1;
        if (
numanim >= ){
            if (
gsAnimCur !=""llStopAnimation(gsAnimCur);
            
gsAnimCur llList2String(glChoixnumanim);
            
llStartAnimation(gsAnimCur);
            
BuildMenu();
        }
        else if (
mess == "NEXT"){giIndexMenu++; BuildMenu();}
        else if (
mess == "BACK"){giIndexMenu--; BuildMenu();}
        else if (
mess == "GIVE/STOP"){
            
giHandle FALSE;
            if (
gsAnimCur !="") {
                   
llStopAnimation(gsAnimCur);
                   
llGiveInventory(gkAvigsAnimCur);
             }
             
llRequestPermissions(gkAviFALSE);
        }
            
    }




Exploitez les clics!!


By Seb

99,99999 % des scripts n'utilise que touch_start comme event user ( action utilisateur) et pourtant...

Vous avez beaucoup plus d'events à votre dispo!!

Exemple d'actions et de reset (différé ou immédiat...)

Code PHP:

integer Clics;
default
{
    
state_entry()
    {
        
llOwnerSay"Bonjour click man!");
    }        
    
touch_start(integer total_number)
    {
        
llOwnerSay "Action comme dab  ICI ..." ); 
        
llResetTime();
    }    
    
touch_end(integer total_number)
    {
        
Clics 0;
        
float stop_clic llGetTime();
        if (
stop_clic 1.0)
        {
            
llOwnerSay"Reset si le temp > 1s >>apres<< le relachement. ==>" + (string) stop_clic);             
            
llResetScript();  
        }
        
llOwnerSay "OU  Action possible ICI ..." ); 
    }
    
touch(integer total_number)
    {
        ++
Clics;
        if ( 
Clics 50  )
        {
            
llOwnerSay"Reset IMMEDIAT si  clics > 50  ==>" + (string) Clics);             
            
llResetScript(); 
        }
        
llOwnerSay "OU encore  ICI ...mais prudence .. prudence..." ); 
    }
 } 
Dernières modifications :
(Voir) 19/9/2009 18:19:52 : Ahuri Serenity (petite correction)
(Voir) (Comparer)10/9/2009 17:05:44 : Seb_01 (Ajout astuce)
(Voir) (Comparer)10/9/2009 01:20:36 : Elenia Boucher (suppression 1 variable inutile)
Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés