Traitement d'un Fichier de configuration...

Répondre
Partager Rechercher
Tout scripteur a, aura, a eu besoin de lire une notecard servant de fichier de configuration pour un projet.

En lisant çà et là les scripts à ce sujet j'ai été surpris de la façon dont a été traité ce sujet ...et le mot est faible.

Je propose donc un p'tit sujet dessus...


Un fichier de configuration c'est quoi?:
Généralement un fichier texte ou binaire contenant les données / paramètres nécessaire à un programme. Ils sont souvent en lecture/écriture afin de lire ou écrire (stocker) les informations.
Pour SL ils sont de type Notecard ... et ne peuvent être malheureusement QUE être lus pour notre plus grand malheur!

Existe il une norme, une règle ?
Non, chacun à sa sauce...mais il est communément admis que:

un signe # est un commentaire
une ligne blanche est ignorée

c'est peu certes mais ... mieux que rien


Exemples:

Code:
1
rouge
50
Citation :
#entrer vos valeurs
# 1 = Vrai 0=Faux
# couleur
# hauteur

1
rouge
50
Comme le fichier de configuation est reservé à l'utilisateur final on peut imaginer des erreurs deux frape ou autres

Citation :
#entrer vos valeurs
# 1 = Vrai 0=Faux
# couleur
# hauteur

vrai
rouge
50,00
On peut donc imaginer la suite et la du script...

Pour lire une notecard il existe principalement 4 instructions LSLet quelques dizaines et plus d'exemples plus ou moins bien faits et plus ou moins complexes


2 exemples peuvent être donnés ici:

http://wiki.secondlife.com/wiki/LlGetNotecardLine

Pour un débutant la lecture du second exemple sera

le premier sera d'une lecture...plus simple et rapidement copié...


Je fais une parenthèse sur Dataserver car elle rebute certains, contrarie les autres, et tourne en bourrique la plupart d'entre nous quand nous avons à l'utiliser en rafale avec plusieurs requêtes différentes...

Cette requète à plusieurs warnings indiqués par SL

Les réponses de type Dataserver ne reviennent pas nécessairement dans l'ordre des appeks.
  • si plusieurs appels sont en attentes, utilisez toujours des clés différentes pour déterminer la réponse reçue.
Les réponses de type Dataserver lanceront l'évènement dataserver dans tous les script de la prim ayant fait la demande.
  • Si plusieurs script d'une même prim utilise les évènements dataserver, utilisez toujours une clé pour déterminer la réponse reçue.
  • L'évènement dataserver ne sera pas lancé dans les scripts des prims filles d'un même objet.
  • Cette fonction fait dormir le script pendant 0.1 secondes ou 1 seconde
Bref en lisant cela on comprend mieux les difficultés du scripteur.

Temps de réponses qui "gèlent" le script pendant le requêtes au serveur et je passe sous silence le traitement des erreurs de toutes sortes...et non gérées dans 99,99% dans les scripts utilisant cette commandes. Si si vérifiez!

Si je rajoute dans le cas qui nous préocupe (la lecture d'une notcard) les warnings donnés pour LlGetNotecardLine

Cette fonction fait dormir le script pendant 0.1 secondes.
Si nom n'est pas dans l'inventaire de la prim et n'est pas un UUID ou n'est pas un note alors une erreur se produit sur le canal DEBUG_CHANNEL.
Si nom est une UUID il n'y a alors aucune conséquence sur les droits de l'objet.
  • L'objet final ne subit pas de nouvelle restriction d'utilisation qui aurait pu être appliquée si l'objet pointé par l'UUID avait été présent dans l'inventaire de la prim.
Si une note contient un objet d’inventaire (telle une texture ou un landmark), le serveur de données renverra la valeur EOF indépendament de la ligne demandée.
Si la ligne demandée fait plus de 255 bytes, le serveur de données ne renverra que les 255 premiers bytes de la ligne.
Si la note est vide, le serveur de données générera une erreur sur le DEBUG_CHANNEL selon laquelle la note n’existe pas car tant que note n’est pas sauvegardée, elle ne consiste qu’en un emplacement d’inventaire (llGetInventoryKey renverra NULL_KEY).


vous comprenez mieux coder la lecture d'une une notecard n'est pas le simple dans LSL

Pour ceux qui ont bien lu les restrictions...??? le delai de 0.1 semble peu important.. semble être oui mais dans la réalité que se passe t il?

le temps TOTAL = 0.1 x le nombre de lignes!!

Un fichier standard/ commun de 35 lignes prendra 3.5 secondes non pas de traitement mais d'attente!

Vous comprenez mieux pourquoi ce sujet est souvent passé sous silence et que

le copier /coller fonctionne à merveille?


Courage! on va essayer de rendre les choses plus simples et si possible plus intéressantes et/ou instructives ..

A suivre...
Moi depuis novembre 2007 j'utilise cette base dénichée sur un site (sais plus où); il répond à tes critéres:
Code:
string notecard_name = "configuration";  // name of notecard goes here

// internals
integer DEBUG = FALSE;
integer line;
key queryhandle;                   // to separate Dataserver requests
key notecarduuid;

init()
{
    queryhandle = llGetNotecardLine(notecard_name, line = 0);// request line
    notecarduuid = llGetInventoryKey(notecard_name);
}

// Config data loaded from notecard, with some sane defaults
integer channel = 1000;
string email_address = "revolution.perenti@skidzpartz.com";
default
{
    changed(integer change)         
    {
        // We want to reload channel notecard if it changed
        if (change & CHANGED_INVENTORY)
            if(notecarduuid != llGetInventoryKey(notecard_name))
                init();
    }

    state_entry()
    {
        init();
    }
    
    dataserver(key query_id, string data)
    {
        if (query_id == queryhandle)
        {
            if (data != EOF)
            {   // not at the end of the notecard
                // yay!  Parsing time
                
                // pesky whitespace
                data = llStringTrim(data, STRING_TRIM_HEAD);

                // is it a comment?
                if (llGetSubString (data, 0, 0) != "#")
                {
                    integer s = llSubStringIndex(data, "=");
                    if(~s)//does it have an "=" in it?
                    {
                        string token = llToLower(llStringTrim(llDeleteSubString(data, s, -1), STRING_TRIM));
                        data = llStringTrim(llDeleteSubString(data, 0, s), STRING_TRIM);

                        //Insert your token parsers here.
                        if (token == "email_address")
                            email_address = data;
                        else if (token == "channel")
                            channel = (integer)data;
                    }
                }

                queryhandle = llGetNotecardLine(notecard_name, ++line);
                if(DEBUG) llOwnerSay("Notecard Data: " + data);
            }
            else
            {
                if(DEBUG) llOwnerSay("Done Reading Notecard");
                state configuration ;
            }
        }
    }
}

state configuration
{
 
    state_entry()
    {
        llListen(channel, "", "", "");
        llShout(0, "Channel set to " + (string)channel);
        llShout(0, "Email set to " + (string)email_address);
    }   
}
Code:
#exemple de notecard 'configuration'

#mettre votre email ici
email_address=elenia.boucher@Yahoo.fr

#vous pouvez changer le canal ici
channel = 69

#note: ce script gére les espaces avant et après le signe = , ce qui aére la présentation
Bon je l'ai mis à toute les sauces. Ma pratique actuelle est plutot de faire un script séparé pour ça qui vérifie, formate et communique les valeurs au script principal avec des llMessageLinked.
Merci de votre participation et avis...

Finalement le script donné par le wiki ... et en plus le plus optimisé! va me servir d'exemple pour démontrer quelques problèmes...

J'ai testé ce script dans plusieurs possibles et malencontreuses situations .

Fichier absent
Fichier présent mais pas du bon type
Fichier vide
Absence de "="
Doublement de lignes
etc etc...

Bref aucune erreur n'a été detectée...les valeurs sont prises par defaut presque à chaque fois.

Pauvre elenia ... va en recevoir des emails elle

Plus sérieusement, ce script est bien incomplet et de plus la gestion des paramètres est "inside" ce qui veut dire que sous le même nom de script va se cacher autant de scripts différents que de fichier de configuations...

Je m'arrête ici dans les remarques celles citées doivent nous inviter à faire mieux... nous les petits frenchies.


Les infos suivantes sont dans un but didactique rien de plus, elles ne sont pas forcement complètes ou optimisées etc... mais devraient servir de base solide
à ce que pourrait être une bonne gestion de fichier de configuration.

Si on pose quelques objectifs histoire de...

Gérer les erreurs possibles et quelques inattendues.
Séparer les fonctions et le travail de chaque entitées.
Ne pas être pénalisé par le delai des Dataservers
Bien séparées le type d'erreurs
Etre réutilisable
Convivial
Maintenance ou évolutions aisées

J'ai du en oublier...à n'en pas fouter

Le mieux comme toujours est de montrer la finalité, pour donner un exemple clair j'ai pensé que le fichier de configuration pouvait contenir certains paramètres de la fonction llSetPrimitiveParams comme cela l'effet sera ... visuel sur la prim elle même ( celles qui contient ce projet).


Config1.png


Tout d'abord le fichier "Fichier de configuration"

Code:
#  Voila votre  fichier de configuration
#   Vous devez parametrer chaque ligne avec une attention particulière
#   Les paramètre sont les suivants

# PRIM_TYPE                     0=box / 1= cylinder   
# PRIM_SIZE _xyz           Sets the prim's size.     
# PRIM_TEXTURE              Sets the prim's texture attributes.     
# PRIM_COLOR                 Sets the face's color.     
# PRIM_FULLBRIGHT       Sets the face's full bright flag.     1= TRUE / 0=FALSE 
# PRIM_SIDE                     What Face?  1 to 6 
#  Il vous permettront de modifier l'apparence un objet

#  Parametres actuels:


 PRIM_TYPE                 = 0
 PRIM_SIZE _x,y,z     =  <0.5,0.75,0.5>     
 PRIM_FULLBRIGHT    =   1  
 PRIM_COLOR              =  < 1.0,1.0,1.0 >     
PRIM_TEXTURE            =  93d9d03a-ce57-3921-3e01-7de6ba14f1ec
PRIM_SIDE_TEXTURE  = 1
Je suis d'accord qu'il n'y a pas de traitement d'erreur. Néanmoins je trouve très simple et élégant comme ça : une valeur erronée, que ce soit par manque du = ou par faux token est tout simplement ignorée et la valeur par défaut est appliquée.

Bien sur, on peut vérifier la présence du = et la validité du token. Mais bon est-ce vraiment utile avec le mécanisme des valeurs par défaut? Si les valeurs par défaut ne peuvent pas être utilisées certainement.

Plus intéressant est la vérification de la validité de la valeur fournie : format, intervalle, ... Mais on rentre dans des cas très particuliers qui doivent être codé à la demande.

Car il est certain que chaque script est adapté à un cas particulier. Car on ne sait pas faire autrement puisque le jeu est d'affecter des valeurs à des variables qui elles sont obligatoirement codées en dur dans le script.
Chacun son avis Elenia sur le sujet...

Je pense que ne pas traiter les erreurs et SURTOUT ne pas en informer l'utilisateur final conduit inévitablement à une situation pénalisante, bloquante voire dangereuse. Surtout que le pauvre utilisateur final n'a pas accès au script, souvent il ne peut pas faire un reset, il a aucune idée des conséquences etc...
Si un objet est distribué à grande échelle et que le paramétrage final (customisation) n'a pas de garde fou ... tu peux être certain que le créateur va crouler sous les IMs.
De plus pour en finir si la config contient 20 paramètres et que le premier est foireux c'est 20 paramètres qui sont faux. Si il s'agit de position, couleur, textures, rotations l'objet va ressembler à quelques choses d'étrange...ou le/les scripts vont planter royalement.
Je passe sur la valeur des canaux d'écoutes qui vont se trouver à écouter rien du tout..
Je suis d'accord avec toi que les valeurs intrinsèques pourrait être testées pour les borner, du moins celles qui pourrait être sensibles ( style ajustement de tiny prims...) mais la on rentre dans l'applicatif lui même ...qui ne fait pas partie des sujets que je voulais traiter dans ce post.

Seb,
Mise en situation lecture des données
Dans un premier temps testons le projet mode no error

Resultat1
Code:
[7:36]  Object: Nombre de lignes traitées =21
[7:36]  Object: Nombre de lignes de commentaires ou vides  = 15
[7:36]  Object: Nombre d'erreurs détectées =0
[7:36]  Object: Nombre de paramètres détectées 6
[7:36]  Object: 

 ***** Parametres ****** 

[7:36]  Object: Parametre 1: PRIM_TYPE => 0
[7:36]  Object: Parametre 2: PRIM_SIZE _x,y,z => <0.5,0.75,0.5>
[7:36]  Object: Parametre 3: PRIM_FULLBRIGHT => 1
[7:36]  Object: Parametre 4: PRIM_COLOR => < 1.0,1.0,1.0 >
[7:36]  Object: Parametre 5: PRIM_TEXTURE => 93d9d03a-ce57-3921-3e01-7de6ba14f1ec
[7:36]  Object: Parametre 6: PRIM_SIDE_TEXTURE => 1
[7:36]  Object:  
***********************
Mode Errors


Maintenant supprimons le fichier:

Code:
[7:39]  Object: >> Fichier de configuration <<...est absent!
[7:39]  Object: Erreur concernant le fichier >> Fichier de configuration <<
Mettons un fichier de mauvais type
Code:
[7:40]  Object: >> Fichier de configuration <<...n'est pas un type de fichier Notecard!
[7:40]  Object: Erreur concernant le fichier >> Fichier de configuration <<
Un fichier vide:

Code:
[7:43]  Object: >> Fichier de configuration <<...est vide!
[7:43]  Object: Erreur concernant le fichier >> Fichier de configuration <<

Problème Dataserver


Code:
[7:49]  Object: Lecture >> Fichier de configuration <<...TIMEOUT
[7:49]  Object: Erreur concernant le fichier >> Fichier de configuration <<
Voilà pour les principales erreurs "système"



Maintenant erreur de paramétrages

Code:
#  Voila votre  fichier de configuration
#   Vous devez parametrer chaque ligne avec une attention particulière
#   Les paramètre sont les suivants

# PRIM_TYPE                     0=box / 1= cylinder   
# PRIM_SIZE _xyz           Sets the prim's size.     
# PRIM_TEXTURE              Sets the prim's texture attributes.     
# PRIM_COLOR                 Sets the face's color.     
# PRIM_FULLBRIGHT       Sets the face's full bright flag.     1= TRUE / 0=FALSE 
# PRIM_SIDE                     What Face?  1 to 6 
#  Il vous permettront de modifier l'apparence un objet
#  Parametres actuels:


 
 PRIM_TYPE                  0
 PRIM_SIZE _x,y,z     =  <0.5,0.75,0.5>     
 PRIM_FULLBRIGHT    =     
 PRIM_COLOR              =  < 1.0,1.0,1.0 >     
                                     =  < 1.0,1.0,1.0 >     
 PRIM_TEXTURE            =  93d9d03a-ce57-3921-3e01-7de6ba14f1ec
PRIM_SIDE_TEXTURE  = 1
Code:
[9:08]  Object: Nombre de lignes traitées =22
[9:08]  Object: Nombre de lignes de commentaires ou vides  = 15
[9:08]  Object: Nombre d'erreurs détectées =3
[9:08]  Object: Nombre de paramètres detectées 4
[9:08]  Object: Nombre de paramètres incorrect! (attendus = 6)
[9:08]  Object: 

 ******* Erreurs ******** 
Line 14    > PRIM_TYPE                  0<
Line 16    > PRIM_FULLBRIGHT    =    <
Line 18    >                                     =  < 1.0,1.0,1.0 >     <
 **********************
[9:08]  Object: Corrigez vos erreurs..
Le projet de façon simple est capable de détecter certaines erreurs et de les présenter à l'utilisateur.
En cas d'erreur les données ne sont pas envoyées à l'applicatif...
Le projet
Une photo vaut mieux qu'un grand discours


config2.png


Il y a 3 scripts...1 est l'applicatif lui même.

Donc 2 scripts pour la gestion du fichier.

Les 2 scripts peuvent être fusionner mais on perdrait en lecture, en répartition des fonctions et aussi compréhension ce qui n'est pas le but ici....et on doit aussi respecter les objectifs fixés.

Analyse de l'applicatif(1/3):

Script Set_Primitive_Params

Code PHP:

float   Timer_End;
float   Timer_Start;

// # PRIM_TYPE                     0=box / 1= cylinder   
// # PRIM_SIZE _xyz           Sets the prim's size.     
// # PRIM_TEXTURE              Sets the prim's texture attributes.     
// # PRIM_COLOR                 Sets the face's color.     
// # PRIM_FULLBRIGHT       Sets the face's full bright flag.     1= TRUE / 0=FALSE 
// # PRIM_SIDE                     What Face?  1 to 6 
default
{
    
    
touch_start(integer total_number)
    {
        
llMessageLinked(LINK_THIS99,"6","");
        
llResetTime();  // inutile :)
        
Timer_Start llGetTime();  // inutile :)
    
}

    
link_message(integer sender_numinteger numstring msgkey id
    {
        if ( 
num == 60 )
        {
            list 
list_values llParseString2List(msg, ["%"], ["/"]);    // Parse the string back to a list
            
            
integer type    llList2Integer(list_values,0);
            
vector  size    = (vectorllList2String(list_values,1);
            
integer switch  = llList2Integer(list_values,2);
            
vector  color   = (vectorllList2String(list_values,3);
            
key     texture llList2Key(list_values,4);
            
integer side    llList2Integer(list_values,5);
            
            
Timer_End llGetTime(); // inutile :)
            
            
llSetPrimitiveParams([  PRIM_TYPE
                                        
type
                                        
PRIM_HOLE_DEFAULT,  
                                        <
0.001.00.0>,   // cut
                                        
0.0,                // hollow
                                        
<0.00.00.0>,    // twist
                                        
<1.01.00.0>,    // top_size
                                        
<0.00.00.0>,     // top_Shear                                        
                                    
PRIM_SIZE,
                                        
size
                                    
PRIM_FULLBRIGHT
                                        
ALL_SIDES
                                        switch, 
                                    
PRIM_COLOR
                                        
ALL_SIDES
                                        
color
                                        
TRUE,                                     
                                    
PRIM_TEXTURE
                                        
side
                                        
texture
                                        <
1.0,1.0,0.0>,
                                        <
0.0,0.0,0.0>, 
                                        
FALSE 

                                
]);
            
            
llOwnerSayllDumpList2String list_values,"\n") );
            
            
llOwnerSay "Durée de lecture: " + (string) ( Timer_End -Timer_Start) + " s");  // inutile :)
            // http://wiki.secondlife.com/wiki/Category:LSL_Vector   
        
}
    }

Résultats après appui sur la touche:

[9:31] Object: 0
<0.5,0.75,0.5>
1
< 1.0,1.0,1.0 >
93d9d03a-ce57-3921-3e01-7de6ba14f1ec
1
[9:31] Object: Durée de lecture: 0.200309 s


Le script est basique....mais 5 choses sont à noter quand même:

1. Le temps de réponse est très rapide 0.2 s au lieu des 3 secondes prévisible

2. L'ordre de lecture des paramètres se fait par

Code PHP:

 llMessageLinked(LINK_THIS99,"6",""); 

6 représentant le nombre de paramètres attends par l'applicatif

3. list_values est une liste contenant les paramètres dans l'ordre d'écritures de la carte.

4. Si on désire un autre paramètre par exemple PRIM_PHANTOM

trois actions simples à faire :

1. porter à 7 le nombre de paramètres
2. enregistrer ce paramètre dans le fichier!

...et modifier l'applicatif en tant que tel pour gérer ce nouveau paramètre.


5. C'est ici que l'on pourra tester les valeurs des paramètres

un vecteur reste un vecteur ... mais sa valeur va varier suivant son utilisation

<0.5,0.5,0.5> pour une couleur et <125.235,125.36,1002.258> pour une position...


Bref la lecture d'un fichier de configuration en est réduit ici côté applicatif à

une demande de lecture.
une réception de liste ... en 0.2 secondes
Analyse de l'applicatif(2/3):
Analyse de l'applicatif(2/3):

Le script Get_Params...accrochez - vous! je plaisante


Code PHP:

list    list_parameters;
list    
list_errors;
list    
list_values;

integer nb_errors;
integer nb_comments_and_blank_lines;
integer nb_parameters;
integer nb_lines;

integer Nombre_de_parametres;

integer display_results ()
{
    
llOwnerSay "Nombre de lignes traitées ="  + (string) nb_lines );
    
llOwnerSay "Nombre de lignes de commentaires ou vides  = " + (string) nb_comments_and_blank_lines );
    
llOwnerSay "Nombre d'erreurs détectées =" + (string)  nb_errors );
    
llOwnerSay "Nombre de paramètres detectées " + (string) (nb_parameters/2) );
    
    if ( 
Nombre_de_parametres != (nb_parameters/2))
    {
        
llOwnerSay "Nombre de paramètres incorrect! (attendus = " + (string) Nombre_de_parametres +")"  );     
  
    }
    
    if (
nb_errors 0
    {   
        
llOwnerSay (    "\n\n ******* Erreurs ******** \n" 
                        
llDumpList2String(list_errors,"\n") +
                        
"\n **********************" );
        return  
FALSE;
    }
    else
    {
       
        
llOwnerSay (    "\n\n ***** Parametres ****** \n" );
     
        
integer i;
        
list_values = [];
            
        for ( 
0nb_parameters
        {
            
llOwnerSay "Parametre " + (string) (+ (i/2)) + ": " 
                          
llList2String list_parameters) + 
                          
" => " llList2String list_parametersi+1) );
            
            
list_values +=  llList2String list_parametersi+1);
            
        }
                        
        
llOwnerSay "\n\n *********************** \n" ); 
        return  
TRUE;
    }

    
}

default
{


    
    
link_message(integer sender_numinteger numstring msgkey id
    {
        if ( 
num == 50 )
        {
            
list_parameters llParseString2List(msg, ["%"], ["/"]);    // Parse the string back to a list
            
nb_lines = (integer) ( (string) id );
        }

        else if ( 
num == 51 )
        {
            
list_errors llParseString2List(msg, ["%"], ["/"]);    // Parse the string back to a list
            
            
nb_comments_and_blank_lines = (integer) ((string) id);
            
            
nb_errors       llGetListLength(list_errors);
            
nb_parameters   llGetListLength(list_parameters);
            
            if ( !
display_results() )
                
llOwnerSay "Corrigez vos erreurs.." );
            else
                
llMessageLinked(LINK_THIS60 ,llDumpList2String(list_values,"%"),"");    
            
        }
        else if ( 
num == 55 )
            
llOwnerSay msg " Corrigez vos erreurs.." );    
        else if ( 
num == 99 )
            
Nombre_de_parametres = (integer) msg;    
    }

En fait il est beaucoup plus simple que sa lecture laisse supposer..

il a simplement deux parties

display_results qui sert à donner le compte rendu

et link_message qui reçoit les messages.


Je vous fais grâce du module display_results rien à dire de spécial


les messages maintenant:

1 vient de l'applicatif donnant le nombre de paramètres attendus

3 qui viennent du script de lecture

Les messages contiennent differentes infos bien utiles

list_parameters = les lignes de parametres brutes

nb_lines = nombre totales de lignes lues

list_errors = les erreurs déja detectées

nb_comments_and_blank_lines = explicite!

avec ces 5 données on déduira toutes les autres infos...


Pour finir:

llist_values = contient les paramètres seuls et sera adressé à l'applicatif si ...aucune erreurs n'a été detectées.


Code PHP:

llMessageLinked(LINK_THIS60 ,llDumpList2String(list_values,"%"),""); 

Analyse de l'applicatif(3/3):
Analyse de l'applicatif(3/3) ouf!:

La partie la plus intéressante mais hélas la plus complexe. Librement inspiré (et modifiée) du wiki pour l'intégrer dans le projet...

Attachez - vous cette fois!


Code PHP:

string NOTECARD_NAME    "Fichier de configuration"// name of the card we are going to read
integer notecard_line   0;
integer num_notecard_lines 0;

integer empty_or_comment_lines;

key notecard_request NULL_KEY;

list 
card_data// the data in the card
list card_errors// the data in the card
 
integer check_card(string name// check that that the named inventory item is a notecard
{
    if (
llGetInventoryType(name) == INVENTORY_NOTECARD )
        return 
TRUE;
    else
        return 
FALSE;
}
 
default
{
    
state_entry()
    {
        
state init;
    }
}
 
state ready
{

    
changed(integer change)
    {
        if (
change & (CHANGED_INVENTORY)) // if someone edits the card, reset the script
        
{
            
llResetScript();
        }
    }
    
link_message(integer sender_numinteger numstring msgkey id
    {
        if (
num == 99 
        {
            
llMessageLinked(LINK_THIS50,llDumpList2String(card_data,"%"),(string) num_notecard_lines);
            
llMessageLinked(LINK_THIS51,llDumpList2String(card_errors,"%"),(string) empty_or_comment_lines);
        }
    }
}
 
state init
{
    
state_entry()
    {

        
integer type llGetInventoryType(NOTECARD_NAME) ;
        if (
type ==  INVENTORY_NONE)
        {
            
// notify owner of missing file
            
llOwnerSay(">> " NOTECARD_NAME " <<...est absent!");
            
state error;
        }
        else if ( 
type !=  INVENTORY_NOTECARD)
        {
            
// notify owner of wrong file type       
            
llOwnerSay (">> " NOTECARD_NAME " <<...n'est pas un type de fichier Notecard!");
            
state error;
        }

        
llSetText("initialising...", <111>, 0);
        
notecard_request NULL_KEY;
        
notecard_line 0;
        
num_notecard_lines 0;
        
llSetTimerEvent(5.0); // if we don't hear back in 5 secs, then the card might have been empty
        
notecard_request llGetNumberOfNotecardLines(NOTECARD_NAME); // ask for the number of lines in the card
    
}
    

    
timer() // if we time out, it meant something went wrong - the notecard was probably empty
    
{
        
llSetTimerEvent(0.0);
        
llOwnerSay  "Lecture >> " NOTECARD_NAME " <<...TIMEOUT");
        
state error;
    }
    
dataserver(key query_idstring data)
    {
        if (
query_id == notecard_request// make sure it's an answer to a question we asked - this should be an unnecessary check
        
{
            
llSetTimerEvent(0.0); // at least one line, so don't worry any more
            
if (data == EOF// end of the notecard, change to ready state
            
{
                if (
num_notecard_lines 1)
                    
state ready;
                else
                {
                    
llOwnerSay (">> " NOTECARD_NAME " <<...est vide!");  
                    
state error
                }
            }
            else if (
num_notecard_lines == 0// first request is for the number of lines
            
{
                
num_notecard_lines = (integer)data;
                
card_data = [];
                
card_errors = [];
                
notecard_request llGetNotecardLine(NOTECARD_NAMEnotecard_line); // now get the first line
           
}
            else
            {
                if ( (
llStringTrim(dataSTRING_TRIM) != "")  &&  (llGetSubString(data00) != "#") ) // ignore empty lines, or lines beginning with "#"
                

                    
integer index llSubStringIndex(data"=");
                    if (  
index  )
                    {
                        
// get parameter of parameter / value pair
                        
string param llStringTrimllGetSubString(data0index 1),STRING_TRIM ) ;
    
                        
// get value of name/value pair
                        
string value llStringTrimllGetSubString(dataindex 1, -1),STRING_TRIM ) ;
                        
                        if ( ( 
param != "") & ( value != "") ) 
                            
card_data +=  [param,value];
                        else
                            
card_errors += ( "Line " + (string) notecard_line "\t" +  ">" data "<");                            
                    }
                    else
                        
card_errors += ( "Line " + (string) notecard_line "\t" +  ">" data "<");
                    
                }
                else
                {
                    ++
empty_or_comment_lines;
                }
                ++
notecard_line;
                
notecard_request llGetNotecardLine(NOTECARD_NAMEnotecard_line); // ask for the next line
            
}
        
// update the hover-text with the progress
        
llSetText("read " + (string)(notecard_line) + " of " + (string)num_notecard_lines " lines", <111>, 1);
        }
    }
 

 
    
state_exit()
    {
        
llSetText("", <000>, 0);
    }
}
 
state error
{
    
state_entry()
    {
        
llOwnerSay("Erreur concernant le fichier >> " NOTECARD_NAME " << ");
    }
    
changed(integer change)
    {
        if (
change CHANGED_INVENTORY)
        {
            
llResetScript();
        }
    }

Le script est largement commenté ce qui aidera les puristes et les curieux...

Comment fonctionne t il?

Déjà tout seul! et sous l'aspect d'une plaisanterie cela n'en est pas une...

En effet souvenez vous du temps de 0.2 s de lecture... rendez possible parce qu'il n'attend personne pour lire le fichier de configuration dès qu'il en a l'opportunité ( detecter un changement ) il va le lire et effectuer tous les tests "système" et charger toutes listes que nous avons vu précédemment.

Code PHP:

    changed(integer change)
    {
        if (
change CHANGED_INVENTORY)
        {
            
llResetScript();
        }
    } 
De façon plus général la fonction Dataserver DEVRAIT toujours être dans un script différent. Si on veut se dédouaner de toutes les contraintes liées à cette fonction cette architecture est parfaite.

Se rappeler TOUJOURS que les scripts sont des taches ... et que les serveurs SL sont multitasks.

je poursuis...

Une deuxième info est l'utilisation IMPERATIVE d'un timer ( watchdog)

Code PHP:

    timer() // if we time out, it meant something went wrong - the notecard was probably empty
    
{
        
llSetTimerEvent(0.0);
        
llOwnerSay  "Lecture >> " NOTECARD_NAME " <<...TIMEOUT");
        
state error;
    } 
Si quelque chose coince dans Dataserver et plus particulièrement sur le serveur SL ce timer nous permettra de se sortir de l'impasse.


Ordre de transmission venant de l'applicatif...

Code PHP:

link_message(integer sender_numinteger numstring msgkey id
    {
        if (
num == 99 
        {
            
llMessageLinked(LINK_THIS50,llDumpList2String(card_data,"%"),(string) num_notecard_lines);
            
llMessageLinked(LINK_THIS51,llDumpList2String(card_errors,"%"),(string) empty_or_comment_lines);
        }
    } 
C'est ici que ce script va simplement envoyer ses 4 données essentiel au second script... données qu'il a déjà lu depuis une moment...

Le reste reste du classique mise à part la batterie de tests que j'ai mis en place, libre à chacun d'en rajouter...

Mon but visait plus à présenter une architecture globale qu'à présenter du code pur du code.

Pour ceux que veulent l'utiliser comme tel... juste remplacer mon applicatif par le votre et paramétrer le fichier de configuration.

Ce projet est dispo sur Xstreet libre de droits pour 0$L.

https://www.xstreetsl.com/modules.ph...ItemID=1714825

Je complèterai possiblement ce post mais pour l'instant j'ai faim!
Citation :
Publié par Seb_01
Chacun son avis Elenia sur le sujet...

Je pense que ne pas traiter les erreurs et SURTOUT ne pas en informer l'utilisateur final conduit inévitablement à une situation pénalisante, bloquante voire dangereuse. Surtout que le pauvre utilisateur final n'a pas accès au script, souvent il ne peut pas faire un reset, il a aucune idée des conséquences etc...
Si un objet est distribué à grande échelle et que le paramétrage final (customisation) n'a pas de garde fou ... tu peux être certain que le créateur va crouler sous les IMs.
...

Seb,
Je suis également d'accord sur ce principe. Sinon c'est bien présenté et expliqué, il suffirait d'un petit pdf pour le distribuer et ça serait parfait.
Citation :
Publié par BlackShade Nightfire
Je suis également d'accord sur ce principe. Sinon c'est bien présenté et expliqué, il suffirait d'un petit pdf pour le distribuer et ça serait parfait.
Merci

Pour le PDF suis pas sûr de disposer dans mon style et mon mode de fonctionnement des qualités de notre Maitre à tous.. et des profs de l'école.
Répondre

Connectés sur ce fil

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