Friday, November 30, 2007

dev-fr.org ... le paradis du homebrew

Bon, j'ai bien aimé "Bloops", adoré "Tetris Attack DS" et "Setsuzoku no Puzzle" ... Mia est passé sur PSP. J'ai bien ri avec Kaboom et versé une larme de nostalgie sur XRickDS et GianaSister DS ... Quoi d'autre. Quid quand les blogs restent quois ? Tournons nous vers les forums !

Après quelques semaines peu convaincantes sur playeradvance (trop noob) et gbadev (trop prise de tête), j'ai essayé dev-fr, et jusqu'ici, je suis séduit ^_^. C'est tout à fait le genre "gazette" que je cherchais. Si vous avez déjà acheté un magazine un peu "touche à tout" sur l'informatique (je pense par exemple au PC team, création graphique, musicale, jeux, OS alternatifs et programmation), vous aurez peut-être remarqué que j'ai tendance à animer ce blog un peu comme quelques rubriques dans ce style de journal.

Okay, i liked Bloops, loved Tetris Attack DS and Setsuzoku no Puzzle. I quite liked Mia's homebrew too, but he's now dev'ing on the PSP. Kaboom made me laugh and both XrickDS and GianaDS have wet my eyes with tears of nostalgia. Then what ? What when blogs are silent ? Let's check out the forums.

My experience on player advance (too n00b) and gbadev (people are just too complicated) didn't convince me until i found dev-fr.org. That's exactly the kind of homebrew gazette i was looking for (i once hoped Drunken Coders news would offer this, but it turn out they merely advertise new movies available on the moonbook project >_<).

Here's a small sample on what you can found there (okay, it's a french-speaking forum. Noone's perfect). An interesting event on that board is the Saturday's demo. A homebrew guru details with videos and code a common special effect or programming technique. It can be about particle systems or the use of horizontal blank interrupts to achieve wobbling and parallax scrolling. Very pleasant.

 Petit tour d'horizon, donc, de ce que j'ai vu d'intéressant. Je laisserai à mon frère le soin de faire les tests et les scores ^_^

FAT Mod Player, par cid2mizard. Comme le nom l'indique, ça sert à écouter .mod ... Tous les mods que vous auriez mis sur votre carte flash.

Autre évènement sympaLa démo du samedi soir: scrolling list. Un principe assez original: illustrer des techniques de programmation (généralement sur la DS) avec le code à la clé. Des articles généralement très bien ficelés, et dignes de ce que l'on trouvait dans les magazine (même si les démos, elles, ne sont pas nécessairement très impressionnantes comparées à ce que l'on voyait sur la Scène Amiga il y a 10 ans d'ici. Le mot d'ordre, c'est BabySteps ...

Plop invaders ... un petit jeu bien sympa qui rappelle le principe de Chuzzle (mais pas tout à fait) avec un scénario digne des invasions blorks de Kid Paddle.

C'est mignon, ça fait cogiter les neurones et c'est bien ficelé, même si les graphismes dans les phases de jeu sont parfois un peu légers à mon goût (et les plops sont probablement un chouya trop petits).

Then come the Plops. Kind of blobs from unexpected nuclear experiment that you have to erradicate by exhausting their air. A chuzzle-like (though the gameplay differ) with a Kid Paddle-like scenario. Cute, mind-twisting and well tied together, despite some effort could be done to improve in-game gfx.  

Par Mollusk , Pitt et Zeblackos. Une collection de mini-jeux un peu curieux, réalisés dans un langage dont personne ne voudrait, mais pour un résultat qui vous dévorera des heures de transport en commun.   

Another blog-worthy stuff is Mental Games by Mollusk & friends. The kind of minigames pack that many suggest on the DS, but with a retro-look that gave me the urge to try it (compared to Puzzle Maniak which still leaves me cold). Ideal if you want 5-minutes fun in the bus. Check out my blogmarks if you want to keep up to date of my other findings. 

Le tout, évidemment, uploadé sur ma DS Phat avec mon propre petit "runme", que j'ai boosté à 1Mbps, et ses scripts perl ... Parce que je le veux bien.

edit: je suis loin d'avoir fait le tour, bien sûr, et comme je ne suis pas mon frère, je ne vais pas essayer d'animer une rubrique "le jeu homebrew de la semaine". Mais si je repère un jeu qui a l'air de valoir le coup sur ces forums, je l'ajoute dans mes blogmarks.

Saturday, November 24, 2007

BG_WRAP_ON ?

I read somewhere that "Background Wrap only applies to rotation backgrounds (Rotation Backgrounds.) Setting the bit to 1 enables wrapping of the background while specifying a value of 0 disables wrapping of the background. Specified by BG_WRAP_ON or BG_WRAP_OFF". -- this suggest we need MODE_5 if we want two planes for the level data. I did the test this afternoon and it turned out that mode 0 is very well capable of wrap-scrolling. And actually it seems to be supported by all the BG layers, too.

class UnderProgress : public Animator {
unsigned offset;
public:
UnderProgress():Animator(1,"-cx-") {offset=0;}
virtual Animator::donecode play() {
if(!running) return Animator::DISMISS;
offset++;
BG2_X0=offset;
SUB_BG0_X0=-(offset>>1);
return Animator::QUEUE;
}
};


Difficile de trouver des informations précises sur le scrolling de la DS, en fait. Si bien que les infos trouvées sur GBAtek et autres wiki laissaient à penser que seul le mode 5 (avec des plans carrés capables de rotations et zooms) pouvait fonctionner avec le "scrolling infini". C'est effectivement le rôle de BG_WRAP_ON pour les plans dits "extended rotation", mais je vous assure pour l'avoir fait fonctionner pas plus tard qu'il y a 2 minutes que le mode 0 et ses plans "texte classique" supporte parfaitement le scrolling infini, et sur tous les background.

En fait, c'est le comportement par défaut de la console et notre bit "BG_WRAP_ON" n'est là que pour désactiver cette fonctionnalité (qui pourrait être gênante si on essayait de se servir des plans déformés pour faire de la 3D (cf. le "mode 7" de la SNES).

Je souffle un coup, donc: je pourrai sans soucis choisir la taille 512x256 qui m'est si chère pour l'instant (les mode 5 n'offrant que 128x128, 256x256, 512x512 et 1024x1024).

(PS: j'en profite pour signaler à ceux que l'anglais rebiffent que le premier des posts "tutoriel" sur le scrolling est (enfin) complètement traduit).

Thursday, November 22, 2007

Scrollons encore

On discutait scrolling avec Cyril hier midi, et il m'a fait remarqué assez justement que je m'inquiétais inutilement pour les scrolling à grande vitesse. Bin oui, tant qu'on ne scrolle que d'un pixel à la fois on peut s'en sortir très bien avec seulement 512 pixels de large dans la mémoire vidéo. Au cours du décalage, on finira bien alors à se retrouver avec un morceau de 256 pixels appartenant à un seul bloc de VRAM à l'écran et on a les mains libres pour changer le contenu du deuxième.

example of what concerns me
Mais si la vitesse devient trop élevée, on risque de se retrouver à passer d'un 'bloc' à l'autre mais en gardant toujours plus d'un bloc de VRAM à l'écran, ce qui nous empèche de mettre l'image à jour sans prendre le risque d'un affichage inconsistant. Mais c'est là que Cyril est intervenu: rien n'empèche d'arrêter le scrolling plus tôt que prévu juste pour ce 1/60eme de seconde... On a alors de nouveau les mains libres pour copier le bloc suivant en VRAM, et au coup suivant, on scrollera un peu plus. L'un dans l'autre, ça devrait être invisible (edit 26.11) J'ai testé sur le temps de midi: c'est indécelable.

Hypothesis: scrolling code will be better if we can do one-big-DMA-copy into a contiguous VRAM block (that is, one 32x32 tiles square in the 512x256 pixels setup I envision)
Problem: at speeds higher than 1px/frame, we might never have one of the squares fully off-screen because we switch from "still showing some pixels from the logical square on our left" to "already showing some pixels from the logical square on our right.
Proposal: snap the camera to the squares grid when a transition is detected. That snapping will affect the scrolling speed, but only for 16.6ms so it shouldn't be noticeable.
Result: that looks smooth, indeed. I cannot notice when the snapping happens.

types de blocs pressentis pour Bilou, et leur encodage sur 4 bitsReste un autre problème: l'encodage des "types" de blocs. Si je faisais une copie élément par élément à la main (jouable si on remet un bloc de 8x8 pavés à jour d'un coup), je pouvais chiper les bits "miroir" pour encoder les types dans la carte "offscreen", et les remettre à zéro avant la copie dans la surface "onscreen".
Mais pour remettre tout un bloc à jour (2K), il serait franchement préférable de passer par le DMA.
Mais bonne nouvelle, on peut s'en sortir avec un encodage un peu plus subtil. Imaginez: on a besoin d'encoder si chaque tile est solide, traversable, incliné, etc. mais aussi s'il s'agit d'un bonus, de pics, de lave, etc. Trop pour 4 bits ... sauf si on encode que 12 types de tiles et que l'on se sert des numéros 12 à 15 pour des blocs plus "spéciaux", par exemple ceux dont seul le personnage du joueur doit s'occuper.

Now a more important issue: I also need to encode some properties about each tile, and my plan so far was to use palette and mirror bits for that. That means 64 different properties, which sounds good.

Issue: Palette bits won't hurt unless I enable multi-palette modes (no such plan at the moment), but mirror bits will affect how things show on screen
Idea: Let's have 12 "direct" properties for slopes, ground, air, etc. and have only other properties for 16x16 pixels blocks. That means we can combine the palette bits of 4 tiles to know what block (out of 256 possible special blocks) we use.

Alors, quel est le truc ? Eh bien voilà. Notre niveau n'est pas construit à partir de tiles 8x8 mais à partir de blocs 16x16. On peut donc supposer que la lave prendra bien 16x16, et ce n'est pas juste 2 bits (de 12 à 15) que l'on a pour faire la différence entre les picots, la lave ou les bonus, mais bien 4x2 bits (2 dans chaque tile faisant partir du bloc 16x16). Soit 256 types de blocs spéciaux possibles. De quoi voir venir...

2024 #choice reality check: scrolling code went for 64x64 pixels "square" instead of full blocks. DMA copies are used only for vertical scrolling, but it does not require such pause-and-resume scrolling logic.

2024 #choice reality check: a newmeta" effort has been started after 2 games have used the 'palette bits", so we could have more slopes and more colours with multi-palette.

Monday, November 19, 2007

Ideas for smooth scrolling on the DS...

Bien sûr, la DS est capable de faire un scrolling fluide à travers un grand niveau, mais ce n'est pas nécessairement une mince affaire. Je cogitais sur les méthodes possibles l'autre jour et j'ai pris mes notes dans mon blog.

Je rafinerai et traduirai plus tard ...

maps & tiles sharing VRAMThe hardware (from Liranura's 2D tutorial)

Map bases always have 32×32 entries. To create bigger BGs (Like 64×64) you need to use several map bases. As for tile bases - If you want to make use of the full tileset (1024 tiles), you will have to use several tile bases - 4 Tile bases for 256col full tileset, and 2 Tile bases for full tileset of 16col mode. Since the Map bases and the Tile bases are located on the same region in VRAM, they can overlap. Since a map base is 2Kbytes and a Tile base is 16Kbytes, each Tile base contains 8 Map bases.
Une surface affichable sur la DS est constituée de 32x32 tiles (l'écran en fait 32x24) et occupe 2KB dans la mémoire vidéo. Pour construire des plans de décor plus larges (p.ex 64x64 tiles), il nous faudra plusieurs 'morceaux' de 32x32 mis côte à côte pour former une surface plus grande. On retrouve le même phénomène avec les tiles (pour utiliser l'entièreté d'un jeu de tiles (1024 tiles), il nous faut 4 sections de 16K en mode 256 couleurs). Notez que c'est au programmeur de choisir s'il va stocker 16K de tiles ou 8 maps dans une région donnée de la mémoire vidéo.

La console nous permet de positionner l'écran librement dans la surface définie (pour chaque plan séparément, hein, ) et -- contrairement aux carte VGA -- par défaut la surface 'boucle' sur elle-même (si la zone affichée déborde par la droite, c'est la gauche de la surface qui sera réutilisée).

We can have hybrid technique ...
At some point we have to alter parts of the map to keep scrolling smoothly. Nothing forces us, however, to have a unified technique that accomodates both horizontal and vertical moves. The fact that the screen isn't a square, that memory stores lines continuously and columns discontinuously and similar consideration -- not omitting the fact that our game may have a side-scrolling or vertical-scrolling action (shmups ;) -- may suggest that we have two distinct mechanisms for updating the map after a vertical and a horizontal movement of the "camera".

Le système de scrolling va forcément mélanger software et hardware. Le côté hardware, c'est le choix de la portion visible de la surface en VRAM (on l'appelle "onscreen surface" -- litéralement 'à l'écran' en anglais). Le côté software consistera à modifier des portions de cette surface (de préférence qui sont actuellement invisible pour le joueur) afin de donner l'illusion d'un décalage continu alors qu'en réalité, nous somme piégés dans un carré de 512x512 pixels.
Avant de ce lancer dans le code, il serait bon de se rappeler que rien ne nous oblige à avoir une technique unique pour les scrolling horizontaux et verticaux. Les uns pourrait parfaitement s'opérer à grands coups de DMA sur des tranches entières alors que les autres se feraient "bloc par bloc" gérés entièrement depuis le CPU.
  1. we can't have smooth scrolling in a 64x64 area if we reprogram 32x32 entries (dma copy) at once (<-- cyril contests this, see next post).
  2. we could do the "John Carmack" trick, using hardware scroll in the 64x64 area and then "jump" to a second 64x64 area when we're about to move out, but that would kind of be silly on hardware that allows to "wrap around" the area with our viewport (which the PC hardware couldn't do properly).
  3. doing "double-buffer" of the map will involve a burst of up to 8K memory transfer (through DMA), potentially leading to gameplay lags
  4. we have better things to do with the other maps (e.g. additional planes)
  5. DMA copy for the map might be interesting to save CPU (well, it is actually frozen during the move), but it requires that the offscreen map is a verbatim copy of what's used on screen. manual copy of the map parts instead allow us to use more bits for tiles meta-data and wipe them as the tiles are moved on screen. That would give us 64 types of block, more than we need to encode "slipperiness", identify bonus, etc.
thinking again ...
Nothing forces us to use 64x64 ... since the height of the screen is smaller than 32 tiles, we can very well work with e.g. 64x32 instead. This is not only nice because it halves the amount of work without loss of functionnality (maybe a bit less effective "caching" of vertical scrolling), but also because it means we now can do the "carmack jump" thing effectively: Suppose we are working with maps 0 and 1 initially (side-by-side 32x32 areas of the level map) and that the viewport is getting close to the edge of the currently displayed area ... rush! we dump another 32x32 chunk of the level map (and now we can just ask for a DmaCopy of the whole data if it is properly stored offscreen) into map #2. When it then appears that we are about to scroll out of the current 32x32 area, we then cheat the video hardware, reprogramming the map base to be map#1 (instead of map #0) and the X scrolling register to show us in the new area (most likely x=x-256 or x=x+256 depending on the movement).

Autre petite constatation: rien ne nous oblige à utiliser du 512x512 pixels! en fait, ce serait même l'inverse (en cas de scrolling horizontal, ce mode nous oblige à faire plus de mises à jour et ne nous offre pas grand-chose en contrepartie). Au passage, ça nous permettrait de faire un "scrolling à la John Carmack" plus efficacement (enfin, au moins une fois de temps en temps...).

Mais entretemps, je suis nettement plus sceptique sur l'interêt de la technique Carmack (rendue célèbre par la série des Commander Keen) sur le hardware de la DS.

I'm not sure we could keep it smooth forever with that technique, unfortunately: there would be no way for us to tell the hardware that map #N-1 and map #0 should be shown as a continuous 64x32 area...

see also captain Apathy's tutorial
voir aussi le tutoriel de Edorul (GBA)
Sonic the Hedgehog 2 (Emerald Hill) from soniccenter.org

Friday, November 16, 2007

Un autre arbre pour Bilou

Je me suis fait une petite scéance de "speed-pixels" ce midi. On ouvre the Gimp, on récupère les couleurs de la forêt de Donkey Kong: King of Swing et c'est parti.

L'idée, c'est de faire vite et de mettre les éléments en place: ils seront retravaillés plus tard dans mon sprite editor, mais ce dernier n'est pas encore très pratique quand on veut rapidement entasser quelques gros ronds pour faire une forme de feuillage (à noter pour plus tard, en blanc sur bleu, ça aurait pu faire de beaux nuages) ou les formes étirées du tronc d'arbre.

J'en dirai plus plus tard, mais je crois que c'est un bon début...

Thursday, November 15, 2007

2D sur la DS : quelques chiffres ...

Plus de 640K de mémoire vidéo sur la DS -- c'est presqu'autant que la mémoire vive dont on disposait sous DOS sans les extensions. Mais comme je vous le montrait dans un de mes posts précédents, il faut répartir cette mémoire entre les deux écrans, les fonds, les cartes, les sprites et éventuellement, les palettes et les textures. Bref, pas mal de monde.

Pour un jeu de plateforme comme Bilou et Badman, le plus raisonable serait de partir sur 256K pour les décors, 128 à 256K pour les sprites (tout ça sur l'écran principal) et de garder les 128K restant pour gérer l'écran secondaire (map, inventaire ... peu importe). 256K de décors ... avec mes blocs qui font 16x16 pixel, ça veut dire que j'ai droit à un bon millier de blocs différents conservés simultanément en mémoire. Soit près de 5 écran de DS. C'est déjà honnête ...

Bien sûr, rien n'empèche de changer le contenu de la VRAM au fil du jeu. C'est notamment ce que j'ai l'intention de faire pour les blocs animés (pensez à la pièce d'or de SuperMario ou au bloc-question). On leur attribue quelques tiles sur lesquels on ira recopier systématiquement l'étape d'animation suivante au bon moment (depuis nos 4Mo de mémoire de travail, donc). Ainsi on gagne du temps et de la place. On pourrait aussi essayer d'éliminer certains blocs non-affichés pour les remplacer par d'autre en fonction de la progression du joueur dans le niveau, par exemple si l'on voulait faire un traveling continu sur les écrans d'Another World... mais il y a peu de chances que je me serve de quelque-chose de ce genre.

Dans un niveau ou le scrolling est libre dans toutes les directions, on choisira de préférence une map 512x512, de manière à pouvoir toujours modifier les "bords" de la map pour avancer dans le niveau sans que le joueur ne remarque quoi que ce soit. Il y a aussi des map 512x256 et 256x256 que l'on utilisera dans des jeux ou le scrolling suit un seul axe (les shoot'm'up, par exemple, ou des niveaux spéciaux dans un jeu de plate-forme).


Evidemment, on pourrait se dire que c'est du gâchis de prendre une carte de 512x512 alors que 256+16 aurait suffi à un scrolling fluide (réfléchissez un peu: pourquoi le GBA a-t-il un écran de 240 pixels de large, selon vous?). Mais ce serait compter sans le fait que la "carte" des tiles peut aussi nous servir directement pour savoir si notre personnage (et tous ses adversaires) ont affaire à un mur ou à un trou. Chaque "tile" est encodé sur 16 bit dans la carte (cf cet autre post précédent) et on peut choisir parmis 1024 "tiles" différents, les autres bits permettant de "retourner" les blocs horizontalement et verticalement.


single tile encoding, from LiraNuna's tutorial


Restent 4 bits inutilisés qui pourraient se révéler bien pratiques pour encoder directement le type de bloc: creux, plein, "montée" à 45°, "descente" à 45°, "montée" à 22.5% ...

Dans Rayman Designer, il y a 24 types de blocs différents, y compris le bloc semi-solide (on peut passer à travers en sautant, mais pas en tombant), les blocs glissants (de nouveau 7 inclinaisons différentes), des blocs qui blessent, qui tuent, qui noient, qui font rebondir, escaladable, etc.

<obsolete why= "si les bits "palette" sont actuellement effectivement utilisés, c'est en les combinant par blocs de 4 tiles que l'on étendra le nombre de propriétés disponibles.">

Bien sûr, avec 4 bits de libre, c'est un peu juste pour proposer les 24 types de Rayman, et pourtant, dans Bilou, il nous faudra bien tout ça. Mais dans notre cas, il y a peu de chance que l'on se serve du "retournement" de tiles. Le plus souvent, ça fouterait en l'air tous nos efforts d'éclairage sur les blocs. Résultat, on pourrait s'arranger, par exemple, pour stocker certains blocs "à l'envers" et pour pouvoir se servir du bit "miroir horizontal" pour faire la différence entre les montées et les descentes ... L'alternative, ce serait d'avoir une des combinaisons pour indiquer que le bloc est "spécial", et de se servir à ce moment de son numéro pour choisir le comportement approprié, sachant que ce sera un peu plus compliqué à traiter... L'idéal, ce serait de garder ça pour les interactions que seul le personnage fera (grimper, se blesser, etc.) et d'avoir les effets sensibles égalements pour les monstres directement encodés sur la map...

</obsolete>

Enfin. 'faudra tester tout ça. J'espère que ça ne vous a pas paru trop imbuvable: j'avais besoin de mettre les idées au clair avant de me lancer dans le code de gestion des niveaux ...

Wednesday, November 14, 2007

LBM & Linux

Je suis un vieux de la vieille qui a commencé ses pixels sous MS-DOS. Et de temps en temps, j'aime pouvoir récupérer mes vieux dessins tout pixelisés. Mais voilà, bien peu de programmes supportent les fichiers .LBM de mon ancien Deluxe Paint II enhanced :(

Sous ubuntu, pourtant, le package netpbm fournit un outil ilbmtopbm qui servira à ImageMagick (les programmes 'display' et 'convert', entre-autres) pour passer du format LBM au format PBM (Portable Bit Map) -- inconnu de tous, sauf de la bibliothèque ImageMagick qui peut pratiquement tout faire à partir de là.

Reste que imagemagick persiste dans l'idée que mes fichiers Deluxe Paint devraient s'appeler ".ilbm" et pas ".lbm". Comme c'est pénible et que je suis geek, j'édite le fichier /usr/lib/ImageMagick-6.2.4/config/delegates.xml et là ou on voyait une ligne "decode 'ilbm', je passe à

  <delegate decode="lbm" command='"ilbmtoppm" "%i" &gt; "%o"' />
<delegate decode="ilbm" command='"ilbmtoppm" "%i" &gt; "%o"' />


Bref, voilà comment mon frère a maintenant mes petits sprites de Kid Paddle pour les envoyer sur le forum de Midam ^_^

(packages à installer: libimlib2, imagemagick et netpbm/libnetpbm10)

Monday, November 12, 2007

Java pas m'énerver ...

Quand il s'agit de manipuler un document XML, moi, mon interface préférée, c'est XPath. Pas besoin de code bien compliqué: on fait comme si tous les éléments étaient des petits fichiers dans une arborescence, et on donne un chemin pour trouver les éléments qui nous intéressent. J'avais déjà pu m'en servir avec succès en PERL, je m'attendais à quelque-chose d'ultra-simple en Java, langage de l'XML par excellence.

J'y vais donc de mon petit test débile:

import java.lang.*;
import java.util.*;
import javax.xml.xpath.*;
import org.xml.sax.InputSource;

public class XpathTest {
public static void main(String[] args) {
try {
System.err.println("creating xpath...");
XPath xpath = XPathFactory.newInstance().newXPath();
String expression = args[0];
InputSource src =
new InputSource(new java.io.FileInputStream(new java.io.File(args[1])));
System.err.println("search for "+args[0]);
String content = xpath.evaluate(expression, src);
System.out.println("here: "+content);

xpath.reset();
src = new InputSource(new java.io.FileInputStream(new java.io.File(args[1])));
Object node = xpath.evaluate(expression, src, XPathConstants.NODESET);
System.out.println("here: ("+node+")");
} catch (javax.xml.xpath.XPathExpressionException ex) {
System.err.println("oops: "+ex);
} catch (java.io.FileNotFoundException ex) {
System.err.println("no file : "+ex);
} catch (java.io.IOException ex) {
System.err.println("doh: "+ex);
}
}
}

Tout ça à l'aide de la première référence que je trouve: la doc de sun bien claire, sauf sur un point "c'est quoi cette 'nodelist' que je suis supposé recevoir mais qui m'est transmise sous la simple forme d'un 'Object' ?". Au bout de quelques minutes de tests, je me rends compte que la seule chose qui marche, c'est le chemin "/" qui reprend tout le texte du document. le reste ("/feed/entry[1]/title" ou "//author", ce genre de choses chouettes et utiles), ne donne aucun résultat. Pas une exception ou un message d'erreur. non. rien, tout simplement. que le chemin corresponde à un noeud existant dans le document ou pas, d'ailleurs.

grmbl.

Puis à force d'essayer, je finis par constater que le fameux objet que je récupère est de la classe com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList.

Et là, je dis: ça pue. pourquoi m'ont-ils renvoyé un objet interne non-documenté et (de plus) qu'est-ce que c'est que ce "sun...apache..." ou bien c'est dans le standard, ou bien ça n'y est pas, non ?


allons bon ...
Xerces does not provide an XPath implementation (it implements only support for the limited expressions allowed by XML Schema constraints). Xalan provides an XPath 1.0 implementation and from the com.sun.org.apache... package name it seems there is the Sun (derived from Xalan) implementation that is included with the JVM.ref

Tout de suite, ça m'étonne moins. Reprenons calmement avec la doc d'apache, donc.
  1. on installe le package "xalan", un traducteur XSLT qui est sensé fournir XPath pour de vrai
  2. on redéfini sa variable d'environnement CLASSPATH pour pointer sur /usr/share/java/xalan2.jar
  3. on google sur "org.apache.xpath example" et pas "xpath java example"
Voyons voir. Ouais. des tonnes de Factory et docBuilder.parse() ... 'fallait s'y attendre. Et naturellement, rien n'aide dans le code java ou dans JavaDoc à retrouver comment il serait bien possible de construire un objet de telle ou telle classe à partir d'un nom de fichier, ce qui rend toujours les tutoriels Java pénibles (très 'magie noire', si vous voyons c'que j'voulions dire).

Bref, le code le plus simple auquel j'arrive ressemble à ceci:
import org.apache.xpath.*;   // for XPathAPI, obviously!
import org.apache.xpath.objects.*; // for items, nodelists, etc. (XObject)
import org.w3c.dom.*; // for Document and friends
import java.lang.*; // for String and the like.

/** see also:
http://xml.apache.org/xalan-j/apidocs/javax/xml/parsers/DocumentBuilder.html
*/
class xpath2 {
public static void main(String args[]) {
Document doc=FileToDoc(args[0]);
String xpath = args[1];
System.out.println("\nQuerying DOM using xpath string:" + xpath);
try {
XObject o = XPathAPI.eval(doc, xpath);
System.out.println(o.getTypeString()+"> "+o);
} catch (Exception e) {
System.err.println("doh:" +e);
}
}
}
mais pour ça, il me faut écrire une horrible fonction capable de passer d'un nom de fichier à un Document, parce que visiblement, les gens qui ont écrit XPathAPI n'ont pas pensé à rendre ce cas-là simple :(
    static Document FileToDoc(String fname) {
/* well, Document is an interface and cannot be built simply */
/* -- grmbl -- */
try {
javax.xml.parsers.DocumentBuilderFactory docBuilderFactory =
javax.xml.parsers.DocumentBuilderFactory.newInstance();
javax.xml.parsers.DocumentBuilder docBuilder =
docBuilderFactory.newDocumentBuilder();

// Parse the XML file and build the Document object in RAM
return docBuilder.parse(new java.io.File(fname));
} catch (Exception e) {
System.err.println("parse error: "+e);
return null;
}
}
Notez que si vous avez déjà un InputStream prêt sur le contenu (p.ex. une connexion réseau), inutile de passer par un fichier: docBuilder.parse(InputStream) existe également. Il y a même docBuilder.parse(URL) pour ceux qui ne sont pas occupés à faire un exercice sur l'écriture d'un client HTTP :P
(soupir)

Et pour être sûr, ils ont *aussi* rendu la réécriture de documents XML vers un fichier tordue:
 import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);


Pour la postérité, la javadoc de l'API XPath sur le projet Apache.

Jouons avec netcat ...

Les blogs sont nos amis, mais pour la plupart d'entre nous (même les programmeurs), la simple question "y a-t-il du neuf sur mon blog préféré" paraît trop complexe pour qu'on se donne la peine de la programmer. Pourtant, j'aimerais bien lancer mes étudiants sur un TP basé sur les flux RSS. Alors ?

Alors, bin souvenez-vous du post sur netcat, cet outil unix capable d'ouvrir et de manipuler des sockets en ligne de commande (je simplifie, mais bion). Avec quelques connaissances de HTTP 1.1 on va pouvoir s'amuser un peu. Tout d'abord, il nous faut l'URI d'un flux RSS, en l'occurence, le mien: http://sylvainhb.blogspot.com/feeds/posts/default

Nous avons déjà l'outil wget qui nous permet de récupérer une ressource sans passer par un navigateur, mais netcat va nous permettre de bidouiller un peu notre requête HTTP et de récupérer "en live" la réponse du serveur.

Voyons donc. La manière la plus simple pour récupérer la page avec notre flux RSS en HTTP correspond à

GET http://sylvainhb.blogspot.com/feeds/posts/default HTTP/1.1
Host: sylvainhb.blogspot.com


Auquel le serveur répondra quelque-chose comme
HTTP/1.1 200 OK
Content-Type: application/atom+xml; charset=UTF-8
Last-Modified: Mon, 12 Nov 2007 11:00:00 GMT
Cache-Control: max-age=0 private
ETag: "8ec946e4-7f26-4280-8dad-30c1e7f20e51"
Transfer-Encoding: chunked
Date: Mon, 12 Nov 2007 11:15:53 GMT
Server: GFE/1.3
Connection: Close

c04
<?xml version='1.0' encoding='UTF-8'?>
<?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
<id>tag:blogger.com,1999:blog-34057821</id>
<updated>2007-11-12T12:07:24.724+01:00</updated>
<title type='text'>Bilou HomeBrew's Blog</title>
...


La réponse est divisée en deux parties: des en-têtes, qui nous indiquent ce que l'on va recevoir, de quand celà date et comment gérer la suite du transfer et le contenu qui nous intéresse (le flux RSS proprement dit). Ceux qui ont fait un peu de programmation WEB auront tout de suite reconnu le "look HTML" de notre contenu. Il s'agit en fait d'un document XML, qui adopte les conventions d'HTML pour présenter n'importe quel type d'information (ici, un "flux" () qui possède ses paramètres propres (titre, dernière mise à jour ...) et une série d'entrées (), qui correspondent aux articles du blog.

Grâce à la balise , il serait possible de suivre l'évolution du blog en redemandant régulièrement le flux par HTTP et en comparant avec la dernière version reçue. Mais bon, là, mon flux pèse dans les 128K et ce serait ridicule de tout retransférer à chaque fois. Il y a évidemment moyen de faire mieux -- et à mon avis, c'est un des principaux avantages de HTTP/1.1 par rapport à sa version 1.0.

Vous avez remarqué la ligne "Last-Modified" dans les en-tête de la réponse ? Il s'agit de la dernière date de mise à jour (enfin, ce qu'en pense le serveur). Et nous pouvons demander au serveur de ne nous retransmettre le document que s'il a changé depuis cette date avec une petite variante de notre requête:

GET http://sylvainhb.blogspot.com/feeds/posts/default HTTP/1.1
Host: sylvainhb.blogspot.com
If-Modified-Since: Mon, 12 Nov 2007 11:00:00 GMT


Le serveur ne répondra maintenant par un "200 OK" que si la page a changé. Dans le cas contraire, on aura droit à un petit "304", sans contenu. Nettement plus léger, non ^_^ et plus simple que d'extraire à nouveau la balise <updated> pour comparer avec la précédente. Et tout ça avec une seule ligne de commande (à mon avis, bien plus sympa pour les tests que de lancer un "telnet" et d'essayer de taper toute sa requète avant que le serveur WEB n'estime que vous êtes trop lent):

echo "GET http://sylvainhb.blogspot.com/feeds/posts/default HTTP/1.1\nHost: sylvainhb.blogspot.com\nIf-Modified-Since: Mon, 12 Nov 2007 09:51:53 GMT\nConnection: close\n\n" | nc sylvainhb.blogspot.com 80


Petit raffinement: sous Unix, on dispose aussi d'un outil "watch" qui appelle la même commande, encore et encore. On pourra donc se faire un "surveilleur de blog ultra-geek" avec
watch 'echo "GET http://sylvainhb.blogspot.com/feeds/posts/default HTTP/1.1\nHost: sylvainhb.blogspot.com\nIf-Modified-Since: Mon, 12 Nov 2007 09:51:53 GMT\nConnection: close\n\n" | nc sylvainhb.blogspot.com 80'


Bon, okay, la date de référence pour laquelle on teste les mises à jour est toujours la même ici, ce qui rend l'exercice assez inutile. C'est que le début, d'accord, d'accord ?

(edit: amusant: non seulement cette méthode m'indique quand il y a un nouveau post, mais aussi si un post a été remanié ou s'il y a un commentaire en attente de modération ;)

Thursday, November 08, 2007

Encore Titus

De temps en temps, je passe faire un tour sur vgmaps.com, un chouette site qui répertorie les niveaux de jeux vidéos comme un "atlas". C'est du boulot, évidemment. Ayant récupéré "the Blues Brothers" sur abandonia, je me suis amusé à photographier le jeu au fur et à mesure.

the Blues Brothers, c'est le jeu où Titus Interactive prépare les ingrédients de ses futurs jeux. Des munitions (caisses à lancer sur les ennemis), pas encore tant de passages secrets, mais une série de "salles secrètes", comme les cavernes de Prehistorik (premier du nom) qu'il faudra explorer pour rassembler les objets nécessaires au concert de Jack et Elwood, mais qui peuvent aussi bien contenir des "bêtes" bonus ou un loubard.

Many games feature a way to carry items between different places, but Titus Interactive, in their nineties, brought that as the number-one mechanic of the game. The amno, for instance, are things you carry along, like crates. You can only carry one at a time, and if you get hit, you lose what you're carrying. The result is a platformer where careful progress is required, but where you also face a puzzle dimension, in the sense that you will need to pick your route to save amnos.

The whole is spiced up with some exploration, as you will need to find a special item in each level to get the "good ending" of the game. By then, I found that mix much more interesting than the usual straightforward, infinite amno, gun-oriented platformer.


Autre détail qui a son importance, si vous êtes touchés alors que vous transportez une caisse, vous la perdez, ce qui veut dire que plus loin dans le jeu, vous serez désarmé face à un autre danger. Bref, le moindre faux-pas est lourd en conséquence. Autre exemple, les designers se sont amusés à s'assurer qu'à deux ou trois endroits, vous risquiez de retomber plus en arrière dans le jeu (de préférence là où vous n'aviez pas la possibilité d'éliminer tous les ennemis).

  • niveau 1 : le magasin (guitare dans une des échoppes). On affronte des loubards, des mémés en caddie, des policiers, des serveuses furieuses et des jardiniers toqués. Une fois dans les nuage, c'est le piaf pond-vite qui nous mêne la vie dure
  • niveau 2: en construction (lunettes et chapeau que je ne parviens pas à atteindre. Peut-être faut-il un parapluie ...) Sale gosses armés de pulvérisateur d'acide et gros bras maniaques de la clé à molettes, mais gare aux mares d'acides et aux broyeurs.

Autre petit élément sympathique, des objets tels que les parapluies ou les balons du premier niveau, offrent un gameplay plus varié.

Blues Brothers II -- jukebox adventure: un flopBref, du bon jeu de plateau comme on aimerait en voir à nouveau. Dommage que l'opus "II" (juke box adventure) n'ait pas été une réussite... Bien sûr, les graphismes sont nettement plus beaux, mais ils ont complètement perdu le fil de l'histoire. Tout d'abord, le principe même du jeu n'a plus rien avoir. On le catégorise sur abandonia de "no-brain fun", et en effet, plus la moindre recherche. La progression dans le niveau est devenue ultra-linéaire. Finies les caisses, ici on lance des disques, et il faudra souvent en balancer plusieurs pour venir à bout d'un monstre apparemment inoffensif.

Là aussi, le bât blesse: alors que Blues 1 nous avait fait affronter des personnages parfois déjantés, mais toujours réalistes, on se retrouve ici face à des pièges à loup vivants et géants, et .. euh une sorte de tondeuse à gazon croisée avec une pelleteuse mécanique qui à visiblement des instincts de pit-bull.

Bref, les deux premiers niveaux sont beaux, mais franchement insipide une fois leur côté démo/tutoriel passé, et dès le niveau 2, les monstres sont tout simplement pénibles (mouvements trop rapides, impossibilité de les dégommer quand ils sont "au repos", bref, pas impassables, mais pénibles)

Et pour le coup de grâce, je vous laisse contempler, hébétés, nos fameux blueseurs transformés en sur-hommes après avoir mangé une part de gâteau, ce qui semblerait leur permettre de faire des sauts plus longs, et peut-être renforcerait la puissance de leur lancer de disque.

Ridicule et pas convaincant pour un sous. Bref, autant Titus Interactive nous a fait de bons jeux dans ses jeunes années, autant il semblerait qu'ils aient été contaminés par la transformation du monde du jeu vidéo en un "marché" et progressivement perdu toute notion du gameplay et de la conception des bons niveaux. Je n'oserais même pas dire qu'ils ont transformé BB en un clône de Megaman: la seule ressemblance viendrait du fait que l'on tire sur des ennemis dans un sidescroller.

Bref, concentrez-vous sur le premier volet à moins d'être un graphiste en manque d'inspiration.

Monday, November 05, 2007

Pas bien (2)

je ne dois pas blogger au lieu de préparer ma défense de thèse.
je ne dois pas blogger au lieu de préparer ma défense de thèse.
je ne dois pas blogger au lieu de préparer ma défense de thèse.
je ne dois pas blogger au lieu de préparer ma défense de thèse.
je ne dois pas blogger au lieu de préparer ma défense de thèse.
je ne dois pas blogger au lieu de préparer ma défense de thèse.
je ne dois pas blogger au lieu de préparer ma défense de thèse.
je ne dois pas blogger au lieu de préparer ma défense de thèse.