samedi 28 juin 2008

asio et les timers

Dans AdH, certaines actions s'effectuent dans la durée. Par exemple, un joueur peut décider d'aller à la plage pour ramasser des coquillages, et avoir par exemple une chance d'en trouver toutes les 10 secondes. S'il décide de s'en aller, l'action courante est automatiquement annulée.

Ce qui tombe bien, c'est que asio::deadline_timer gère tout cela (presque) tout seul. Je peux créer une action, et lui dire d'appeler une méthode de ma classe de session dans un temps donné. Après ce délai, la méthode est appelée, et je peux alors effectuer l'action, par exemple rajouter un coquillage à l'inventaire du personnage.

Si le joueur change de lieu, il est facile d'annuler l'action, simplement en appelant cancel. La méthode d'action est alors appelée avec un code d'erreur, et je peux alors en profiter pour renvoyer au joueur l'information que l'action a été annulée.

Le système gèrera ainsi une action courante pour chaque joueur, qui sera utilisée notamment par toute la partie artisanat.

vendredi 27 juin 2008

L'inventaire

Le voili, le voiça, l'inventaire!

Placé dans une belle grille (j'aime bien les grilles wxWidgets, elles sont très flexibles), et directement remplie par le serveur. Maintenant, pour la voir évoluer, il va falloir commencer à gérer les actions. La première action que je compte implémenter, c'est ramasser des coquillages sur la plage. Ces coquillages pourront ensuite être revendus à un bijoutier qui en fera des colliers.



Donc, je récapitule: actions, commerce, et artisanat! Encore quelques soirées à coder en perspective.

lundi 23 juin 2008

Memcached

Connaissez vous memcached? C'est un système de cache distribué, qui permet de sauver et de retrouver un bout de binaire à partir d'une clé, la clé étant un autre bout de binaire. Ainsi, l'on a accès à une quantité de mémoire limitée seulement par le nombre de machines qui font tourner le service, et à partir du moment où l'on connait la clé, toute application pouvant se connecter au cache peut récupérer les données. Rajoutez quelques détails comme la gestion de la durée de vie des objets, et c'est un service tout à fait redoutable qui se profile.

Memcached est principalement utilisé dans les applications web. J'avais installé Mediawiki en local, et voyant le pic CPU lors de la génération de chaque page, je me demandais quel type de hardware wikipedia pouvait bien avoir sous le capot pour gérer autant de requêtes à la fois. La réponse, c'est memcached! Au moment d'accéder à la base de données, Mediawiki regarde tout d'abord si les données sont accessibles dans le cache. Si oui, il les récupère, s'épargnant une couteuse requête auprès de la base de données. Lorsqu'un utilisateur modifie la page, l'entrée dans le cache est supprimée, pour être remplacée par la nouvelle page. En majorité, seules les requêtes en écriture vont effectivement arriver sur la base de données, ce qui améliore grandement les performances. De fait, de nombreux sites à fort débit utilisent memcached.

Récemment, je travaillais sur une application qui commence à poser problème parce qu'elle arrive aux limites des 2 gigaoctets d'espace d'adressage. L'on a bien passé Windows en mode 3 gigaoctets, au prix d'une stabilité réduite, et de soucis avec d'autres applications qui ne s'attendaient pas à ce que le noyau aie son propre espace aussi réduit. Je me disais que c'est probablement la seconde utilisation qui puisse être faite de memcached: au lieu de garder les objets directement dans la mémoire du processus, on peut les envoyer dans la mémoire d'un autre processus, potentiellement sur une autre machine, et libérer ainsi le précieux espace disque sans pour autant trop impacter les performances, en attendant que l'on passe tous enfin en 64 bits.

mardi 17 juin 2008

Ressources C++

J'avais fait cette petite liste pour aider deux collègues qui se verraient bien lâcher le VB et faire du C++. Voici donc les ressources (bouquins, sites web, bibliothèques) que je considère comme un très bon point de départ pour approfondir le langage en allant un peu plus loin que la syntaxe.

Livres

* Andrei Alexandrescu - Modern C++ Design - Un excellent bouquin qui ouvre vraiment les yeux sur ce qu'il est possible de faire avec le C++.
* GOF - Design Patterns - Plutôt une référence, ne se lit pas vraiment comme un roman! Utile, probablement, mais gare cependant avec les patterns. Comme pour épicer son repas, le tout est de savoir doser.
* Scott Meyers - Effective C++ - Une bonne lecture pour s'habituer à coder proprement. Très sain.
* Scott Meyers - More Effective C++ - La digne suite du précédent.


Sites web

* C++ FAQ Lite - Reprend point par point les petites bizarreries du C++. Un must pour préparer les interviews!
* SGI STL - La doc STL. S'il ne doit y avoir qu'un bookmark sur le C++ dans votre navigateur, c'est celui-là.
* Guru of the Week Archive - Très intéressantes discussions sur le C++
* Project Euler - Tout plein de problèmes mathématiques et algorithmiques. Un excellent entrainement à la maitrise d'un langage.

Bibliothèques

* Boost - Le complément indispensable à la STL, Boost est vraiment l'API du C++
* SDL - C'est du C, mais c'est quand même très utile pour coder des jeux en C++.
* SDL_net - Si vous êtes rebuté par boost::asio, la bibliothèque réseau de la SDL est très simple d'utilisation.
* wxWigets - Un framework pour coder des interfaces graphiques.
* libpqxx - La bibliothèque pour interfacer le C++ à la base de données Postgresql.

samedi 14 juin 2008

Quand on compresse d'un côté...

Il vaut mieux penser à décompresser de l'autre. Il m'aura fallu une bonne demie-heure pour comprendre pourquoi j'avais droit à un plantage magistral lorsque j'envoyais trop de texte.



Enfin, c'est résolu, et cela marche même plutôt bien avec les alphabets non romains. Ce n'est malheureusement pas non plus parfait, comme les sinophones (non, Firefox, pas les saxophones!) distingués auront pu le remarquer: la longueur des caractères chinois est mal calculée par la fenêtre, et le passage à la ligne se produit donc beaucoup trop loin, cachant toute une partie du texte. Peut-être un rapport de bug à remonter chez wxWidgets?

libpqxx - Un tutorial

Aucun doute, libpqxx est une bibliothèque très bien foutue. C'est un wrapper C++ autour de la libpq, qui est l'interface C de Postgres.

Cependant, j'ai trouvé que la documentation était trop orientée sur la description des interfaces, et trop peu sur l'utilisation en elle-même. Voici donc un petit topo sur les transactors.

Les transactors, ce sont des objets qui gèrent l'ensemble de la transaction, le support du rollback, et les essais multiples en cas de problème de connexion. Typiquement, un transactor qui fait simple SELECT pourrait ressembler à cela:


class SelectObjects : public pqxx::transactor<pqxx::transaction<> >
{
public:
SelectObjects(std::vector & objects):
m_objects(objects)
{
}

void operator()(argument_type & T)
{
std::ostringstream sql;
sql << "select name from objecttype";
pqxx::result result = T.exec(sql.str());

pqxx::result::const_iterator it = result.begin();
pqxx::result::const_iterator end = result.end();
for(; it != end; ++it)
{
m_objects.push_back(it->at(0).as());
}
}

private:
std::vector & m_objects;
};


A noter, la structure de données qui sera remplie est passée par référence. En effet, le transactor sera copié au moins une fois, et potentiellement plusieurs si la connexion est interrompue et que la commande doit être relancée.

L'on appellera le code ainsi:


// dbConn est une pqxx::connection valide
std::vector objects;
dbConn.perform(SelectObjects(objects));


L'on pourra ensuite facilement vérifier, à l'aide de notre ami Boost, que les données ont été récupérées:


BOOST_FOREACH(const std::string & obj, objects)
{
std::cout << obj << std::endl;
}


Allez, une autre pour la route: imaginons que l'on veuille insérer une valeur dans la table.


class InsertObject : public pqxx::transactor<pqxx::transaction<> >
{
public:
InsertObject(const std::string & value):
m_value(value)
{
}

void operator()(argument_type & T)
{
std::ostringstream sql;
sql << "insert into objecttype values "
<< "(5, '" << T.esc(m_value) << "', 0.7)";
T.exec(sql.str());
}

void on_commit()
{
std::cout << "Success!" << std::endl;
}

private:
std::string m_value;
};


L'on remarquera le T.esc() qui permet d'échapper la chaine en bon SQL. Tant qu'à faire, autant éviter l'injection! La méthode "on_commit" sera appelée automatiquement s'il n'y a pas eu de souci pendant la transaction.

Voilà, en espérant que ces quelques fragments soient utiles un jour!

lundi 9 juin 2008

Ça cause!

Quand je disais qu'avec la nouvelle architecture, tout devenait simple! En un peu plus d'une heure, le système de chat était de nouveau sur les rails.



Rien de très compliqué, mais c'est à peu près le niveau de chat disponible dans la version Castor. En dehors d'un peu de nettoyage de code et d'une refonte de l'interface, la prochaine étape n'est pas encore choisie. L'inventaire? Le système de commerce? Le système de villes? Ou simplement livrer cette version (ce qui veut dire une compilation sous Windows, youpie!), et tenter de la faire monter en charge?

dimanche 8 juin 2008

Authentification

Le système d'authentification est enfin fini. Il permet à l'utilisateur de se connecter à un serveur, de s'identifier, et de choisir l'avatar qu'il souhaite incarner. Petite visite guidée.

C'est le même écran que deux posts auparavant. L'utilisateur choisit son serveur, entre ses informations, et se connecte. En l'occurrence, mon compte n'existe pas encore, je coche donc pour le créer à la volée.



Voilà la liste de mes avatars. C'est un nouveau compte, aucun n'est donc disponible. Je créé donc Nestor.



La prochaine fois que je me connecte, mon cher Nestor apparait dans la liste des avatars disponibles. Je peux le sélectionner, ou en créer un autre.



Enfin, toutes les erreurs possibles sont attrapées. Si je retente de créer Nestor, un message d'erreur m'expliquera le problème.



L'architecture tient étonnamment le coup. Cela n'a pas été toujours sans soucis: par exemple, il m'a fallu jongler un peu avec les évènements wxWidgets: mes évènements réseau étant gérés à travers le même mécanisme, l'on casse tout si l'on tente par exemple d'afficher une boite de dialogue modale directement dans l'évènement, et il faut donc se rajouter à la queue d'évènements de la boite de dialogue courante.

La prochaine étape, porter le vieux système de chat vers la nouvelle architecture. Rien de trop complexe en théorie, mais la possibilité d'envoyer de gros messages va enfin mettre à contribution le système de compression des paquets. Restez branché!

vendredi 6 juin 2008

Ping!

Ce soir, ajout du ping! Le framework étant en place, ce fut à tout casser 45 minutes de boulot, donc
ça vaut amplement le coup pour le petit côté wizzz.

Petit récapitulatif de la manière dont fonctionne la boite de dialogue d'authentification:



Le client connait, à partir par exemple d'un fichier XML (ou pourquoi pas d'une ressource web centralisée), la liste des serveurs publiés. Il tente de se connecter à chacun des serveurs, en lui demandant ses infos personnelles. Dans la boite de dialogue, le premier serveur est fonctionnel, et a donc répondu en donnant son nom, sa description, sa langue, le nombre de joueurs connectés, et le ping. Le deuxième serveur n'existe pas, la connexion échoue donc, et le serveur demeure affiché en orange.

Le ping, c'est tout bête. Le client calcule le nombre de millisecondes depuis le début de la journée, l'envoie au serveur, qui se contente de copier l'info et de la renvoyer avec quelques informations supplémentaires (le nom, la description, et autres). Au retour, le client refait le même calcul, petite soustraction, et basta.

Rajout d'une petite horloge, qui renvoie le ping pour chaque connexion encore valide. Je reçois, je mets à jour. Byzance!

jeudi 5 juin 2008

Segfault

Un étrange crash que je viens de découvrir: définissant par erreur deux fois de suite la valeur à afficher dans un contrôle d'édition de wxWidgets, il se produit une segfault apparemment à cause d'un mutex dans un autre thread, dans la libglib. La correction a bien entendu été triviale (ne définir la valeur qu'une fois), mais ce problème m'interpelle, surtout que les conditions pour le reproduire ne semblent pas si ésotériques. Peut-être un souci d'évènements qui se télescopent?