Les idées de Xenos

Un peu de vocabulaire

De Vers Cela s'appelle
Votre tête Un morceau de papier Le Design
Un document papier Un code lisible Le développement
Un code lisible Un code machine Une compilation
Un code machine Un site en ligne Un déploiement

Content Negociation

Votre jeu/site web doit être capable de faire de la Content Negociation , c'est à dire servir la page dans le format demandé par le client (via le header HTTP Accept), dans la langue demandée par le client (via HTTP Accept-Language), compressé dans le format adapté pour le client (via Accept-Encoding). Le respect de ce Content Negociation facilitera énormément votre travail côté client (dans vos requêtes AJAX par exemple) car vous n'aurez plus qu'à demander le format dont vous avez besoin là où vous en avez besoin.

Dans Eclerd ou Variispace, vous pouvez demander n'importe quelle page dans différents formats, dont HTML, mais aussi JSON, YAML ou encore PHP sérialisé et même au format Excel !

Le paramètre GET http-accept n'est pas standard: c'est la solution que j'ai choisie d'un point de vue personnel pour permettre de passer l'information du format demandé par le client ailleurs que dans le HTTP header Accept.
Cela me sert par exemple dans le cas d'une image: l'attribut src d'une balise img ne permet pas de définir les headers HTTP, donc, le paramètre GET http-accept est utilisé à la place.

La plupart de mes projets ont un fichier /resources/doc/doc.html qui indique les formats disponibles pour chaque endpoint (entre autres infos). Par exemple, la doc d'ECLERD vous donnera les format listés ci-dessus.

Les formats indiqués dans cette doc sont les formats génériques: certains endpoints peuvent les avoir surchargés (c'est souvent le cas du format HTML qui est mise en forme différemment d'une page à l'autre) ou désactivés (c'est souvent le cas des formats SVG/PNG qui sont des formats d'image n'ayant pas forcément de sens pour certains endpoints).

Monitorez votre traffic

Mettez en place un système vous permettant de savoir combien de joueurs ont joué aujourd'hui, combiend de pages ont été vues, combien de hits sur des CSS/JS/PNG..., combien d'erreur serveur (5xx) ou client (4xx) ont eu lieue, etc. Cela vous permettra de suivre un peu l'évolution de votre jeu web, sans entrer inutilement dans trop de détails: OVH propose des statistiques très avancées, basées sur le log Apache, mais elles sont souvent trop lourdes pour être utiles au quotidien; mieux vaut donc créer un petit système simple et complémentaire.

Personnellement, j'ai une CRON task quotidienne qui va voir le log Apache de la veille, et qui m'envoie par email le nombre de visiteurs, de pages vues et de requêtes, ainsi que la part des bots (indexation, genre google) et des scammers (collecte marketing et hackers), et le nombre de réponse HTTP 5xx, qui signifient que j'ai un soucis dans mon code. Cela me permet de savoir si les jeux sont plus ou moins joués, plantent ou pas, etc

Exemple de tableaux de statiques
Exemple des statistiques de fin Juin 2019

Gestion d'erreur

Votre jeu web doit être capable de gérer toutes les erreurs pouvant arriver dans le code, et doit pouvoir retourner le bon statut HTTP (500 502 etc pour les erreur serveur, 4xx pour les erreurs clients). De plus, les pages d'erreur doivent être présentables si elles sont demandées en HTML: vous ne devez pas servir une page blanche (même avec le bon code d'erreur!) mais une page ayant le même thème que le jeu web, et indiquant le problème de manière claire et esthétique pour le joueur (exemple: "La somme d'argent à transférer doit être positive").

Pensez à logger les exceptions et les erreurs (dans Mantis par exemple, ou par email) quand elles sont issues du serveur. Si elles sont issues du client, les logger n'est pas indispensable (car elles sont peut-être volontaires si le client est un pirate) mais cela peut parfois vous aider à détecter un soucis d'interface. Par exemple, dans le cas de "La somme d'argent à transférer doit être positive", logger cette erreur en plus de l'afficher à l'utilisateur vous informera que votre interface n'est peut-être pas claire (vous devriez indiquer une aide type "Entrez une somme d'argent (positive)") ou qu'un check pourrait être ajouté côté client en plus du check côté serveur (<input type="number" name="sommeArgent" min="1"/>).

Si vous utilisez de l'AJAX (XMLHttpRequest), alors vous devez également être capable de gérer ces erreurs côté client, pour ne pas laisser l'utilisateur en plan en cas de problème suite à la requête issue du Javascript.

Ne pas sortir les données de la BDD

Je vois souvent des développeurs voire des architectes système qui appliquent le procédé "sortir les données de la BDD, les manipuler dans le code client (PHP, Java etc) puis les réinjecter dans la BDD".

Cette approche est mauvaise: vous scindez en deux le modèle (stockage des données côté MySQL et traitement des données côté PHP) alors que les deux sont liés (une modification de l'un entraine forcément une modification de l'autre); vous créez des latences réseau (échanges PHP⟺SQL nombreux); vous allongez le temps de lock des lignes de la BDD, ce qui réduit le nombre d'appels concurrents possibles sur cette BDD (votre jeu sera alors lent et, non, "supprimer les transactions" n'est pas une solution!); vous ajoutez énormément de complexité côté PHP (besoin d'un DAO, d'un ORM, etc); vous éclatez un peu partout la logique de chaque page web (les DAO/ORM étant souvent réutilisés d'une page à l'autre, le flot d'exécution en PHP sera foisonnant et cela devient vite compliqué à suivre).

Ainsi, au lieu de sortir les données de la BDD pour les traiter en PHP, je vous conseille de laisser les données dans le serveur SQL et de les traiter dans ce serveur SQL: pour chaque page web, créez un dossier avec un fichier PHP (contenant le code controlleur, le modèle de données de la page, le/les formatteurs etc), un CSS/un JS/des PNG si la page en a besoin, et 1 fichier SQL contenant la définition d'une procédure stockée qui sera utilisée par la page web pour récupérer toutes les données dont elle aura besoin, structurée comme elle en aura besoin.

En passant de cette façon par une procédure stockée, vous éviterez les DAO/ORM en PHP, réduirez le temps de chargement des pages, et vous pourrez utiliser à fond les autocomplétions/refactorings/inspections d'intelliJ. En effet, dans cette approche, les procédures stockées des pages deviennent "l'API publique" de votre serveur mysql: vous pourrez refactorer toutes les structures internes de la DB (tables) et n'avoir rien à changer côté PHP, tant que les procédures stockées ne sont pas modifiées!

Le user SQL de votre code PHP n'aura besoin que du droit CALL sur ces "procédures de pages web", et vous éviterez énormément de risques d'injections/d'accès illégal à certaines données.

Le code de la procédure stockée (= le fichier .sql) ne doit pas être déployé sur le serveur web!

Pourquoi faites-vous votre jeu web?

Vous devez savoir répondre à cette question, car de la finalité de votre projet dépendra son déroulement. Vous le faites pour le fun d'essayer des nouvelles technos/stacks/code? Alors il ne sortira sûrement jamais.
Vous le faites pour en vivre? Alors c'est un vrai boulot à plein temps, qui nécessite de vous entourer de gens compétents dans le domaine de l'entreprenariat (business plan, législation, indemnisations, investissements, etc seront requis).
Vous le faite parce qu'un autre jeu vous plait mais n'existe plus? Assurez-vous d'avoir le droit de le refaire, recherchez la communauté de joueurs/créateurs autour de cet ancien jeu et trouvez pourquoi ce jeu n'existe plus pour ne pas suivre le même chemin.
Vous le faites pour étoffer votre CV? Alors misez à fond sur la partie qui intéressera votre futur recruteur (si votre CV est celui d'un designer, ce n'est pas sur le code du jeu web qu'il vous faudra passer du temps mais sur… le design! inversement pour un développeur; pour un Community Manager, ce sera encore différent, etc).

Dangers de l'ambigüité

Soyez précis et non-ambigü dans la définition des règles et des fonctionnalités de votre jeu web! Par exemple, quelqu'un a demandé sur Jeu Web comment générer une spirale à 6 bras. Le problème semble correctement défini, néanmoins, un problème sous-jascent a surgi: comment compter le nombre de bras d'une spirale? L'image ci-dessous est-t-elle une spirale de 12 bras ou d'un seul bras?

Spirale, à 12 ou 1 bras?
Combien de bras possède cette spirale?

Vous devez définir (spécifier) tous les éléments de votre jeu web de manière non-ambigüe.

Les spécifier à l'écrit vous aidera à éliminer un maximum de cas ambigüs et vous serez ainsi bien plus précis que si vous gardiez votre idée "vaguement en tête".

Le triangle du projet

Pour bien gérer votre projet (de jeu web), vous devez avoir conscience qu'il est soumis à 3 contraintes: les finalités, les moyens, le temps. Pour réussir votre projet, vous devrez donc jongler entre ces trois éléments pour les équilibrer.

Diagramme de Venn
Les 3 contraintes projet

Soyez prêt à renoncer à certaines fonctionnalités du jeu pour le sortir dans les temps (ou avec vos moyens humains et financier). Soyez prêt à mettre un peu plus d'argent que prévu dans votre projet (achat d'IntelliJ, hébergement pro, etc) pour ne pas perdre trop de temps. Soyez prêt à renoncer à d'autres loisirs pour dégager du temps pour votre projet, car il n'avancera pas tout seul (sauf si vous êtes en équipe)!

Motivez-vous

La baisse motivation sera le pire ennemi de votre projet (quel qu'il soit). Alors, motivez-vous: parlez de votre projet autour de vous, fondez une communauté, impliquez les gens dedans, présentez-leur des avancements réguliers, et sortez le jeu rapidement, même en bêta (cela s'appelle la stratégie Early Release). Sortir votre jeu au plus vite vous obligera alors à le rendre simple (donc abordable) et vous aurez des retours rapides des joueurs, vous permettant de partir ensuite dans la bonne direction (qui ne sera peut-être pas celle que vous aviez initialement en tête).

L'Early Release permet aussi de "former" votre noyau dur de joueur: comme le jeu aura peu de fonctionnalité au début, il sera abordable à vos premiers testeurs. Ensuite, quand ils auront maîtrisé cette base, vous pourrez leur proposer des fonctions et des stratégies bien plus avancées, qu'ils accueilleront avec plaisir.

Les anciens joueurs pourront aussi "former" les nouveaux arrivants dans votre communauté, leur facilitant l'assimilation d'un jeu plus complexe.

Ne rien perdre d'essentiel

Point capital pour mener votre projet à bien, vos données doivent être protégées. Je ne parle pas juste des piratages en ligne auxquels vous devrez faire face, mais bien des données du projet sur votre PC: il n'y a rien de pire (et de plus démotivant!) que de tout perdre si on vous vole votre PC, ou si votre disque dur grille/est corrompu, ou si vous perdez les accès à votre Github/Dropbox.

Ayez toujours au moins 2 backups de votre jeu web , et ne les mettez pas à jour en même temps (faites un roulement). Evitez si possible de conserver ces backups au même endroit, et faites un "test" en essayant de restaurer un fichier/dossier/voire tout le projet depuis ces backups, pour vous assurer qu'ils fonctionneront le moment venu.

Les données de votre jeu une fois en ligne doivent aussi être backupées, séparément. Laissez-ça à votre hébergeur (ex: OVH), mais conservez quand même une copie des backups sur votre PC à vous, au cas où (si jamais vous oubliez de renouveler votre NDD ou si votre compte vous est volé). Pensez à mettre cette copie locale à jour de temps à autre.

Lancer plusieurs projets

Pour bien gérer votre projet de jeu web, la concentration est essentielle: ne vous lancez pas dans plusieurs projets en même temps! C'est le meilleur moyen de n'en sortir aucun. Faites-les un par un, et si vous avez une envie pressante d'en entamer un second, faites une milestone (=terminez une étape) du premier, mettez-le en ligne et faites-le fonctionner avant de passer au suivant.

CRON task à la seconde

Si vous avez "besoin" de tâches CRON à la seconde ou à la minute (souvent demandées sur JeuWeb!), alors vous avez un problème d'architecture: soit vous avez en fait besoin d'un jeu en temps réel (essayez Unity, Neoaxis ou d'autrs stacks que PHP+MySQL), soit vous utilisez mal votre SGDB. Dans ce dernier cas, la solution consiste à stocker une information fixe en base de données, et à cacluler sa valeur à la volée lors de l'affichage (puis éventuellement de stocker de nouveau cette valeur dans la BDD).

Ainsi, au lieu de stocker le temps de trajet restant d'une unité entre deux points de la carte et de mettre ce temps à jour à chaque seconde, vous stockerez la date et heure de départ et d'arrivée de l'unité. Vous n'aurez plus, à l'affichage, qu'à calculer le temps restant.
Ou encore, si un combat doit avoir lieu à une date et heure donnée, vous avez juste à stocker les forces en présence dans la BDD, et à calculer le résultat du combat au moment de l'affichage, puis vous sauvez ce résultat en BDD. Le combat était alors censé avoir lieu à, par exemple, 14h00 mais en pratique, vous l'avez calculé à 14h37, quand le joueur s'est connecté de nouveau.

N'oubliez pas les principes de la causalité (cf cette petite conférence ): le combat n'a lieu qu'à partir du moment où quelqu'un (un joueur) est là pour le voir.

Minification

La minification consiste à prendre un fichier texte, et à en réduire le contenu le plus possible.
Pour du CSS, cela passe par la suppression des tabulations, des retours à la ligne et des commentaires.
Pour une page HTML, cela se fait de la même façon, voir parfois, en supprimant quelques caractères de plus (les guillemets autour des attributs, les / des balises auto-fermantes comme <input/> ou <br/>,...).
Pour du JS, cela se fait en supprimant les mêmes choses, mais parfois également, en remplaçant les noms de variables par des noms courts (const myVariable = 1 devient const a = 1).

Aucun intérêt

Si cette optimisation a effectivement un léger impact sur les temps de chargement, en réduisant un peu la taile des fichiers, je la trouve sans intérêt: une compression gzip (ou autre algo) sera tout aussi efficace, bien plus simple à mettre en oeuvre et universelle.

La minification cassera également les numéros de ligne et parfois les noms de variabels (en JS par exemple), rendant votre travail de débogage très (trop!) compliqué pour rien.

Sprites

Une "sprite" est une image qui en regroupe plusieurs, en les accolants les unes à côté/en dessous des autres. Ensuite, à l'affichage, on "coupera" (crop) la sprite pour n'afficher que la partie intéressante. L'intérêt, dans la tête du développeur qui met ce système en place, est "d'optimiser" son site web/jeu web en réduisant le temps de chargement. Or, c'est faux: les sprites réduisent les performances d'un site parfois sur le long terme, parfois sur le court terme, parfois les deux.

Les sprites sont une réponse applicative à un problème réseau: on n'est pas dans la bonne couche OSI, et elles répondent alors mal au problème d'optimisation (réseau) initial.

Exemples

Supposons que l'on ait un site ayant 100 images de 1Ko chacune. Considérons que le temps nécessaire pour télécharger 1 fichier soit de 1 seconde pour établir la connexion plus 1 seconde par Ko transféré. Dans ce cas, télécharger les 100 images mettra 100x(1s + (1Ko) / (1Ko/s)) = 200s. Si on les regroupe en 1 sprite, elle fera 100Ko (on suppose l'absence de compression additionnelle) et mettra 1x(1s + (100Ko) / (1Ko/s)) = 101s.
Si on semble avoir gagné du temps, un problème se posera lors de la mise à jour de la sprite: si on change 1 seule image du pack de 100 initiales, alors on devra attendre 1x(1s + (1Ko) / (1Ko/s)) = 2s de plus, soient 202s au total, alors qu'il faudra de nouveau 1x(1s + (100Ko) / (1Ko/s)) = 101s pour re-télécharger la sprite, soit un total de… 202s! On n'a rien gagné! Et à la seconde mise à jour, on aura même perdu du temps.

La même chose s'applique aux "sprites CSS/JS": fusionner tous les CSS (ou tous les JS) du site en un seul fichier sera également inefficace.

Mais il y a même des cas où dès le premier chargement, les sprites déteriorent les performances! Reprenons le même exemple que précédemment, et considérons que la première page affichée à l'utilisateur ne se serve que de 50 images sur les 100. Alors, sans sprite, il faudrait 50x(1s + (1Ko) / (1Ko/s)) = 100s de chargement. Par la suite, au fil des autres pages, il faudra de nouveau 100s pour charger le reste des images. En revanche, avec la sprite, il faudra 101s dès la première page!
On a perdu 1s de performances, sur la première page.

Certains jeux web poussent le principe à fond en essayant de charger toutes les ressources du jeu dès la première page, affichant un "écran de chargement" pendant parfois de longues minutes. De nombreux joueurs risquent de partir, et le score dans les moteurs de recherche sera misérable.

Pour optimiser votre jeu, privilégiez plutôt un bon usage du cache du navigateur , l'emploi de HTTP/2.0 et l'utilisation d'emojis ou de symboles unicode au lieu d'images customs.