Sunday, September 19, 2021

Eclipse Operating System

Un de mes objectifs en 1997, c'était de parvenir à maîtriser le mode 32-bits des PC. Windows était encore un hybride 16/32 à l'époque, et pas franchement convainquant pour la programmation de jeux. Tous les jeux dos parus sur les dernières années annonçaient "Dos4GW" ou quelque-chose de ce genre, et la taille toujours croissante des modules de mon frère me faisaient bien comprendre que "640Ko pour tout le monde", ça ne serait plus suffisant bien longtemps.

Seulement voilà: basculer du mode 16-bit au mode 32-bit (connu à l'époque sous le nom de 'mode protégé'), ça demande une rigueur à tout épreuve et ma documentation était malheureusement plutôt imprécise. En plus, les fonctions du BIOS et du MS-DOS deviennent du coup inaccessibles (plus de chargement de fichier ni de changement de mode graphique, par exemple). Et au milieu de tout ça, je tombe sur une pépite venant de la démoscène: Eclipse Operating System et son jeu-démo Greedy. Il me lancera à la recherche du Watcom C++ sans lequel on ne sait pas recompiler les exemples. Une chasse qui durera jusqu'à la Inscene, si ma mémoire est bonne.

Saturday, September 11, 2021

Wall walk test.

If I want to have An understanding of what causes Bilou to get into walls when he arrives straight into it, I need to find a way to reproduce that at will. I have to update my slopS unit tests to cover that new scenario.

The difficulty with that setup is that the mecha will have to keep walking towards the wall for some frames, and only then try to turn back. Scripting a fake game pad controller to force the mecha to do that sure won't be the easiest path to follow. But I have a plan.

ouais. Bon. 'faudra que je le traduise, lui

Let's have a second mecha that is walking "normally" on one large floor while another one, the Gob-Under-Test is tracking its position and tries to follow it, just like Berry Bats would do, except "walking". That should work, right ?

Well, reality is hitting me hard, here. I haven't managed to get the test cycle work properly. I'm caught in the process of navigating the history and running because even with the obvious sorted out, my new test gets a "memory is leaking" warning and the former 'slopes' test isn't working either.

I think from now on, I'll use the 'default' branch as a way to track what is properly passing automated tests and what isn't. Good news, commit 6ab9f8ca2931 dating from this year (April 13rd) pass the slopes tests.

Caveats when doing things like this:

  • Makefile does not detect whether I change devkitpro version, so things that used to work years ago on an older devkit may fail to build nowadays and you'll have to manually clean output/*.o
  • some tests depend on fakreoot/*.cmd, which is not automatically re-populated from SchoolTests/efsroot/ or AppleAssault/efsroot/. and in SchoolTests, the files are generated from SchoolTest/cmd/, which does not occur if you just rebuild unit tests.

18731874

edit: I identified a commit at which the TestBasicSlopes started failing. Surprisingly, it was not a side-effect or whatever: the commit directly does


       if ((cspeed&cp) && !cdata[0]) continue;// this testpoint only works with speed
-      if ((chkflr&cp) && !(checks[pos] & F_FLOOR)) {
+      if ((chkflr&cp) && (checks[pos] & F_FALLTHRU)) {
        if (isi) isi->TestPoint(xtile, ytile, pos, checks[pos], "floor");
        cdata[GOB_TESTPOINTS]|=cp;
       }

Which cannot have no effect on how testpoints behave in the slope tests. But if we compare how monsters in the green zone behave before (1873) and after (1874) this commit, it is clear that it has desirable effects. So I'm now sure I have to fix the tests, not the way testpoints are handled. 

edit++: okay. The leaking object was a 'builder' helper used by the GobState objets. The builder is a std::* object temporarily used while e.g. collecting transitions from a given state before they got 'flattened' into an array for the 'running' part. The array, just like GobStates are allocated in the 'tank', a larger memory chunk meant to host many small objects that all have the same lifetime: one level.

In the 'slope' tests, each state was used to spawn a GameObject using that state for the test. There were no transitions: we first use a forward-moving mecha to check things are fine when moving forward with the Forward state, and then a distinct backwards-moving-mecha. But for my new test, I need transitions. That means that I won't spawn for every states. That means some states are not 'frozen' into the tank, and hence still hold references to std::* objects like vectors or strings. These are the leaking ones. Let's get cracking!

Tuesday, August 31, 2021

Pyramid mechanics

Jusque là, j'avais prévu de me servir des "blocs poilus" pour servir de blocs destructibles dans la pyramide, mais je crois bien que j'ai trouvé mieux. Et tout simple, avec ça: des pots. Eh oui. Ils peuvent être brisés, ils peuvent libérer des trucs, ils peuvent bloquer le chemin de Bilou.

Bilou peut naturellement ramasser des trucs. Les pots aussi, mais on peut faciement dire que seuls ceux qui n'ont rien par-dessus. On peut construire plein de scènes sur cette base-là, où un 'mur' de pots bloque un accès, qu'on ne saura libérer qu'avec une carapace de scorpion.

What can play the role of breakable blocks in the pyramid ? I once thought it could be furry blocks, and possibly I'll keep them in some places ... but building walls with them ? or large structures like SMB3 "pyramidal stack" ? I finally found something that could work better. Okay, lifting up jars and throwing them against walls to get goodies isn't exactly an original mechanics, but it would work fine with the pyramid setup as well as with a shell from a scorpio.

On peut en imaginer des 'intacts' qui font faire demi-tour à la carapace et d'autres déjà fendus qui laissent passer d'un coup.

Enfin, on peut ranger des pots sur une étagères, on peut les faire tomber en cognant l'étagère ... bref, ils peuvent servir de 'briques en l'air' qu'on casse en tapant dedans par le bas.

L'autre truc qui ne me branchait pas, c'est les lanceurs de flèches. J'avais une note proposant 'des statues de chat pour les boules de feu, des statues d'aigle pour propulser vers le haut'. Mais des piafs embusqués qui tentent de becquer Bilou et dont on peut utiliser le cou comme tremplin-à-la-Ori, ça serait sans doute nettement mieux.

One other thing needed more thoughts: wall hasards. Like arrow throwers or spears pointing out and coming back. I loved the idea of them being helpful to climb up with the right timing, but we've been served the cliché of climbing up spears stuck into walls after you dodged them so many times ... It wasn't too clear what would throw them either.

But given how birds are expected to play a major role in the last world of "Bilou Dreamland", I thought "why not long-neck beaked baddies trying to snap Bilou and then getting stuck into the wall facing them, so we can use their neck to climb up ?

Monday, August 30, 2021

Trouver le thread fautif

Bon, ce coup-ci, j'étais sur un autre cas de figure: le programme fautif est toujours occupé à tourner, mais un de ses threads est mort, causant une attente infinie dans un autre. Je sais demander un .dmp au Task Manager, ouvrir le .dmp en question sur la machine de dev avec les bons chemins pour les serveurs de symboles (eh oui, moi du futur: tu as oublié mais sous Windows, il n'y a pas de .elf de debugging: les symboles de debug sont d'office dans un fichier séparé) et parcourir la liste des threads en espérant y voir apparaître KERNELBASE!UnhandledExceptionFilter dans la pile d'appels correspondante. 

Ce n'est pas trop dur de repérer l'adresse du crash, mais c'est une autre histoire de connaître l'état des registres au moment du plantage.

Il devrait y avoir une extension de windbg pour gérer ça, mais le site qui la proposait a disparu dans les limbes du temps. Par contre si je parviens à deviner où elle se trouve, la structure EXCEPTION_POINTERS devrait nous fournir toutes les infos nécessaires pour continuer à debugger.

Eh bien voilà: cette structure est produite dans le stack frame (les variables locales, NdT) de ntdll!_C_specific_handler (encadré en rouge dans la capture ci-contre) et référencée dans le stack frame de UnhandledExceptionFilter. J'ai utilisé les adresses renseignées par la colone 'Child-SP' et retracé ça à la main dans Gimp, hein. Ici, un seul candidat: 0x16bee7c0.

Reste à s'assurer que c'est bien la bonne adresse. Je peux utiliser la commande windbg dt 0x16bee7c0 EXCEPTION_POINTERS pour le coup: si je suis dans le bon, elle me donnera deux pointeurs valides

En cliquant sur le 'exception record', on devrait avoir un code valide (type 0xc000000*) et une adresse qui correspond à notre fonction fautive (aussi renseignée comme adresse de retour pour KiUserExceptionDispatch, soit ici à l'intérieur de MoveSmall).

Thursday, August 26, 2021

Trouver un core dump ...

bin oui, parce que coder, c'est aussi chercher pourquoi un programme s'est planté. Linux est assez sympa sur ce coup-là avec son ulimit -c unlimited qui permet d'autoriser la production de 'core dumps' dans le répertoire courant pour un shell donné sans que tout le système ne se mette à saturer le disque dur en cas de dysfonctionnement. Après, on ouvre le dump dans un débuggeur et en avant pour l'autopsie.

Mais là, pas'd'bol, je suis sous Windows. Le boulot, vous comprenez. Le frangin de Jigé me proposait de faire clic-droit-debug dans le gestionnaire de tâches (onglet 'détails' de préférence, apparemment) et hop youpi, visual studio prendrait en marche le programme fautif. ça m'arrangerait assez bien vu qu'il est appelé depuis du python au bout d'une séquence de 3 ou 4 programmes histoire que la caméra à l'autre bout du fil soit dans les bonnes dispositions pour les tests. 

Mais re-pas'd'bol, quand je fais ça, j'ai juste droit à "not responding" sur Visual Studio puis les deux programmes qui s'arrêtent avec pas plus d'infos. ça marchait mieux avec "create dump file", mais ce n'est pas ce qu'il me faut aujourd'hui.

Pour le coup, je vais devoir refaire appel à devenv /debug dans une console VSpéciale et croiser les doigts pour être dans les bonnes conditions pour reproduire le crash. 

  • c'est apparemment normal d'être accueilli dans un Visual Studio qui fait semblant de ne pas savoir ce que vous voulez. Debug>Start Debugging devrait nous mettre en route
  • ça devrait aussi être normal que la fenêtre qui s'ouvre quand le programme est démarré (après tous les téléchargements de symboles depuis les servers MS) se ferme automatiquement en cas d'arrêt du programme. On va mettre un p'tit breakpoint sur exit pour calmer un peu tout ça;
  • bon, par contre j'ai beau torturer ma ligne de commande, pas moyen de reproduire le crash dans cet environnement-là. Il va falloir trouver autre chose.

Et le plus agaçant, c'est que je sais que Windows peut le faire. Sur tant d'autres machines, il m'a saoûlé avec ses boîtes de dialogues ok/cancel/debug... ça pourrait être parce que Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug est vide, mais non: il me fait l'effet d'avoir bien tout ce qu'il faut là où il faut.

Ahmaisattends ... le programme de test mélange une bibliothèque C++ et du code compilé avec GHC, et le runtime haskell a un flag supplémentaire +RTS --generate-crash-dumps -RTS. On peut toujours essayer d'utiliser ça pendant notre debugging VS... Ah ? ... Bingo ?

On transfère bien son .pdb sur la machine de test ... Et voilà: j'ai un nom de fonction et un numéro de ligne. La méditation va pouvoir commencer.

Ah oui, il y a aussi le Windows Error Reporting (WER) system, et ses fichiers "planqués" dans C:\ProgramData\Microsoft\Windows. C'est ce qui ressemble le plus à mon ulimit. il faut créer la clé HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps et y définir "DumpType"=dword:00000002 (et plus si affinités. Attention, ça change le répertoire utiliser pour les dumps :-P)

Il y aura aussi procdump -e à tester, tiens.

Monday, August 23, 2021

walking kritter

A large part of the 'walk' effort is to have characters' feet sticking on the ground while walking. It may not always be a good idea but I always grimace when I see walking characters half-sliding on the ground in games with no obvious reason. At least for monsters, it should almost never occur.

In my current engine, this is achieved by hints in the animation sequence about how much pixels the character would travel with the new frame. Animator code then ensures the game character has already accumulated that much 'deferred moves' before switching simultaneously to both the next frame and the next position.

Quand je fais marcher un personnage, j'aime généralement mieux qu'il soit bien ancré dans le sol. Ce n'est pas toujours forcément une bonne idée, mais les personnage qui donnent l'impression de patiner au lieu de marcher alors qu'ils ne portent pas de patins, ce n'est pas ma tasse de thé. Du coup, mon moteur de jeu est prévu pour des animations qui contiennent des indices sur le nombre de pixels (plutôt que de frames) à attendre entre deux images.

But checking the walk cycle for the 'Kritter' monster in DKC makes me realise that there might be an easier approach, in which the motion speed for the character is constant while its animation still features a 'natural' motion where it speeds up and down: just offset the monster graphics within the buffer. This might sound like advice from a nightmare from a one-hardware-sprite-per-object point of view, where pixels would have to be effectively shifted in video memory to accomodate for the partly-moved object, but for a SNES-like sprite that re-codes the relative OAMs positions with every frame anyway, it could be nice to have.

Now there's a catch: the hitbox for your character will move at constant speed and your graphics will not. That might be source of other (Infogramesque) collision issues.

ça a aussi l'avantage de fonctionner avec des cycles de marche qui ne sont pas uniformes. Les boîteux, les excessifs, les discrets ... toutes catégories de personnages qui viendraient sans problème peupler l'univers de Bilou. Et où regarder après un exemple de ce genre de persos mieux que dans la série Donkey Kong Country ?

J'avais donc repris l'animation d'un Kritter et choisi un point de référence qui devrait rester ancré au sol (un orteil) pour voir comment il se déplaçait de frame en frame. Chose assez intéressante: on est souvent autour de la même valeur de 5 pixels par frame d'animation, mais pas tout le temps. Et vu les faibles déviations, vu aussi que l'image doit de toutes façon être reconstituée à partir de plusieurs sprites hardware, on pourrait probablement ici avoir un game object qui avance à vitesse constante et avoir un léger décalage sur la gauche ou la droite de l'image par rapport à la hitbox. Attention quand-même à ne pas pousser ça trop loin sous peine de créer des situations infogramesques.