Hello, Avatar!

Second Life et Programmation Créative

Aller au contenu | Aller au menu | Aller à la recherche

Du code...

Vous aimez le LSL ?
En voici, en voilà des kilomètres ! Le niveau de la difficulté du code présenté dans les articles est visible dans les tags. La difficulté est établie en fonction des critères suivants :

  • LSL Facile : Programmation élémentaire, algorithmie de base
  • LSL Assez Facile : Communication inter-primitives, boucles, tests
  • LSL Moyen : Physique, rotations, particules
  • LSL Assez Difficile : Algorithmes poussés, véhicules, astuces techniques
  • LSL Difficile : Communication externe, persistance de données, gestion d'exceptions...

Fil des billets - Fil des commentaires

Un guide du Scripting LSL en français

lslscriptingguide.png

Vous vous croyez nuls en LSL ? Comment ça, "c'est pas pour vous" ? Quoi ? Le scripting, c'est un métier ? Taratata...

Comme le basic des années 80, le LSL, c'est bidonnant. Et vous n'avez plus aucune excuses pour ne pas vous y mettre :

Bestmomo Lagan a écrit un Guide du Scripting LSL en français !

Merci, Bestmomo !!!

Utilisez le Whiteboard sur votre OpenSim !

Nous avons vu dans un billet précédent qu'il est possible de dessiner sur une primitive à l'aide d'une applet écrit en Silverlight.

Chose promise, chose dûe : nous allons voir comment utiliser ce dernier sur votre OpenSim, sans toucher autre chose qu'un paramètre et une ligne dans un petit script en LSL.

Tout d'abord, il faut activer le XMLRPC sur votre simulateur afin que les scripts LSL puissent avoir des oreilles : pour ce faire, renseignez le paramètre remoteDataPort avec le port de votre choix.

Par exemple :

; Uncomment below to enable llRemoteData/remote channels
remoteDataPort = 80

Bien entendu, il faudra rendre visible ce port de l'extérieur en ajustant les paramètres de votre pare-feu.

Voila qui est fait. A présent, prenez le script ci dessous dans un éditeur. Changez le paramètre TargetSimURL en lui mettant l'adresse publique de votre sim, puis collez tout dans le contenu d'un prim.

// Mettre ci dessous l'adresse ou est hebergee la page. Ne changer que si vous l'hebergez vous-meme
string WhiteBoardURL="http://lslblog.free.fr/draw/";

// Mettre l'adresse IP, ou l'URL de votre sim, suivie du port utilise par RemoteData
string TargeSimURL = "http://maupiti.ath.cx";

// Exemples:
// http://maupiti.ath.cx (80 par défaut)
// http://127.0.0.1:8080

key remoteChannel;
string name = "";

default
{
    state_entry()
    {
        llSetText("Touchez le prim pour dessiner dessus !", <1,1,0>, 1);
        llOpenRemoteDataChannel();
    }
   
    on_rez()
    {
        llResetScript();
    }

    touch_start(integer c)
    {
        name = llDetectedName(0);
        llSetText("Hello " + name + ", allez à l'URL indiquee dans le Chat, puis dessinez !", <1,1,0>, 1);

        llInstantMessage(llDetectedKey(0),
            "Allez sur\n " + WhiteBoardURL + "?key=" +
            remoteChannel + "&tgt=" + TargeSimURL +
            " \navec votre navigateur externe pour dessiner sur ce prim ! ^^");
    }

    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval)
    {
        if (type == REMOTE_DATA_CHANNEL)
        {
            remoteChannel = channel;
        }

        if (type == REMOTE_DATA_REQUEST)
        {
            llSetText(name + "\nTouchez le prim pour dessiner dessus !", <1,1,0>, 1);
            llRemoteDataReply(channel, NULL_KEY, "__OK__", 0);
            osSetDynamicTextureData("", "vector", sval, "512", 0);
        }
    }  
}

Voilà, en principe, ça marche !

A l'attention de ceux qui souhaitent héberger la chose eux-même, j'ai joint à ce billet un fichier zip qui contient :

  • Une petite partie Web : une page HTML, le fichier XAP pour Silverlight et un petit proxy écrit en PHP;
  • Le code source de l'application en Silverlight sous la forme d'un projet Visual Studio 2008.

Il reste encore quelques aménagements à apporter, comme par exemple pouvoir utiliser la forme executable du plugin (le XAP) sur n'importe quel site. Ce qui pour le moment n'est pas possible sans modifier une petite ligne dans l'application Silverlight.

Bon amusement !

XMLRPC sur OpenSim : osOpenRemoteDataChannel(key channelID)

Juste un petit post pour faire part d'une nouveaute sympathique dans la gestion du XMLRPC avec OpenSim et remote_data, maintenant qu'il est repare.

Comme les scripteurs le savent bien, pour transformer un script en serveur XMLRPC, il faut ouvrir un canal a l'aide de llOpenRemoteDataChannel. Ensuite, cette instruction provoque un evenement remote_data, qui, la premiere fois, transmet une key qui correspond au numero de canal.

Cette cle devra etre transmise a chaque appel. Aussi, comme au depart la key n'est connue que par le script LSL, il faut monter toute une tringlerie pour que cette key soit recuperee par le client d'une maniere ou d'une autre. Pour des raisons de securite, c'est tout a fait adapte, mais pour des prototypages d'applications simples et rapides, c'est vraiment pas le pied.

C'est pourquoi je me suis attele a creer la fonction osOpenRemoteDataChannel(key channelID) qui accueille en parametre un numero de canal de type UUID. Cela permet d'avoir une "cle publique" pouvant etre incorporee directement a un client, et de prototyper ainsi une ebauche d'echange de donnees de facon rapide.

Finis alors les llOwnerSay((string) channel), les copier/coller entre le viewer et son outil de developpement, recommencer en cas de redemarrage du script ou du simulateur et j'en passe.

Un grand merci aux membres de la core team d'OpenSim pour avoir integre ces modifs au trunk aussi rapidement.

Du XMLRPC avec OpenSim

Le XMLRPC avec remote_data vient de faire son arrivee sur OpenSim !

Pour l'utiliser, il faudra positionner le parametre remoteDataPort dans le fichier OpenSim.ini en lui donnant un numero de port.

Dans OpenSim.ini.example, on trouve :

remoteDataPort = 20800

Si le service XMLRPC que vous souhaitez implementer en LSL doit etre visible de l'exterieur, il vous faudra ouvrir le port correspondant. Pour ma part, j'utilise plutot le port 80, car mon hebergeur Web (free) bride les appels externes autres que sur certains ports.

Ensuite, vous pourrez faire un essai en prenant le script ci-dessous, qui est tire de ce tutorial, mais avec une petite variante toutefois : parce qu' un bug encore non corrige existe avec l'evenement state_exit() qui se declenche avant le state_entry(), j'ai du retirer cet evenements de l'exemple pour que ca marche.

key remoteChannel;
init()
{
    llOpenRemoteDataChannel(); // create an XML-RPC channel
}

default
{
    state_entry()
    {
        init();
    }

    on_rez(integer param)
    {
        llResetScript();        
    }

    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval)
    {
         if (type == REMOTE_DATA_CHANNEL)
        { // channel created
            llSay(DEBUG_CHANNEL,"Channel opened for REMOTE_DATA_CHANNEL" +
                (string)channel + " " + (string)message_id + " " + (string)sender + " " +                        
                (string)ival + " " + (string)sval);
             remoteChannel = channel;
             llOwnerSay("Ready to receive requests on channel \"" + (string)channel + "\"");                        
             state receiving; // start handling requests
        }
        else
        {
            llSay(DEBUG_CHANNEL,"Unexpected event type");
        }
     }
}

state receiving
{
    state_entry()
    {
        llOwnerSay("Ready to receive information from outside SL");
    }
 
     on_rez(integer param)
    {
        llResetScript();
    }

    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval)
    {
        if (type == REMOTE_DATA_REQUEST)
        {
            // handle requests sent to us
            llSay(DEBUG_CHANNEL,"Request received for REMOTE_DATA_REQUEST " + (string)channel + " " +
                (string)message_id + " " + (string)sender + " " + (string)ival + " " + (string)sval);
                llRemoteDataReply(channel,NULL_KEY,"I got it",2008);
                llOwnerSay("I just received data in "+ llGetRegionName() +
                " at position " + (string)llGetPos() + "\n" +
                "The string was " +  sval + "\nThe number was " + (string)ival + ".");
        }
    }
}

Voila, il vous suffit de glisser ce bout de code dans un primitive, puis de noter la cle qu'il vous communique afin de l'integrer au programme client.

C'est cette cle qu'il va falloir fournir lors de chaque appel au service XMLRPC. Ce qui veut dire que si vous redemarrez le script, la cle sera differente, et il vous faudra la modifier a nouveau dans le programme client.
C'est pourquoi certains scripts evolues font au prealable une sorte d'echange de cles afin de pouvoir fonctionner ensemble. C'est tres lourd.

Aussi, mon prochain travail consistera a creer une instruction LSL specifique a OpenSim pour preciser la cle qui sera utilisee par le script en LSL. Je pense a un truc du genre osOpenRemoteDataChannel(key channel), ce qui evitera la torture decrite ci-dessus.

Pour ce qui est du programme client, celui en PHP qu'on trouve sur cette page fonctionne tres bien, et pour ma part un petit truc en C# de 5 lignes a bien fonctionne egalement.

Quant aux les contraintes connues sur la "grille officielle" avec XMLRPC (delais entre deux appels et autres limitations que j'ai oublie...), elles ne sont pas applicables ici. De plus, les temps d'execution sont bien plus rapides, de l'ordre de l'instantane. Ca n'est pas etonnant, quand on pense a la charge de tels serveurs chez Linden Labs...

Voila, maintenant qu'OpenSim a des oreilles in-world, alors profitez-en !

Draw on Prims !

Draw on Prims, c'est la possibilité dans OpenSim de, tenez-vous bien, dessiner sur des faces de primitives, en vectoriel, à l'aide d'un macro langage apparenté à Logo.

Ci dessous, c'est la liste des instructions spécifiques à OpenSim pour pouvoir utiliser Draw on Prims dans nos scripts :

string osMovePen(string drawList, int x, int y);
string osDrawLine(string drawList, int startX, int startY, int endX, int endY);
string osDrawLine(string drawList, int endX, int endY);
string osDrawText(string drawList, string text);
string osDrawEllipse(string drawList, int width, int height);
string osDrawRectangle(string drawList, int width, int height);
string osDrawFilledRectangle(string drawList, int width, int height);
string osSetFontSize(string drawList, int fontSize);
string osSetPenSize(string drawList, int penSize);
string osSetPenColour(string drawList, string colour);
string osDrawImage(string drawList, int width, int height, string imageUrl);

// Et ensuite, pour afficher le tout sur un prim : osSetDynamicTextureData("", "vector", drawList, "", 0);

Ca a l'air compliqué comme ça, mais pas du tout. Regardez l'exemple ci-dessous :

//cs
public void default_event_state_entry()
{
    string drawList = "";
    drawList = osDrawLine (drawList, 10,20,240,20); // on trace une ligne
    drawList = osMovePen (drawList, 50,100);  // on positionne le crayon
    drawList = osDrawImage(drawList, 100,100,"http://opensimulator.org/images/d/de/Opensim_Wright_Plaza.jpg"); // on insere une image
    drawList = osSetPenSize (drawList, 1);  // on donne une largeur de trait
    drawList = osMovePen (drawList, 50,70); // on deplace le crayon
    drawList = osDrawEllipse (drawList, 20,20); // on trace un cercle
    drawList = osMovePen(drawList, 90,70); // on deplace le crayon
    drawList = osDrawRectangle (drawList, 20,20 ); // on trace un carre
    drawList = osMovePen (drawList,130,70);  // on deplace le crayon
    drawList = osDrawFilledRectangle(drawList, 20,20); on trace un carre (rempli celui la)
    drawList = osSetFontSize (drawList, 12 ); // on donne une taille de caractere
    drawList = osMovePen (drawList,15,32);  // on deplace le crayon
    string regionName = llGetRegionName();
    drawList = osDrawText (drawList, "Hello and welcome to " + regionName ); // on ecrit un message
    drawList = osSetFontSize (drawList, 7);  // on donne une taille de caractere
    drawList = osSetPenColour (drawList, "blue"); // On change la couleur
    drawList = osMovePen (drawList, 70,220); // on deplace le crayon
    drawList = osDrawText (drawList, "The End"); // on ecrit un message
    osSetDynamicTextureData("", "vector", drawList, "", 0); // on cree une texture dynamique !
}

A la fin de l'execution de toutes ces commandes, la variable drawList contient la chaine ci-dessous. Cette chaine, vous pouvez la créer comme vous voulez, par exemple, à l'aide d'une notecard, ou bien alors depuis un serveur externe, puis vous l'envoyez directement en paramètre à osSetDynamicTextureData.

MoveTo 10,20; LineTo 240,20; MoveTo 50,100;Image 100,100,http://opensimulator.org/images/d/de/Opensim_Wright_Plaza.jpg; PenSize 1; MoveTo 50,70;Ellipse 20,20; MoveTo 90,70;Rectangle 20,20; MoveTo 130,70;FillRectangle 20,20; FontSize 12; MoveTo 15,32;Text Hello and welcome to Mnemonic; FontSize 7; PenColour blue; MoveTo 70,220;Text The End;

Et ça donne ceci :

Draw on Prims

Pas mal, non ?

Donc, pour m'amuser, je me suis fait un petit radar (en C#). Même si il y'a un petit bug pour la couleur...

//cs
public void default_event_state_entry()
{
    llSensorRepeat("", NULL_KEY, AGENT, 96, TWO_PI, 10);
}

public void default_event_sensor(integer detected)
{
    string DrawList = "FontSize 8; PenColour blue; Text Radar; ";

    for(int i=0; i<detected; i++)
    {
        vector Pos = llDetectedPos(i);        
        DrawList += string.Format("PenColour red; MoveTo {0:#},{1:#}; ", Pos.x, Pos.y);
        DrawList += string.Format("Ellipse 3, 3; MoveTo {0:#},{1:#}; PenColour green; Text {2}; ", Pos.x, Pos.y-15, llDetectedName(i));
    }

    osSetDynamicTextureData("", "vector", DrawList, "", 0);
}

...ça donne ça, et c'est très réjouissant.

Radar

Voilà qui est donc extrêmement intéressant pour faire :

  • Des plans;
  • Des organigrammes;
  • Des courbes statistiques, des diagrammes en barre, des analyses de fonctions;
  • Des panneaux d'affichages de furieux;
  • De l'affichage de texte (j'en connais un a qui ça va plaire : Hugobiwan devrait avoir plus de facilites a concevoir une Bibliotheque speciale Francogrid :-)

Seul hic : la surface est limitée à 512x512. Mais... Que dis-je, il suffira à ce moment là d'aligner plusieurs prims !

Tiens, en parlant de panneau d'affichage... Allez admirer celui que Vinc a fait pour la Francogrid. Ca le fait !!!

Et ne cherchez pas à faire la même chose sur la grille des Linden... Ca ne marchera pas ;-)

- page 2 de 6 -