<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Développement | Magazine Jeux</title>
	<atom:link href="https://www.magazine-jeux.com/category/developpement/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.magazine-jeux.com</link>
	<description>Le magazine des jeux en ligne</description>
	<lastBuildDate>Mon, 22 Mar 2010 07:15:34 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.4.8</generator>
	<item>
		<title>Les redirections en PHP</title>
		<link>https://www.magazine-jeux.com/les-redirections-en-php/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Mon, 22 Mar 2010 07:15:34 +0000</pubDate>
				<category><![CDATA[Programmation]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/les-redirections-en-php/</guid>

					<description><![CDATA[Les redirections en PHP permettent de ne pas afficher n&#8217;importe quoi à l&#8217;utilisateur. Par exemple, lorsqu&#8217;il recherche une fiche d&#8217;un jeu sur un annuaire et que cette fiche n&#8217;existe pas / plus. Il y a quelques règles à respecter pour ne pas tomber dans certains pièges. Nous allons voir ça tout de suite. Redirection simple [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Les redirections en PHP permettent de ne pas afficher n&rsquo;importe quoi à l&rsquo;utilisateur. Par exemple, lorsqu&rsquo;il recherche une fiche d&rsquo;un jeu sur un annuaire et que cette fiche n&rsquo;existe pas / plus.</p>
<p>Il y a quelques règles à respecter pour ne pas tomber dans certains pièges. Nous allons voir ça tout de suite.</p>
<h2>Redirection simple</h2>
<p>La redirection la plus simple consiste à faire la chose suivante : </p>
<p><code><br />
header("Location: fichier_destination.php");<br />
</code></p>
<p>Cette ligne dans votre fichier PHP vous conduira vers le fichier « fichier_destination.php ».<br />
<br />Attention a ne pas envoyer au navigateur quoique ce soit avant cette ligne sinon, la redirection ne fonctionnera pas.</p>
<h2>Redirection 301</h2>
<p>Mais, dans l&rsquo;exemple d&rsquo;un annuaire où une fiche de jeu n&rsquo;existe pas / plus, il est préférable de faire une redirection de type 301. Voilà comment s&rsquo;y prendre : </p>
<p><code><br />
header("Status: 301 Moved Permanently", false, 301);<br />
header("Location: fichier_destination.php");<br />
</code></p>
<p>Et là, Google saura que la page n&rsquo;existe plus et qu&rsquo;il ne faut pas la référencer.</p>
<h2>Piège à éviter</h2>
<p>Juste un petit jeu pour finir. A votre avis, à la fin du code suivant, il y aura quoi dans le fichier « dummy.txt » ?<br />
<br />Soit il y aura « premier passage », soit « second passage ».</p>
<p><code><br />
$file = "dummy.txt";<br />
$a = "premier passage";<br />
file_put_contents($file, $a);<br />
header("Location: test.php");<br />
$a = "second passage";<br />
file_put_contents($file, $a);<br />
echo "ok";<br />
</code></p>
<p>Disons-le tout de suite, vous ne verrez pas inscrire « ok » sur votre écran.</p>
<p>Par contre, dans le fichier, il y aura « second passage ».<br />
<br />Oui, une redirection n&#8217;empêche pas la continuité du code !</p>
<p>Erreur de débutant dirons certains. Erreur tout simplement.</p>
<p>La solution, mettre une fin après la redirection : </p>
<p><code><br />
header("Location: test.php");<br />
exit();<br />
</code></p>
<p>Et là, vous avez une jolie redirection et pas de problèmes.</p>
<p>L&rsquo;idéal étant de faire une fonction avec possibilité de faire une redirection 301. Ça permet de mettre un test avant chaque redirection également. Utile pour débugger.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Optimisation : MySQL</title>
		<link>https://www.magazine-jeux.com/optimisation-mysql/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Fri, 15 Jun 2007 05:00:00 +0000</pubDate>
				<category><![CDATA[Bases de données]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/optimisation-mysql/</guid>

					<description><![CDATA[Le but ici est de comparer les types de tables MyISAM et InnoDB. Pour cela, nous avons pris une base de test que nous avons remplie au fur à et mesure avec du texte aléatoire, jusqu&#8217;à obtenir 1 000 000 d&#8217;enregistrements. C&#8217;est à partir de cette base que nous avons effectué les tests. Pour commencer, [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Le but ici est de comparer les types de tables MyISAM et InnoDB. Pour cela, nous avons pris une base de test que nous avons remplie au fur à et mesure avec du texte aléatoire, jusqu&rsquo;à obtenir 1 000 000 d&rsquo;enregistrements. C&rsquo;est à partir de cette base que nous avons effectué les tests.</p>
<p>Pour commencer, voici donc le temps nécessaire pour remplir une base :</p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>Table</strong></td>
<td><strong>Enregistrements</strong></td>
<td><strong>Temps</strong></td>
</tr>
<tr class='row_odd'>
<td>MyIsam</td>
<td>100 000</td>
<td>72sec</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>100 000</td>
<td>>2min</td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td>20 000</td>
<td>1.43 sec</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>20 000</td>
<td>105 sec</td>
</tr>
</tbody>
</table>
<p>C&rsquo;est indiscutable, InnoDB semble plus lent pour l&rsquo;insertion des données. Et pourquoi donc ?! Il faut savoir que InnoDB est de type transactionnelle alors que MyISAM est (on s&rsquo;en doute un peu) non-transactionnelle. Bon&#8230; Et ?! Ca veux dire quoi au juste ces termes barbares ?!</p>
<p>Les tables non-transactionnelles sont plus rapide, utilisent moins d&rsquo;espace disque et moins de mémoire. Alors pourquoi choisir InnoDB ?! Car les tables transactionnelles sont plus sûr (en cas de crash de MySQL, vous pouvez tout de même récupérer vos données) et pour d&rsquo;autres petits détails qui sortent un peu de cet article.</p>
<p>Les tables contiennent 4 champs : </p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>Champ</strong></td>
<td><strong>Type</strong></td>
</tr>
<tr class='row_odd'>
<td>Id</td>
<td>bigint</td>
</tr>
<tr class='row_even'>
<td>Nom</td>
<td>varchar(30), null autorisé</td>
</tr>
<tr class='row_odd'>
<td>Prenom</td>
<td>varchar(30) , null autorisé</td>
</tr>
<tr class='row_even'>
<td>Age</td>
<td>Tinyint</td>
</tr>
</tbody>
</table>
<p>Alors, nous voilà avec une table de 1 000 000 enregistrements (lignes) et nous allons pouvoir commencer les premiers tests. Juste pour info, voilà la place occupée par les différentes tables utilisées pour les tests (estimation de PhpMyAdmin, après optimisation) :</p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>table</strong></td>
<td> <strong>index sur Nom</strong></td>
<td><strong>Taille pour VACHAR(30</strong></td>
<td> <strong>Taille pour CHAR(30)</strong></td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td> non</td>
<td>56.4Mo</td>
<td>194.9Mo</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>non</td>
<td>60.6Mo</td>
<td>93.6Mo</td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td> oui</td>
<td>61.8Mo</td>
<td>202Mo</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>oui</td>
<td>104.2Mo</td>
<td>168.4Mo</td>
</tr>
</tbody>
</table>
<p>On constate que InnoDB ne prend pas forcément plus de place que MyISAM. Mais cela tient surtout aux choix de stockage des données si vous choisissez plutôt CHAR que VARCHAR par exemple. Il faut bien garder à l&rsquo;esprit que CHAR va allouer la taille indiqué (ici 30) quelque soit la quantité d&rsquo;information stockée alors que VARCHAR va allouer la taille de l&rsquo;information stockée (plus un reliquat pour indiqué la fin).<br />
<br />CHAR peut s&rsquo;avérer plus rapide sur des tailles importantes (à partir de 100), mais n&rsquo;a pratiquement aucune incidence en dessous.</p>
<p>Dans tous les tests que nous avons fait, le terme recherché, que ce soit un chiffre ou des lettres, est changé à chaque itération afin de ne pas récupérer un résultat dans un cache. Car, en effet, si MySQL reçoit une requête déjà effectuée, cette seconde requête (et les suivantes) est nettement plus rapide. La recherche est lancée plusieurs fois et le temps affiché est une moyenne. Les tests ont été relancés plusieurs fois à différents moments pour être certain que rien ne pouvait interférer sur les résultats.</p>
<h2>Les tests</h2>
<hr />
<p><strong>SELECT * FROM MaTable WHERE Age>45</strong></p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>Table</strong></td>
<td><strong>Résultat</strong></td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td>1.370</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>2.376</td>
</tr>
</tbody>
</table>
<p>InnoDB est plus lent sur une requête simple.</p>
<hr />
<p><strong>SELECT * FROM MaTable WHERE Nom=&rsquo;dummy&rsquo;</strong></p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>Table</strong></td>
<td><strong> Résultat </strong></td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td>0.504</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>1.382</td>
</tr>
</tbody>
</table>
<hr />
<p><strong>SELECT * FROM MaTable WHERE Nom<>&lsquo;dummy&rsquo;</strong></p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>Table</strong></td>
<td><strong> Résultat </strong></td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td>1.996</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>3.163</td>
</tr>
</tbody>
</table>
<p>La recherche par différence est plus longue que l&rsquo;égalité.</p>
<hr />
<p><strong> SELECT * FROM MaTable WHERE Nom LIKE &lsquo;xyz%&rsquo; </strong></p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>Table</strong></td>
<td><strong> Résultat </strong></td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td>0.512</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>1.282</td>
</tr>
</tbody>
</table>
<p>Aussi étonnant que cela puisse sembler, ici le temps est pratiquement le même pour une égalité pour un LIKE. Notons également que le temps reste le même si les données sont du type CHAR(30) ou VARCHAR(30).</p>
<hr />
<p><strong>SELECT * FROM MaTable WHERE Nom LIKE &lsquo;xyz%&rsquo; ORDER BY Age </strong></p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>Table</strong></td>
<td><strong> Résultat </strong></td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td>0.484</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>1.409</td>
</tr>
</tbody>
</table>
<p>Nous rajoutons un tri à la recherche. Du coup, InnoDB augmente un peu son temps alors que MyISAM le réduit.</p>
<hr />
<p>Attention, maintenant, nous allons faire exactement la même requête, mais en ajoutant un index sur le champs Nom. Et là, vous allez voir que ce n&rsquo;est pas négligeable d&rsquo;y penser : </p>
<p><strong>SELECT * FROM MaTable WHERE Nom LIKE &lsquo;xyz%&rsquo; ORDER BY Age </strong></p>
<table>
<tbody>
<tr class='row_even'>
<td><strong>Table</strong></td>
<td><strong> Résultat </strong></td>
</tr>
<tr class='row_odd'>
<td>MyISAM</td>
<td>0.0005</td>
</tr>
<tr class='row_even'>
<td>InnoDB</td>
<td>0.002</td>
</tr>
</tbody>
</table>
<h2>Conclusion</h2>
<p>Pas simple de conclure avec ces chiffres. Disons que si vous avez des informations importantes à stocker et que vous avez beaucoup d&rsquo;utilisateurs qui vont faire des écritures simultanées dans votre base, il vaut peut-être mieux choisir InnoDB.<br />
<br />Si vous faites beaucoup de lecture de données et peu d&rsquo;écriture, le choix doit se porter sur MyISAM.</p>
<p>Mettre un index sur un champs permet d&rsquo;augmenter considérablement la vitesse lors d&rsquo;une requête, mais augmente aussi la taille de la base. Faites donc attention surtout si vous êtes sur un serveur mutualisé.</p>
<p>Un dernier mot : il existe d&rsquo;autres type de tables pour MySQL. Notamment les tables MEMORY. Mais ce type ne peut-être utilisé pour des tables de cette taille sans avoir une quantité de mémoire très importante. Nous effectuerons très certainement des tests dans un prochain article.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>La spécificité</title>
		<link>https://www.magazine-jeux.com/la-specificite/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Mon, 14 May 2007 05:00:00 +0000</pubDate>
				<category><![CDATA[Intégration]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/la-specificite/</guid>

					<description><![CDATA[Entre les deux déclarations ci-dessous, à votre avis, laquelle sera appliquée sur l&#8217;élément de liste < li> ? .page .menu li color: red; body div.menu ul li color: blue; Si les deux déclarations désignent bien le même élément, l&#8217;une est prioritaire sur l&#8217;autre et du style de la première déclaration dont va hériter l&#8217;élément de [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Entre les deux déclarations ci-dessous, à votre avis, laquelle sera appliquée sur l&rsquo;élément de liste < li> ?</p>
<p><code>.page .menu li <em>color: red;</em></p>
<p>body div.menu ul li <em>color: blue;</em><br />
</code></p>
<p>Si les deux déclarations désignent bien le même élément, l&rsquo;une est prioritaire sur l&rsquo;autre et du style de la première déclaration dont va hériter l&rsquo;élément de liste. La couleur bleue de la deuxième déclaration ne sera pas appliquée.<br />
<br />Si la première déclaration (.page .menu li) prend le pas sur la deuxième (body div.menu ul li) c&rsquo;est que le calcul de spécificité lui donne plus d&rsquo;importance.</p>
<p>La spécificité d&rsquo;une déclaration est un calcul qui indique le « poids » d&rsquo;une déclaration.<br />
<br />Ce « poids » est alors utilisé pour comparer les différentes déclarations entre-elles et savoir laquelle est prioritaire sur l&rsquo;autre, laquelle a le plus de « poids ».</p>
<p>Le calcul se fait :<br />
&#8211; en fonction de 3 types d&rsquo;éléments qui composent une déclaration (ID, class, élément balise)<br />
&#8211; sur 3 chiffres concaténés</p>
<p>Pour connaitre le premier des trois chiffres concaténés, il faut compter le nombre d&rsquo;ID dans la déclaration (a).<br />
Pour le deuxième chiffre, il faut compter le nombre de class (b).<br />
<br />Enfin, le troisième chiffre est donné par le nombre d&rsquo;élément balise (c).</p>
<p>Le poids d&rsquo;une déclaration est le nombre donné par ces trois résultats mis côte à côte (abc).</p>
<p>Dans notre exemple :</p>
<p><code>.page .menu li <em>color: red;</em><br />
Nombre d'ID : 0 (donc a=0)<br />
Nombre de class : 2 (donc b=2)<br />
Nombre d'élément : 1 (donc C=1)<br />
</code><br />
Le poids de cette déclaration est donc de 21 (021).</p>
<p><code>body div.menu ul li <em>color: blue;</em><br />
Nombre d'ID : 0 (donc a=0)<br />
Nombre de class : 1 (donc b=1)<br />
Nombre d'élément : 4 (donc C=4)<br />
</code><br />
Le poids de cette déclaration est donc de 14 (014).</p>
<p>La première déclaration est donc bien prioritaire sur la deuxième.</p>
<p>Quelques exemples :<br />
<code>#contenu #contenu-principal<br />
</code><br />
La spécificité est de 200 (a=2 ; b=0 ; c=0)</p>
<p><code>div.informations #mentions p.important span<br />
</code><br />
La spécificité est de 123 (a=1 ; b=2 ; c=3)</p>
<p>Et comme une image (ludique) vaut toujours mieux qu&rsquo;un long discours :</p>
<p><a href="http://www.stuffandnonsense.co.uk/archives/images/specificitywars-05v2.jpg">http://www.stuffandnonsense.co.uk/&#8230;/specificitywars-05v2.jpg</a></p>
<p>Cela nous permet de comprendre pourquoi, dans certains cas « inexplicable » notre déclaration ne s&rsquo;applique pas alors qu&rsquo;elle ne contient aucune erreur. L&rsquo;élément cible hérite en fait d&rsquo;une déclaration ayant une spécificité plus forte.<br />
Parfois, ces problèmes peuvent simplement se régler en « surchargeant » la déclaration afin de lui donner plus de poids. Attention néanmoins à ne pas avoir le reflexe de trop charger ses déclarations (pour éviter ce type de désagrément) au point d&rsquo;avoir des déclarations très fortes et peu souples.</p>
<p><strong>Aller plus loin :</strong></p>
<p>Spécification CSS2 (traduction française) > Le calcul de la spécificité d&rsquo;un sélecteur &#8211; <a href="http://www.yoyodesign.org/doc/w3c/css2/cascade.html#specificity">http://www.yoyodesign.org/doc/w3c/css2/cascade.html#specificity</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Jouer avec AJAX II/III</title>
		<link>https://www.magazine-jeux.com/jouer-avec-ajax-ii-iii/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Mon, 25 Sep 2006 05:00:00 +0000</pubDate>
				<category><![CDATA[Intégration]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/jouer-avec-ajax-ii-iii/</guid>

					<description><![CDATA[Dans le dernier article nous avons discuté de ce qu&#8217;Ajax pouvait apporter en terme d&#8217;ergonomie pour le joueur. Nous sommes finalement arrivé à la conclusion qu&#8217;Ajax est très riche &#8211; quel surprise &#8211; mais aussi que cette technique peut s&#8217;avérer assez complexe à mettre en place. Notre but cette fois et de parcourir une implémentation [&#8230;]]]></description>
										<content:encoded><![CDATA[<p class="post_excerpt">Dans le dernier article nous avons discuté de ce qu&rsquo;Ajax pouvait apporter en terme d&rsquo;ergonomie pour le joueur. Nous sommes finalement arrivé à la conclusion qu&rsquo;Ajax est très riche &#8211; quel surprise &#8211; mais aussi que cette technique peut s&rsquo;avérer assez complexe à mettre en place.</p>
<p>Notre but cette fois et de parcourir une implémentation (parmi tant d&rsquo;autres) d&rsquo;un moteur de gestion de carte. Notre moteur tentera de tirer parti d&rsquo;Ajax en évitant au joueur de devoir supporter une période de loading.</p>
<p>Notre carte est volontairement simple : pas de support de layers, nous n&rsquo;afficherons que le sol. Le but ici est de démontrer qu&rsquo;Ajax peut être un outil fort intéressant dans le cadre du pré-loading d&rsquo;une carte, rien d&rsquo;autre. Evidemment si cette structure peut vous donner des idées c&rsquo;est tant mieux&#8230;
</p>
<p><span id="more-137"></span></p>
<h2> <strong>Concept</strong> </h2>
<p>Notre carte est composée de cases, toutes placées fort judicieusement les unes à côté des autres. Jusque là rien de bien nouveau.</p>
<div id="attachment_135" style="width: 272px" class="wp-caption aligncenter"><img fetchpriority="high" decoding="async" aria-describedby="caption-attachment-135" class=" aligncenter size-full wp-image-135" src="https://www.magazine-jeux.com/wp-content/uploads/2006/09/map_concept_3D_petit.gif" alt="Concept de la carte" title="Concept de la carte" class="caption" align="center" width="262" height="247" /><p id="caption-attachment-135" class="wp-caption-text">Concept de la carte</p></div>
<p>Nous ne voulons pas de loading, il nous faudra donc :</p>
<ol>
<li> Pouvoir faire le rendu de la carte de façon modulaire</li>
<li> Pouvoir gérer un ou plusieurs buffers, dont le rendu sera assuré par les méthodes du point 1.</li>
</ol>
<p>Dans notre cas nous prendrons 4 buffers, en haut, en bas, à droite et à gauche. La dimension des buffers dépends de vous et de vos tests, ici je les prendrai de 1&#215;5 (1 case de large sur 5 cases de long). </p>
<h2> <strong>Base de données</strong> </h2>
<p><div id="attachment_136" style="width: 144px" class="wp-caption alignleft"><img decoding="async" aria-describedby="caption-attachment-136" class=" alignleft size-full wp-image-136" src="https://www.magazine-jeux.com/wp-content/uploads/2006/09/DB_map_petit.gif" alt="Structure de la DB" title="Structure de la DB" class="caption" align="left" width="134" height="243" /><p id="caption-attachment-136" class="wp-caption-text">Structure de la DB</p></div>Voici la structure de la DB. Très classiquement nous allons retrouver des zones (les carrés de la carte) qui permettrons de construire la carte (on parle de tiles ou de tuiles).</p>
<p>Notre carte sera découpée en lignes et colonnes, ce qui nous permettra plus facilement de gérer un buffer de cases. </p>
<p>Chaque case dispose d&rsquo;une image qui lui est propre, cette image peut être NULL. Si ce champ est NULL nous utiliserons l&rsquo;image par défaut du type de terrain (type_terrain.type_img)</p>
<p>Pour le principe ajoutons une table joueur minimaliste. Elle ne retient qu&rsquo;une chose : la position du joueur.</p>
<p>Dans la réalité le schéma de votre DB sera nettement plus complexe, on est tous d&rsquo;accord, mais ça nous suffira pour l&rsquo;instant.</p>
<p>Ecrire des queries pour cette structure est très simple : prenons un exemple : comment trouver le buffer de gauche de notre carte ?</p>
<p><code><br />
SELECT C.case_id, C.type_terrain_id, C.case_ligne, C.case_col, C.case_img, T.type_img<br />
FROM 	type_terrain T, cases C<br />
WHERE T.type_terrain_id=C.type_terrain_id<br />
AND C.case_col = 4<br />
AND C.case_ligne BETWEEN 11 AND 15<br />
</code></p>
<p>Ce select renvoie donc le buffer gauche de notre carte. C&rsquo;est à dire une colonne composée de 5 cases. Pour obtenir les autres buffers nous jouerons sur les deux dernières conditions, faisant varier soit les colonnes soit les lignes. Par exemple, pour trouver le buffer du bas</p>
<p><code><br />
AND C.case_col BETWEEN 5 AND 9<br />
AND C.case_ligne = 16<br />
</code></p>
<p><strong> </p>
<h2>Structure du code</h2>
<p> </strong><br />
Nous allons essayer de faire aussi générique que possible. De quoi avons-nous besoin ? Tout d&rsquo;abord d&rsquo;un script capable de nous renvoyer le XML proprement dit. Nous appellerons ce script un module. </p>
<p>Notre structure, en sus de la carte, devra être capable d&rsquo;accueillir bien d&rsquo;autres modules, pour à peu près n&rsquo;importe quoi. Puisque nous risquons d&rsquo;avoir un nombre de modules importants il ne serait pas opportun de lier chaque appel Ajax à un module en particulier, ça nous obligerait à connaître à chaque fois l&rsquo;URL de ce module, ce qui peut être contraignant (et ennuyeux niveau sécurité).</p>
<p>Pour éviter de coupler module et code côté client nous allons définir un point d&rsquo;entrée. Ce script particulier sera chargé de dispatcher l&rsquo;appel Ajax vers le module ad-hoc puis d&rsquo;en renvoyer le code XML. Avantage supplémentaire, nous pourrons valider ce que le client nous envoi directement au niveau du point d&rsquo;entrée.</p>
<p>Le code en dessous vous donne un aperçu de l&rsquo;implémentation du point d&rsquo;entrée et d&rsquo;un module.</p>
<h2>
<h2>Le point d&rsquo;entrée</h2>
</h2>
<p><code><br />
<?
//A ne pas oublier si vous utilisez des sessions
session_start();


/***********************************
      	GLOBAL
***********************************/
//Contient les infos génériques (passwords, etc)


   include_once("../globals.php");


/***********************************
      	DATABASE
************************************/
/** Fonctions d'accès à la DB. Notez qu'il peut être utile de les découper aussi en fonction des modules, ce qui évite d'inclure des centaines de lignes inutiles.**/


   require_once("../includes/database/db_interface.php");


/***********************************
      	DEFINITION MODULES
************************************/
/**
Contient la liste des modules devant être supportés. Il est plus simple de rassembler tous les requires des modules dans un seul fichier


Pour plus d'efficacité on peut également faire le require du module nécéssaire juste avant d'apeller son code.
**/


   require_once("module_definition.php");


/***********************************
      	OPERATIONS
************************************/


//! Fermer la connexion  *AVANT* de renvoyer le code XML.
   db_open_connection();


   $str_xml ="";


      if (!isset($_REQUEST['action']) )
      <em><br />
      	die "action non définie!";<br />
      	/**<br />
      		Erreur mal gérée! Idéalement il faut logger les détails de la requête.<br />
      		Il peut également être intéressant de renvoyer un XML contenant les détails de l'erreur<br />
      		Ca simplifiera d'autant le debugging.<br />
      	**/<br />
      </em><br />
      else<br />
      <em><br />
      	switch($_REQUEST['action'])<br />
      	<em></p>
<p>      		/** SUPPORT MODULE CARTE **/<br />
      		case $_REQUEST['action']=='map':<br />
      			$str_xml .= get_default_view(/*params*/);<br />
      			if ($SYS_DEBUG==1) raw_log(1, "list_mail ok");<br />
      			break;</p>
<p>      		// Pour créer un nouveau mode il suffira d'ajouter un case ici</p>
<p>      		/** AUTRE / DEFAULT **/<br />
      		default:<br />
      			$str_xml .= "DEFAULT";<br />
      			if ($SYS_DEBUG==1) detailled_log(1, "action vide");<br />
      	</em></p>
<p>      </em>	  	   </p>
<p>	  //Fin de la génération du XML --> fermeture de la connexion devenue inutile<br />
	  db_close_connection();</p>
<p>	  // Renvoie le code XML généré<br />
	  echo $str_xml;<br />
?><br />
</code></p>
<h2>
<h2>Un module</h2>
</h2>
<p> (ici la carte, donc)<br />
<code><br />
<?


/****************************
/*		DEFAULT				*
/***************************/


//fonction appelée par le mode lié à ce module, c'est à daire par le site "standard", pas par un appel AJAX.
//Elle affiche la version de base du module


function map_mod_default()
<em><br />
//Cette fonction renvoie du HTML, pas du XML, ce n'est pas un appel AJAX</p>
<p>  $str_html="";</p>
<p>  //scripts necessaires à l'application --> AJAX<br />
  $str_html .= init_map_script();<br />
  $str_html .= /* rendu de base */;</p>
<p>  return $str_html;</p>
<p></em>	 </p>
<p>/****************************<br />
/*		 Script JS			*<br />
/***************************/</p>
<p>function init_map_script()<br />
<em><br />
    /**<br />
    N'importe quel script dont vous pourriez avoir besoin est à ajouter ici.<br />
    Notez que dans le cas des scripts génériques utilisés régulièrements il sera plus sage de les<br />
    inclure dans votre index.php une fois pour toute.<br />
    **/<br />
    return "";<br />
</em></p>
<p>/****************************<br />
/*		Actions				*<br />
/****************************/<br />
function get_default_view()<br />
<em><br />
	$str_xml = ... /* Renvoie vos infos sous forme de XML */</p>
<p>	/**<br />
		C'est ici qu'il faudra insérer l'accès à la DB et la création du XML,<br />
		autrement dit c'est le coeur de votre moteur de carte, pas niveau rendu,<br />
		mais bien niveau data<br />
	**/</p>
<p>	return $str_xml<br />
</em></p>
<p>?><br />
</code></p>
<h2> <strong>Côté client</strong> </h2>
<p>Mais qu&rsquo;en est-il côté client ? Très simple : du javascript à foison ! Dans le chapitre précédent nous avions un simple « Moteur javascript », et bien c&rsquo;est lui qui va se charger de tout :</p>
<ol>
<li>Des appels pour récupérer le code XML</li>
<li>Des buffers</li>
<li>De l&rsquo;affichage des informations recueillies</li>
</ol>
<p>La solution la plus propre pour gérer l&rsquo;affichage est d&rsquo;utiliser des feuilles de style XSL. Mais ce n&rsquo;est pas forcément la solution la plus simple. XSL fait appel à des technologies telles que XPath, qui ne sont pas toujours accessibles aux moins expérimentés.</p>
<p>En pseudo-code, voilà ce que donne notre moteur JS :<br />
<code><br />
Vérifier si le buffer contient ce dont on a besoin<br />
Si il ne le contient pas<br />
Recupérer le handler (l'objet XMLHttpRequest)<br />
Construire la requête en fonction de ce qu'on désire obtenir<br />
Envoyer la requête et récupérer la réponse</p>
<p>Tant que la réponse n'est pas reçue entièrement on attend cette réponse</p>
<p>Si la réponse qui est reçue est une erreur (ex: une page 404) on gère l'erreur<br />
Sinon on met à jour le buffer</p>
<p>On sélectionne le nœud que l'on veut afficher</p>
<p>On récupère la feuille de style XSL</p>
<p>Si on a à la fois les données et la feuille de style on effectue le rendu<br />
Sinon on gère une erreur</p>
<p>On identifie la cible dans laquelle il faut insérer le contenu (avec son attribut ID)<br />
On insère le code HTML obtenu grâce à la transformation XSL<br />
</code></p>
<p>Il existe des tas de très bon tutoriaux pour découvrir quelles sont les fonctions qui vont bien en JavaScript. Allez jeter un œil <a href="http://web.developpez.com/tutoriel/javascript/#ajax">du côté de developpez.com</a> par exemple pour un très bon papier dessus.</p>
<p><strong> </p>
<h2>Conclusions</h2>
<p> </strong><br />
Avec ce que nous avons vu ici vous devriez être capable de construire une structure simple de gestion d&rsquo;appels Ajax. Pour augmenter les possibilités de cette structure il ne vous reste qu&rsquo;à développer de nouveaux modules, à choisir l&rsquo;action qui y est liée (mod) et à effectuer l&rsquo;appel javascript qui va bien.</p>
<p>Dans le cas qui nous intéresse, une carte, il est clair que nous gagnons en ergonomie ce qui nous perdons en complexité. Le joueur ne doit plus reloader la page à chaque mouvement de son personnage, mais seulement quand le besoin d&rsquo;un scroll se fait sentir. </p>
<p>Implémenter le support du scrolling manuel (le joueur qui déplace la vue) est également un jeu d&rsquo;enfant : c&rsquo;est déjà supporté.</p>
<p>Soyons clair, la méthode décrite n&rsquo;est pas parfaite et peut-être améliorée. Le code présenté n&rsquo;est pas orienté objet, ce qui est très dommage. Point de vue performance, le switch sur l&rsquo;action n&rsquo;est sûrement pas la panacée. Avec de nombreuses actions différentes vous aurez vite un code horrible. Mais cet exemple a le mérite de montrer qu&rsquo;il n&rsquo;est pas forcément si complexe de concevoir « from scratch » le support d&rsquo;une carte en Ajax.</p>
<p>Dans le prochain épisode nous verrons qu&rsquo;il existe aussi des frameworks qui supportent déjà toutes ces fonctions, vous évitant donc de réinventer la roue à chaque fois. Stay tuned, et rendez-vous dans un petit mois!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>La sécurité dans un jeu PHP/MySQL</title>
		<link>https://www.magazine-jeux.com/la-securite-dans-un-jeu-php-mysql/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Fri, 15 Sep 2006 15:11:46 +0000</pubDate>
				<category><![CDATA[Intégration]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/la-securite-dans-un-jeu-php-mysql/</guid>

					<description><![CDATA[La sécurité dans un jeu en PHP/MySQL est un élément primordial. Si votre site n&#8217;est pas sécurisé, c&#8217;est la porte ouverte à toutes sortes de tricheries de la part de joueurs moins honnêtes que les autres&#8230; Et qui dit triche, dit plaisir de jeu perdu pour les victimes ! Il est également primordial de penser [&#8230;]]]></description>
										<content:encoded><![CDATA[<p class="post_excerpt">La <strong>sécurité</strong> dans un jeu en PHP/MySQL est un élément <strong>primordial</strong>. Si votre site n&rsquo;est pas sécurisé, c&rsquo;est la porte ouverte à toutes sortes de <strong>tricheries</strong> de la part de joueurs moins honnêtes que les autres&#8230; Et qui dit triche, dit plaisir de jeu perdu pour les victimes !<br />
Il est également primordial de penser à la sécurité avant même de commencer à programmer. Plus tard, vous aurez bien d&rsquo;autres préoccupations, et il risque d&rsquo;être difficile de revenir en arrière. Par contre, quelques <strong>bonnes pratiques</strong> respectées tout au long du développement vous éviterons bien des soucis par la suite !</p>
<p>Il n&rsquo;est pas question dans cet article de faire l&rsquo;inventaire de toutes les failles possibles de sécurité pour un site en PHP. Je conseille à ce sujet le très bon <a href="http://phpsec.org/projects/guide/fr/">Guide Sécurité PHP du PHP Security Consortium</a>, après avoir bien sûr parcouru la <a href="http://www.php.net/manual/fr/security.php">rubrique sécurité du manuel PHP</a>.<br />
Cet article a pour but de vous donner les <strong>bases</strong> qui vous permettront de développer l&rsquo;esprit tranquille, le tout illustré d&rsquo;exemples concrets applicables aux jeux web.</p>
<p><span id="more-138"></span></p>
<h2>Configuration et utilisation des variables</h2>
<p>La configuration de php, stockée dans le <em>php.ini</em> est importante puisqu&rsquo;elle conditionne le comportement des variables prédéfinies. Utilisez une configuration par défaut, elle est le fruit de l&rsquo;expérience de la communauté PHP. Evitez d&rsquo;utiliser des fonctionnalités spécifiques, qui pourraient ne pas être disponibles sur n&rsquo;importe quel serveur ! On peut citer <em>register_globals</em>, <em>magic_quotes_gpc</em>, <em>short_open_tags</em>, &#8230;<br />
Et bien sûr, si vous êtes plusieurs à développer, utilisez tous la même configuration.</p>
<p>Voici les points vraiment très importants :</p>
<ul>
<li> <em>register_globals</em> doit être positionné à <em>Off</em>. Lorsque vous utilisez des variables de session, utilisez toujours $_SESSION, idem pour les cookies, variables passées en GET et POST :  $_COOKIE, $_GET et $_POST.</li>
</ul>
<ul>
<li> Positionnez <em>error_reporting</em> à <em>E_ALL</em>, afin d&rsquo;afficher toutes les erreurs, warnings et notice. Un bon moyen est d&rsquo;appeler <em>error_reporting(E_ALL)</em> au début de chaque script. Si vous avez le moindre avertissement, c&rsquo;est potentiellement une faille de sécurité !</li>
</ul>
<ul>
<li> Evitez d&rsquo;utiliser le <em>include($page)</em>, même si vous contrôlez avec une liste de pages autorisées.</li>
</ul>
<ul>
<li> Si vous avez un espace d&rsquo;administration, contrôlez son accès avec un mot de passe (.htaccess)</li>
</ul>
<ul>
<li> Si vous offrez la possibilité d&rsquo;uploader un fichier, contrôlez son contenu, cela pourrait être&#8230; un script php, qui pourrait être exécuté par le serveur !</li>
</ul>
<ul>
<li> De manière générale, évitez de re-développer des fonctionnalités déjà existantes, comme la gestion des sessions. Sauf si vous êtes un véritable expert, il y a de fortes chances pour que vous laissiez passer certains aspects importants (la sécurité en est un), et cela pour un gain pas forcément réel.</li>
</ul>
<ul>
<li> Sur la même note, si vous utilisez des outils externes (forum phpBB, punBB, &#8230;) assurez-vous de le maintenir à jour régulièrement afin de combler les failles de sécurité. Si vous décidez de gérer vous-même votre hébergement (serveur dédié), sachez que c&rsquo;est un métier à part entière, et un métier complexe. Là encore, agissez prudemment, n&rsquo;utilisez que des outils stables et sécurisés, et entourez-vous de personnes compétentes sur le sujet.</li>
</ul>
<ul>
<li> Pour éviter les injections SQL, assurez-vous de toujours échapper les variables utilisées dans les requêtes, surtout si elles proviennent de l&rsquo;utilisateur. La fonction addslashes est généralement utilisée, mais il est préférable d&rsquo;utiliser celle correspondant à votre base de données : <em>mysql_real_escape_string</em> pour MySQL (qui échappe en plus le caractère 0)<br />
Attention aux <em>magic quotes</em> : les variables peuvent être déjà échappées ou non selon la configuration php.<br />
Le manuel PHP fournit une <a href="http://fr2.php.net/manual/fr/function.mysql-real-escape-string.php)">solution sécurisée</a> les prenant en compte :<br />
<code><br />
<?php
// Protège la variable
function quote_smart($value)
<em><br />
   // Stripslashes<br />
   if (get_magic_quotes_gpc()) <em><br />
     $value = stripslashes($value);<br />
   </em><br />
   // Protection si ce n'est pas une valeur numérique ou une chaîne numérique<br />
   if (!is_numeric($value)) <em><br />
     $value = "'" . mysql_real_escape_string($value) . "'";<br />
   </em><br />
   return $value;<br />
</em></li>
</ul>
<p>// Connexion<br />
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')<br />
OR die(mysql_error());</p>
<p>// Fabrication d'une requête sécurisée<br />
$query = sprintf("SELECT * FROM users WHERE user=%s AND password=%s",<br />
quote_smart($_POST['username']),<br />
quote_smart($_POST['password']));</p>
<p>mysql_query($query);<br />
?><br />
</code></p>
<p>Il est généralement recommandé de désactiver les <em>magic quotes</em>, et de systématiquement<br />
échapper les variables utilisées dans une requête SQL (cela simplifie également<br />
leur sortie sur la page html). Pour désactiver les magic quotes quelle que soit<br />
la configuration, on peut placer ce bout de code au début de chaque page :</p>
<p><code><br />
set_magic_quotes_runtime(FALSE);<br />
if(get_magic_quotes_gpc())<em><br />
	function remove_magic_quotes(&$var) <em><br />
		if(is_array($var))<em><br />
			array_walk($var, "remove_magic_quotes");<br />
		</em> else if (is_string($var)) <em><br />
			$var=stripslashes($var);<br />
		</em><br />
	</em></p>
<p>	remove_magic_quotes($_POST);<br />
	remove_magic_quotes($_GET);<br />
	remove_magic_quotes($_REQUEST);<br />
	remove_magic_quotes($_COOKIE);<br />
</em><br />
</code></p>
<p>Pour éviter les problèmes d&rsquo;injection SQL, on peut aussi se tourner vers les requêtes préparées, supportées entre autres dans la nouvelle <a href="http://www.php.net/manual/fr/ref.pdo.php">extension PDO</a> inclue dans PHP 5.1.</p>
<h2>Contrôle des données fournies par l&rsquo;utilisateur</h2>
<p>Si vous n&rsquo;en avez jamais entendu parler, c&rsquo;est que vous n&rsquo;avez lu aucun article sur la sécurité.<br />
Les données fournies en provenance de l&rsquo;utilisateur, qu&rsquo;elle proviennent d&rsquo;un formulaire de saisie (variable $_POST), ou qu&rsquo;elles soient passées en paramètre de la requête HTTP ($_GET), peuvent aisément être falsifiées. Il faut donc impérativement contrôler TOUTE valeur que vous récupérez du navigateur web, et vérifier que ces valeurs sont bien fournies !</p>
<p>Une bonne pratique est de lister, pour chaque action, les variables nécessaires, ainsi que des contraintes sur leur contenu (ceux qui ont suivi des cours se remémoreront la notion de « pré-condition »). Si une valeur est facultative pour l&rsquo;utilisateur, mais que vous en avez besoin pour un traitement, pensez à l&rsquo;initialiser avec une valeur par défaut.</p>
<p>Exemple pour l&rsquo;action « inscription d&rsquo;un joueur » :</p>
<p>$_POST[&lsquo;nom_perso&rsquo;] : texte, min 1 caractère, max 100 caractères, obligatoire<br />
$_POST[&lsquo;description&rsquo;] : texte, min 0 caractère, max 1000 caractères, obligatoire<br />
$_POST[&lsquo;force&rsquo;] : nombre, min 1, max 20, obligatoire<br />
$_POST[&lsquo;endurance&rsquo;] : nombre, min 1, max 20, obligatoire<br />
force + endurance = 20</p>
<p>Toutes ces contraintes devront être vérifiées avant de pouvoir effectuer l&rsquo;inscription. Si une contrainte n&rsquo;est pas respectée, à vous de décider de renvoyer une erreur à l&rsquo;utilisateur (« Nom du perso obligatoire ») ou bien de considérer cela comme une tentative de tricherie ! S&rsquo;il n&rsquo;est pas possible en navigant normalement de fournir une certaine valeur, alors c&rsquo;est très probablement une tentative de triche. Exemple, vous avez sur une page 3 liens :</p>
<ul>
<li> avancer.php?direction=1</li>
<li> avancer.php?direction=2</li>
<li> avancer.php?direction=3</li>
</ul>
<p>Il n&rsquo;y a que 3 directions possibles, et il n&rsquo;existe nulle part sur le site de lien avec une direction différente de 1, 2 ou 3. Si l&rsquo;on vous fournit une valeur différente, alors c&rsquo;est que l&rsquo;utilisateur a modifié le lien manuellement !</p>
<p>Les contraintes listées précedemment pourraient se traduire ainsi :</p>
<p><code><br />
function valider_inscription($variables) <em><br />
	if (!isset($variables['nom_perso'])) return array('TRICHE', 'nom_perso non défini');<br />
	if (!is_string($variables['nom_perso'])) return array('TRICHE', 'nom_perso non textuel');<br />
	if (strlen($variables['nom_perso'])<1 || strlen($variables['nom_perso'])>100) return array('VALID', 'Le nom du perso doit comprendre entre 1 et 100 caractères');</p>
<p>	if (!isset($variables['description'])) return array('TRICHE', 'description non défini');<br />
	if (!is_string($variables[' description '])) return array('TRICHE', ' description non textuel');<br />
	if (strlen($variables[' description '])>1000) return array('VALID', 'La description du perso ne doit pas excéder 1000 caractères');</p>
<p>	if (!isset($variables['force'])) return array('TRICHE', 'force non défini');<br />
if (!is_numeric(($variables['force'])) return array('VALID', 'La force doit être un nombre');<br />
if ($variables['force']<1 || $variables['force']>20) return array('VALID', 'La force doit être comprise entre 1 et 20');</p>
<p>	if (!isset($variables['endurance'])) return array('TRICHE', 'endurance non défini');<br />
if (!is_numeric(($variables['endurance'])) return array('VALID', 'L'endurance doit être un nombre');<br />
if ($variables['endurance']<1 || $variables['endurance']>20) return array('VALID', 'L'endurance doit être comprise entre 1 et 20');</p>
<p>if ($variables['force']+$variables['endurance']!=20) return array('VALID', 'Le cumul de la force et de l'endurance doit valoir 20');</p>
<p>return array('OK', '');<br />
</em><br />
</code></p>
<p>Cette fonction pourra être utilisée ainsi :</p>
<p><code><br />
list($erreur, $message)=valider_inscription($_POST);<br />
if ($erreur=="TRICHE") <em><br />
	//selon votre politique, logger la tentative, afficher un message à l'utilisateur, etc<br />
</em> else if ($erreur=="VALID") <em><br />
	//Renvoyer le formulaire avec le message d'erreur $message à l'utilisateur<br />
</em> else <em><br />
	//OK, effectuer l'inscription en utilisant $_POST['nom_perso'], $_POST['force'], etc<br />
</em><br />
</code></p>
<p>A propos de la politique concernant la triche, il est utile d&rsquo;enregistrer quelque part toutes les tentatives de triche. Lorsqu&rsquo;une entrée ne correspond pas à ce qu&rsquo;elle devrait être, on peut enregistrer cela sur le disque, avec la requête lancée, la page appelée, l&rsquo;IP, ainsi que toutes les variables de session (numéro de compte). Ce fichier peut même entre envoyé à un administrateur par mail régulièrement. Cela permet de voir immédiatement une tentative de piratage, son auteur, et de prendre immédiatement les mesures qui s&rsquo;imposent.<br />
On peut faire la même chose pour les requêtes SQL ayant échoué. A chaque requête lancée, on va vérifier que l&rsquo;exécution s&rsquo;est déroulée correctement (mysql_query() renvoit false en cas d&rsquo;échec). Si ce n&rsquo;est pas le cas, alors on va envoyer un mail à l&rsquo;administrateur avec toutes les infos citées précédemment. En plus de permettre de contrôler que toutes les requêtes sont bonnes, cela permet quelquefois de détecter une tentative de hack par injection SQL.</p>
<p>Voici deux exemples qui vous convaincront de la nécessité de bien contrôler ses variables.</p>
<p>1) Imaginons que vous ayez une fonctionnalité de don d&rsquo;argent à un autre personnage.<br />
L&rsquo;utilisateur choisit un destinataire, puis saisit la somme qu&rsquo;il veut lui céder dans un formulaire. Imaginez d&rsquo;abord que vous contrôliez bien que la somme soit un nombre, mais que vous ne testiez pas les bornes. Votre traitement ressemble à :</p>
<p><code><br />
UPDATE Perso set argent=argent-somme where id_perso=$donneur<br />
UPDATE Perso set argent=argent+somme where id_perso=$destinataire<br />
</code></p>
<p>Imaginez qu&rsquo;un joueur malin saisisse une somme négative&#8230; cela aura pour effet de retirer de l&rsquo;argent au destinataire, et d&rsquo;en ajouter au donneur !</p>
<p>2) Votre jeu se déroule sur une carte, chaque personnage se trouvant une case. Votre script d&rsquo;attaque, attaquer.php, prend en paramètre un nombre correspondant au personnage visé, id_perso_vise. Lorsque l&rsquo;utilisateur choisit d&rsquo;attaquer, le site lui présente une liste de personnages qui sont à sa portée, c&rsquo;est-à-dire à moins de 3 cases, accompagné d&rsquo;un lien attaquer.php?id_perso_vise=xxxx<br />
Si vous ne contrôlez pas que le personnage visé est bien à moins de 3 cases dans le script attaquer.php, alors un joueur pourrait modifier l&rsquo;url, et attaquer n&rsquo;importe quel perso où qu&rsquo;il soit !</p>
<p>Il existe des cas très subtils, par exemple prenons si une variable $force contient la chaîne « 15+250 » (saisie par l&rsquo;utilisateur) :</p>
<ul>
<li> Dans un calcul PHP, $force sera converti en nombre, la conversion récupère tous les chiffres jusqu&rsquo;au premier caractère non numérique è if ($force<20) renverra true</li>
<li> Si alors vous faites une requête : UPDATE Perso set force=$force, alors la valeur 265 sera insérée dans la table !<br />
è il faut vérifier l&rsquo;utilisateur a bien saisi un nombre (<em>is_numeric</em>). Il est aussi possible de systématiquement convertir le nombre en entier avec <em>intval()</em> par exemple, mais cela ne permet pas de détecter la tentative de triche.</li>
</ul>
<h2>Concurrence d&rsquo;accès aux données</h2>
<p>Maintenant que les données en entrées des traitement sont bien contrôlées, nous allons nous intéresser aux traitements aux-mêmes.</p>
<p>Reprenons notre exemple de don d&rsquo;argent. Cette action est effectuée par un script donner.php qui prend en paramètre le destinataire du don, ainsi que la somme &#8211; entier positif, qui doit être inférieur au montant disponible sur le compte bancaire du joueur. Le numéro du donateur (joueur connecté) est bien entendu conservé dans une variable de session.</p>
<p>La structure générale du script est la suivante :</p>
<p><code><br />
//Validation : on vérifie que le joueur possède assez d'argent<br />
$r=mysql_query("SELECT argent from Perso where id_perso=".$_SESSION["id_perso']);<br />
list($argent_dispo)=mysql_fetch_row($r);<br />
if ($somme > $argent_dispo) <em><br />
	// renvoyer un message d'erreur à l'utilisateur indiquant qu'il n'a pas assez d'argent<br />
</em><br />
...</p>
<p>//Traitement : on transfert l'argent d'un perso à l'autre<br />
mysql_query("UPDATE Perso set argent=argent-".$_POST['somme']." where id_perso=".$_SESSION['id_perso']);<br />
mysql_query("UPDATE Perso set argent=argent+".$_POST['somme']." where id_perso=$destinataire");<br />
</code></p>
<p>Tout va bien jusqu&rsquo;à présent, notre code fonctionne. Maintenant, imaginons qu&rsquo;il survienne un délai notable entre la validation et le traitement. Les serveurs web gèrent plusieurs processus ou requêtes simultanément. Un script peut donc être interrompu provisoirement, avant de reprendre son exécution plus tard. Vous pourriez également avoir à effectuer des calculs longs et compliquées entre la validation et la mise à jour de la base de données.</p>
<p>Si un joueur envoie alors plusieurs fois de suite la même requête http (avec le même contenu), ce qui peut aisément être effectué en cliquant plusieurs fois de suite, très rapidement, sur le bouton de validation, alors nous avons plusieurs requêtes qui arrivent simultanément au serveur web. Notre serveur web sait exécuter plusieurs scripts (processus) simultanément, et leur exécution peut être effectuée simultanément &#8211; surtout si par exemple, le serveur dispose de plusieurs CPU ; mais aussi parce que le système peut décider de suspendre un processus pour en démarrer un autre.</p>
<p>Considérons donc le cas où 2 appels identiques (mêmes paramètres) à donner.php sont arrivés au serveur web simultanément. Nous allons les appeler A et B. Le donateur (joueur n°42) dispose de 50 pièces sur son compte. Il souhaite en donner 30 au joueur n°123, qui en possède initialement 20.<br />
Le séquencement des opérations peut être le suivant :</p>
<p>Le système démarre l&rsquo;appel A au script<br />
A effectue la phase de validation è ok, le joueur veut donner 30 et dispose de 50<br />
Le système interromps l&rsquo;appel A et démarre l&rsquo;appel B au script.<br />
B effectue la phase de validation è ok, le joueur veut donner 30 et dispose de 50<br />
B effectue le traitement, il retire 30 à A et les verse à B è joueur 42 dispose de 20, joueur 123 dispose de 50<br />
B se termine<br />
A reprend son exécution<br />
B effectue le traitement, il retire 30 à A et les verse à B è joueur 42 dispose de -10, joueur 123 dispose de 80 !</p>
<p>C&rsquo;est ce genre de problème qui a conduit des informaticiens, il y a quelques dizaines d&rsquo;années, à inventer les bases de données et la notion de transaction. Je ne vais pas rentrer dans les détails ici (internet fournit une large documentation sur le sujet), sachez juste que les transactions ne sont pas supportées par le format de table par défaut de MySQL, MyISAM, mais qu&rsquo;elles le sont avec InnoDB.<br />
Les transactions permettent de verrouiller les données manipulées, et de s&rsquo;assurer qu&rsquo;elles n&rsquo;ont pas été modifiées entre le début et la fin de la transaction. Si nous avions encapsulé toutes nos requêtes SQL dans une même transaction, nous aurions eu l&rsquo;assurance que le 2ème appel n&rsquo;aurait pas abouti.</p>
<p>Outre ce cas d&rsquo;école, il existe de nombreuses situations où vous pourriez avoir des problèmes.</p>
<p>Prenons l&rsquo;exemple d&rsquo;une gestion des points d&rsquo;action (PA). Le joueur gagne 1 PA par heure. Pour gérer ce système, on stocke la date/heure (timestamp) de dernière connexion pour chaque joueur. Lorsqu&rsquo;il se connecte, on calcule le temps écoulé depuis le dernier gain de PA, et on lui redonne autant de PA que d&rsquo;heures écoulées. Notre script pourrait ressembler à :</p>
<p><code><br />
$r=mysql_query("SELECT timestamp_gain from Perso where id_perso=".$_SESSION['id_perso']);<br />
list($timestamp_gain)=mysql_fetch_row($r));<br />
if (time()-$timestamp_gain>3600) <em> //si plus de 3600 secondes (1h) se sont écoulées depuis la dernière connexion<br />
	$gain = floor((time()-$timestamp_gain)/3600); //calculer le nombre d'heures écoulées<br />
	mysql_query("UPDATE Perso set PA=PA+$gain, timestamp_gain= timestamp_gain+3600*$gain_timestamp where id_perso=$id_perso");<br />
</em><br />
</code></p>
<p>Maintenant, imaginez si deux appels à ce script arrivent simultanément au serveur web&#8230; Quel est le risque ? Que le joueur perçoive 2 fois trop de PA !</p>
<p>Pour éviter cette situation délicate, il n&rsquo;existe guère de salut en dehors des transactions. Vous pourriez effectuer toute l&rsquo;opération en une seule requête (la requête est compliquée mais c&rsquo;est parfaitement faisable), mais ce n&rsquo;est valable que pour ce cas précis. Vous pourriez aussi gérer vous-même une section critique à l&rsquo;aide de sémaphores UNIX, de la fonction <em>flock()</em> ou la fonction <em>GET_LOCK()</em> de MySQL. Oubliez tout de suite, c&rsquo;est une très mauvaise idée. D&rsquo;une part, parce que (hormis avec <em>flock</em>), vous devenez dépendant de fonctions qui ne seront pas forcément disponibles sur toute plate-forme, mais surtout parce que vous êtes en train de réinventer la roue ! La gestion des transactions est un problème compliqué, fiez-vous plutôt aux implémentations fiables et éprouvées qui existent déjà, vous gagnerez du temps et de la sueur ! 🙂</p>
<p>Pour clôre ce chapitre (et vous faire des frayeurs :p), appliquez le comportement que l&rsquo;on vient de voir à une action d&rsquo;attaque d&rsquo;un adversaire : le joueur inflige des dommages à un adversaire. Si le nombre de Points de Vie de l&rsquo;adversaire bascule en dessous de zéro, l&rsquo;adversaire meurt. Plusieurs joueurs peuvent même attaquer un même adversaire simultanément. Que peut-il se passer en cas d&rsquo;attaques simultanées sur un même perso ?</p>
<h2>Multi-comptes</h2>
<p>Vous avez maintenant toutes les cartes en main pour sécuriser votre site. Il reste cependant un problème potentiel : le multi-comptes. En effet, dans un jeu traditionnel, comme un jeu de carte, une personne ne peut jouer qu&rsquo;un seul joueur. Dans un jeu sur internet, il en va autrement. Une même personne peut très facilement se créer plusieurs comptes sur votre jeu !<br />
Selon le type de jeu, cela aura un impact plus ou moins important. Dans un jeu de guerre, où le nombre de joueurs influence les chances de victoire, cela peut être catastrophique. A moindre échelle, un autre compte peut être utiliser pour affaiblir l&rsquo;ennemi avant de l&rsquo;attaquer avec son vrai personnage, pour espionner ou même infiltrer son ennemi.</p>
<p>Il faut savoir qu&rsquo;il est presque impossible, au niveau technique, d&#8217;empêcher le multi-comptes. Les principales techniques de détection, basées sur l&rsquo;e-mail, l&rsquo;adresse IP ou les cookies, peuvent être contournées par une personne suffisamment expérimentée. De même, il peut arriver que plusieurs personnes jouent réellement à partir du même poste (familles, écoles, cyber-cafés, &#8230;). Aucune technique de détection n&rsquo;est donc véritablement fiable.</p>
<p>En conséquence, il est important d&rsquo;évaluer les impacts et les risques provoqués par une personne gérant plusieurs comptes sur votre jeu. De là, vous devrez en déduire une politique à adapter face à ce problème.<br />
Selon le type et les règles du jeu, cela pourrait être :</p>
<ul>
<li> autorisation explicite de faire du multi-comptes (aucun d&rsquo;impact)</li>
<li> autorisation implicite (très faible impact)</li>
<li> interdiction explicite sans répression.</li>
<li> interdiction explicite avec répression, ce qui suppose la mise en place d&rsquo;un système de détection. On peut joueur sur le niveau de tolérance, sur l&rsquo;éducation plutôt que la répression, s&rsquo;aider de la dénonciation, etc. Le système de détection n&rsquo;étant pas fiable, certains jeux mettent même en place des équipes chargées d&rsquo;enquêter et juger les cas litigieux.</li>
</ul>
<p>Le meilleur moyen de limiter le multi-comptes reste sans aucun doute de faire en sorte qu&rsquo;il n&rsquo;ait aucun impact sur le jeu&#8230; Ce qui n&rsquo;est pas toujours forcément évident. C&rsquo;est donc un aspect qui devrait être pris en compte dès la conception du jeu !</p>
<p>Pour conclure, retenez surtout qu&rsquo;il n&rsquo;y a pas de solution toute faite. Il faudra la choisir en fonction de votre jeu, de votre public et&#8230; de votre intuition ! 😉</p>
<hr />
<p><em>Je remercie <a href="http://www.jdr-delain.net">Merrick</a>, <a href="http://www.mountyhall.com">VYS</a> et <a href="http://www.nainwak.com">DT</a> pour leurs remarques et relecture de cet article 🙂</em></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Astuces pour une feuille de style pour l&#8217;impression</title>
		<link>https://www.magazine-jeux.com/astuces-pour-une-feuille-de-style-pour-limpression/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Mon, 04 Sep 2006 05:00:00 +0000</pubDate>
				<category><![CDATA[Intégration]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/astuces-pour-une-feuille-de-style-pour-limpression/</guid>

					<description><![CDATA[Les CSS permettent d&#8217;utiliser une feuille de style différente en fonction du média utilisé : écran, imprimante, etc. On peut appliquer à une page web un style spécifique pour l&#8217;impression. Voici quelques astuces et reflexes à avoir au moment de préparer ses pages et ses feuilles de style pour bien profiter des possibilité des styles [&#8230;]]]></description>
										<content:encoded><![CDATA[<p class="post_excerpt">Les CSS permettent d&rsquo;utiliser une feuille de style différente en fonction du média utilisé : écran, imprimante, etc.<br />
On peut appliquer à une page web un style spécifique pour l&rsquo;impression.</p>
<p>Voici quelques astuces et reflexes à avoir au moment de préparer ses pages et ses feuilles de style pour bien profiter des possibilité des styles d&rsquo;impression.</p>
<p><span id="more-129"></span><br />
<strong>L&rsquo;appel à la feuille de style</strong></p>
<p>De la même manière que vous appelez votre ou vos feuilles de style habituellement, vous pouvez ajouter des appels à d&rsquo;autres feuilles en spécifiant bien le média auquel elles se destinent.</p>
<p>Exemple :<br />
<code>
<link rel="stylesheet" type="text/css" href="style-ecran.css" media="screen" />
<link rel="stylesheet" type="text/css" href="style-imprimante.css" media="print" />
</code></p>
<p><strong>La typo</strong></p>
<p>Certaines polices de caractères sont plus agréables à l&rsquo;écran que d&rsquo;autres. De la même manière, une police de caractère peut être désagréable ou moins « jolie » une fois imprimée. On peut donc utiliser la feuille de style d&rsquo;impression pour changer de typo.<br />
Les typo à empâtement, par exemple, sont très fréquentes sur support papier alors qu&rsquo;elles le sont moins sur écran.<br />
Il faut aussi penser à adapter la taille des caractères.</p>
<p>Exemple :<br />
<code>body <em><br />
font: 10pt Times New Roman, serif;<br />
</em></code></p>
<p><strong>Les liens</strong></p>
<p>Les liens de navigation interne n&rsquo;ont plus d&rsquo;intérêt sur une feuille imprimée. On peut donc, dès le début, prévoir une classe spécifique à appliquer sur tous ces liens. Ainsi, on peut faire disparaître, par exemple, le sousligné sur les liens type « &#8230;que vous trouverez sur la page Recherche ».</p>
<p>Les liens externes, eux, sont certainement des liens qui continuent à avoir un sens hors du site.</p>
<p>CSS2 permet d&rsquo;afficher l&rsquo;adresse d&rsquo;un lien après celui-ci. C&rsquo;est très intéressant pour une feuille de style d&rsquo;impression puisque, sur papier, impossible de cliquer. Sans cette possibilité, l&rsquo;information principale du lien (l&rsquo;adresse) est perdue.<br />
Ne soyez pas déçus, néanmoins, si vous imprimez avec Internet Explorer : cette fonction n&rsquo;est pas encore implémentée. Cela ne générera pas d&rsquo;erreur &#8230; mais cela n&rsquo;affichera pas l&rsquo;adresse internet non plus !</p>
<p>Exemple :<br />
<code>a.navigationInterne <em><br />
text-decoration: none;<br />
</em><br />
a:after <em><br />
content: " (" attr(href) ") ";<br />
font-size: 0.9em;<br />
</em><br />
a.navigationInterne:after <em><br />
content: "";<br />
</em></code></p>
<p><strong>Les couleurs et les images de fond</strong></p>
<p>Les couleurs de fond peuvent avoir de gros inconvénients, une fois sur papier.<br />
Elles peuvent, dans certains cas, gêner la lisibilité (plus que sur écran où le contraste n&rsquo;est pas le même).</p>
<p>C&rsquo;est encore plus vrai pour les images de fond qui peuvent être jolies et sans effet gênant sur un écran mais poser des problèmes de contraste à la lecture sur papier.</p>
<p>Couleurs de fond et images de fond peuvent aussi utiliser beaucoup d&rsquo;encre pour un effet graphique mineur. L&rsquo;internaute apprécierait qu&rsquo;un site ait pensé à lui éviter le problème.<br />
Cette fonctionnalité est d&rsquo;ailleurs déjà implémentée par défaut dans certains navigateurs.</p>
<p>Attention, en enlevant les couleurs de fond, à ne pas se retrouver avec un texte illisible. Si un texte jaune, par exemple, était très lisible sur son fond noir, une fois la couleur de fond enlevée, le jaune sur le blanc de la feuille sera très difficile à lire. Il convient d&rsquo;adapter la couleur du texte à tout changement de couleur de fond.</p>
<p>Exemple :<br />
<code>p <em><br />
background: none;<br />
color: #000;<br />
</em></code></p>
<p><strong>Les marges, les espacements</strong></p>
<p>L&rsquo;aération d&rsquo;une page imprimée n&rsquo;est pas la même que pour une page web.<br />
Il sera alors peut-être nécéssaire de revoir les marges, notamment horizontale, sur votre feuille d&rsquo;impression.<br />
Pensez que l&rsquo;internaute sera déçu s&rsquo;il doit imprimer sur 2 pages avec une marge de 4 cm un texte qui tiendrait sur 1 en utilisant simplement toute la largeur de la feuille.</p>
<p>Adaptez les espacements entre les éléments au média. Les titres n&rsquo;ont pas forcément besoin d&rsquo;être autant espacés de leur texte sur une version papier qu&rsquo;à l&rsquo;écran.</p>
<p><strong>Les sauts de page</strong></p>
<p>Rien de plus désagréable pour un internaute qu&rsquo;une page sur laquelle n&rsquo;a été imprimée qu&rsquo;une seule ligne, qu&rsquo;un paragraphe dont les derniers mots sont sur la page suivante, qu&rsquo;un titre qui n&rsquo;est pas immédiatement suivi par, au moins, la première ligne de son paragraphe, ou qu&rsquo;une image dont la légende a été imprimée sur la page suivante, etc.<br />
Les styles d&rsquo;impression permettent de gérer tout ça. Analysez bien vos pages pour voir quels sont les coupures à éviter. Attention à ne pas tomber dans l&rsquo;excès inverse et de faire des coupures partout : l&rsquo;internaute n&rsquo;appréciera pas non plus de devoir imprimer trop de pages.</p>
<p>Les propriétés à utiliser :<br />
&#8211; page-break-before (gestion du saut de page avant l&rsquo;élément)<br />
&#8211; page-break-after  (gestion du saut de page après l&rsquo;élément)<br />
&#8211; orphans (gestion des lignes seules en bas de page)<br />
&#8211; widows (gestion des lignes seules en haut de page)</p>
<p>Exemple :<br />
<code>h1, h2, h3, h4, h5, h6 <em><br />
page-break-after:avoid;<br />
</em><br />
p <em><br />
orphans: 2;<br />
</em></code></p>
<p>Dans l&rsquo;exemple ci-dessus, aucun saut de page ne sera fait après un titre hn et aucun début de paragraphe ne sera imprimé avec moins de deux lignes en bas de page.</p>
<p><strong>Les zones inutiles</strong></p>
<p>Certaines zones très courantes sur un site web sont néanmoins inutiles sur une version imprimée.<br />
C&rsquo;est le cas par exemple du menu. L&rsquo;internaute qui imprime une page n&rsquo;a plus besoin, quand il voudra relire sa page, d&rsquo;avoir dans sa lecture tous les items du menu imprimés.</p>
<p>Un formulaire n&rsquo;a également plus de sens sur une feuille imprimée (sauf, bien-sûr, les formulaires fait pour l&rsquo;impression).<br />
Il n&rsquo;est pas forcément utile que le formulaire soit encore visible sur une page contact, par exemple, qui aurait été imprimée pour garder une trace du numéro de téléphone, de l&rsquo;adresse, etc.</p>
<p>Les liens tels que « Retour à l&rsquo;accueil » peuvent aussi être évité sur une feuille d&rsquo;impression. Il faut identifier ces liens et leur assigner une classe particulière pour pouvoir les retirer lors de l&rsquo;impression.</p>
<p>Exemple :<br />
<code>#menu, #piedDePage, form, #retourHautPage <em><br />
display: none;<br />
</em><br />
.lienNonImprime <em><br />
display: none;<br />
</em><br />
</code></p>
<p><strong>Les informations perdues</strong></p>
<p>En supprimant le menu, en ayant pas la barre d&rsquo;adresse imprimée, certaines informations se retrouvent « hors de la page web ».<br />
L&rsquo;internaute retrouve souvent ce type d&rsquo;information imprimée en en-tête et pied-de-page (avec la date, parfois le titre&#8230;) en fonction de son navigateur et de sa configuration.<br />
Mais le webmaster n&rsquo;a pas la main sur ces informations.</p>
<p>Pour palier à cela, on peut concevoir, sur chacune des pages, une zone qui ne sera pas visible pour les écrans mais qui le sera à l&rsquo;impression.<br />
Cette zone reprendrait des informations telles que le titre du site, son URL, des informations importantes qui pourrait manquer au lecteur de la page imprimée (l&#8217;email du webmaster, par exemple).</p>
<p>Exemple :<br />
Sur la feuille de style pour écran :<br />
<code><br />
#infosImpression <em><br />
display: none;<br />
</em><br />
</code></p>
<p>Sur la feuille de style pour imprimante:<br />
<code><br />
#infosImpression <em><br />
display: block;<br />
</em><br />
</code></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Jouer avec AJAX I/III</title>
		<link>https://www.magazine-jeux.com/jouer-avec-ajax-i-iii/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Mon, 28 Aug 2006 05:02:00 +0000</pubDate>
				<category><![CDATA[Intégration]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/jouer-avec-ajax-i-iii/</guid>

					<description><![CDATA[Le but de cette série de trois articles est de vous permettre d&#8217;introduire une pincée d&#8217;Ajax dans vos jeux. Nous commencerons par voir quels sont les avantages que l&#8217;on peut espérer de cette technique. Nous passerons ensuite en revue une (parmi d&#8217;autres) vision de l&#8217;implémentation d&#8217;Ajax. Enfin, le dernier article se penchera sur les frameworks [&#8230;]]]></description>
										<content:encoded><![CDATA[<p class="post_excerpt">Le but de cette série de trois articles est de vous permettre d&rsquo;introduire une pincée d&rsquo;Ajax dans vos jeux. Nous commencerons par voir quels sont les avantages que l&rsquo;on peut espérer de cette technique. Nous passerons ensuite en revue une (parmi d&rsquo;autres) vision de l&rsquo;implémentation d&rsquo;Ajax. Enfin, le dernier article se penchera sur les frameworks disponibles actuellement sur le net.</p>
<p><span id="more-130"></span></p>
<h2> <strong>Intro</strong> </h2>
<p>Ajax, Web 2.0, la mode est à l&rsquo;asynchrone &#8230; Ajax est-il incontournable ? Est-il véritablement intéressant d&rsquo;utiliser Ajax comme méthode d&rsquo;échange d&rsquo;information dans le cadre du développement d&rsquo;un jeu ?</p>
<h2> <strong>Petits rappels</strong> </h2>
<p>AJAX, pour Asynchronous JavaScript and XML est une technique de développement web qui vise à rendre les pages plus ergonomiques. L&rsquo;objectif est de donner une sensation de réponse instantanée à l&rsquo;utilisateur. Le principe de base est terriblement simple : échanger de faibles quantités d&rsquo;information via un appel asynchrone &#8211; et en général grâce à du code XML, entre le client et le serveur.</p>
<p>Quand on parle d&rsquo;Ajax, on entend trois choses:</p>
<ol>
<li>L&rsquo;utilisation intensive de JavaScript</li>
<li>L&rsquo;utilisation de l&rsquo;objet XMLHttpRequest</li>
<li>L&rsquo;utilisation du langage XML pour le transfert des informations</li>
</ol>
<p>Les puristes ajouteront un soupçon de XSLT pour le rendu, mais ce sont des puristes 😉</p>
<p>Vous l&rsquo;aurez tous compris, Ajax, comme le DHTML, n&rsquo;est pas une technologie en soi, c&rsquo;est le fait d&rsquo;utiliser un groupe de technologie ensemble dans un but précis.</p>
<p>Utiliser Ajax a des avantages : meilleure gestion de la bande passante, interactivité améliorée, hype pour n&rsquo;en citer que quelque uns. Cette technique a aussi des inconvénients : diminution de l&rsquo;accessibilité, mise en péril de l&rsquo;ergonomie à laquelle l&rsquo;utilisateur est habitué, temps de réponses non maîtrisés et principe de l&rsquo;asynchronisme généralement mal assimilés par les programmeurs débutants. </p>
<h2> <strong>Mise en situation</strong> </h2>
<p>Mettons-nous dans la peau d&rsquo;un webmaster lambda. Il développe son premier jeu. Pour simplifier notre propos considérons que son jeu ne compte que trois éléments: des pages plutôt statiques, comme des news, un classement, ou une FAQ. Des pages plutôt dynamiques, comme une carte. Et des pages composées presque exclusivement de formulaires, comme l&rsquo;administration de compte ou le passage d&rsquo;ordres.</p>
<p>Comment juger de l&rsquo;intérêt d&rsquo;une technologie ? En dehors de l&rsquo;aspect subjectif « j&rsquo;implémente parce que j&rsquo;en ai envie » ou « parce que ça m&rsquo;amuse », mesurons le degré d&rsquo;intérêt en fonction de trois critères : l&rsquo;ergonomie, les performances et la simplicité.</p>
<p><strong>Valeur ajoutée à l&rsquo;ergonomie générale</strong><br />
Comment l&rsquo;utilisateur va t-il percevoir ce changement ? Va-t-il avoir la sensation que les « performances » de l&rsquo;interface sont améliorées ? Va-t-il disposer de nouvelles fonctionnalités qui s&rsquo;intègrent harmonieusement avec le reste, avec les fonctions par défaut de son navigateur (par exemple avec le bouton Back) ?</p>
<p>Supposons que notre webmaster n&rsquo;affiche que 5 news. Il pourrait vouloir proposer un lien « Voir les news suivantes », ou un accès aux « archive » avec un calendrier. Sans Ajax le principe est simple, on ajoute un paramètre au script qui affiche la page et on rafraichi la totalité de la page. Avec Ajax on économise l&rsquo;appel à ce script. On ne rafraichi que le contenu que l&rsquo;utilisateur demande : les news. Il reçoit sa réponse plus vite, sans le « clignotement » de la page qui se rafraîchi. Attention néanmoins à ne pas le dérouter. Il faut que le changement sur la page soit clair,  bien visible. </p>
<p>En ce qui concerne la carte, il est nécessaire de supporter un minimum de scrolling. La solution du rafraîchissement de la page à chaque déplacement est possible, bien sur, mais fort limité tout de même. Sans parler que son jeu aura l&rsquo;air à la traîne techniquement. Quand on parle de scrolling on pense forcement à maintenir un (ou des) buffer(s). Techniquement, Ajax est bien adapté à ce type d&rsquo;utilisation. A tout moment il est possible de déclencher le pré chargement d&rsquo;une partie de la carte de façon invisible. Maintenir le buffer devient plus simple. Le XML se prête aussi à ce type d&rsquo;utilisation. Le buffer deviendra un ou plusieurs node(s) dont on assurera le rendu via une fonction JS qui va bien.</p>
<p>Reste les pages dotées de formulaires. Nous étions habitués à devoir les valider d&rsquo;abord côté client, puis à éventuellement demander une correction à l&rsquo;utilisateur. Depuis fort longtemps nous sommes tous habitué à vérifier nos formulaires une seconde fois côté serveur. Ajax apporte ici deux avantages majeurs : </p>
<ol>
<li>la possibilité de proposer des informations contextuelles (pensez au système d&rsquo;auto-completion de gmail par exemple). </li>
<li>la possibilité de valider le formulaire au fur et à mesure, donnant un feedback plus rapide à l&rsquo;utilisateur, évitant ainsi la frustration du formulaire refusé.</li>
</ol>
<p>Bien sur, ça n&rsquo;exclu pas la vérification côté serveur, mais ici l&rsquo;intérêt en terme d&rsquo;ergonomie est énorme, puisque les formulaires sont partout dans nos jeux.</p>
<p><strong>Gain de performances côté serveur</strong><br />
Que va-t-on gagner en définitive ? On peut estimer le gain en fonction du temps d&rsquo;exécution des scripts. De la répartition des requêtes SQL. Du taux d&rsquo;utilisation moyen des ressources. Mais globalement ce qui nous intéresse c&rsquo;est surtout le temps de réponse du site.</p>
<p>Le serveur n&rsquo;a pas besoin de calculer le rendu de toute la page. Il n&rsquo;effectue le plus souvent qu&rsquo;un seul SELECT, pour récupérer les informations demandées par l&rsquo;utilisateur. L&rsquo;économie ici est triple :</p>
<ol>
<li>On économise sur les SELECT qu&rsquo;aurait effectué le serveur si il avait du rafraîchir toute la page, puisque seules les infos demandées sont rafraîchies</li>
<li>On économise la bande passante : pas besoin de renvoyer tout le code HTML, mais seulement un petit bout de code XML.</li>
<li>On économise sur le rendu : raffinement supplémentaire, le rendu peut-être délégué à une fonction JS, autant d&rsquo;économie en temps d&rsquo;exécution PHP.</li>
</ol>
<p>Ceci dit Ajax peut avoir un effet pervers : la multiplication des requêtes. Il est tentant par exemple d&rsquo;effectuer un rafraîchissement automatique par polling. Une aide contextuelle dans un formulaire utilisé pour chaque passage d&rsquo;ordre signifie une requête au serveur web (et potentiellement une requête SQL) à chaque fois. Si la solution peut-être envisageable attention aux ressources qu&rsquo;elle requiert.</p>
<p><strong>Simplicité de mise en œuvre</strong><br />
Est-il aisé d&rsquo;implémenter cette solution ? Est-il plus aisé de continuer à utiliser les techniques habituelles de DHTML ? Pourra-t-on la maintenir facilement ? Pourra-t-on facilement l&rsquo;améliorer ? Considérons pour le moment que notre MJ ne se base pas sur un framework existant.</p>
<p>Soyons réaliste, non Ajax n&rsquo;est pas simple à mettre en œuvre. Il demande une certaine expérience du développement web, sans doute plus que le DHTML à son époque. Il demande également une grande rigueur dans le code. Il est souvent nécessaire de jongler avec les éléments via leur id&rsquo;s, de pouvoir modifier le contenu à la volée, en respectant le look&#038;feel du site, d&rsquo;où le besoin intensif de CSS. Ajax nécessite aussi de bien comprendre le concept d&rsquo;asynchronisme, d&rsquo;en connaître les limites et les risques.</p>
<h2> <strong>Conclusions</strong> </h2>
<p>Nous approfondirons les points abordés dans la suite, mais d&rsquo;ores et déjà une réponse s&rsquo;impose : oui, Ajax peut nous être utile. Le gain d&rsquo;ergonomie peut-être énorme : assistance au remplissage des formulaire, aide contextuelle, réactivité accrue, meilleure gestion des cartes, autant de domaine ou Ajax excelle. La mise en place demande tout de même un minimum de réflexion. Où l&rsquo;appliquer, tout d&rsquo;abord. Quel sera l&rsquo;impact sur les performances du serveur ? Enfin, la conception même du jeu devra évoluer, on pourra dans un premier temps se contenter d&rsquo;intégrer Ajax à l&rsquo;existant, mais pour en tirer vraiment partit il faudra souvent repasser par la case développement. Stay tuned et rendez vous dans le numéro 2&#8230;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Include ou require ?</title>
		<link>https://www.magazine-jeux.com/include-ou-require/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Mon, 14 Aug 2006 05:34:00 +0000</pubDate>
				<category><![CDATA[Programmation]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/include-ou-require/</guid>

					<description><![CDATA[Vous venez de terminer votre site et il est beau, très beau. Il contient à peu près 30 pages et vous en avez sué pour en arriver là. Et puis, un ami passe par là et vous fait remarquer, très gentiment, qu&#8217;il n&#8217;y a pas de « contact » dans le menu de vos pages. Bon, vous [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Vous venez de terminer votre site et il est beau, très beau. Il contient à peu près 30 pages et vous en avez sué pour en arriver là.<br />
<br />Et puis, un ami passe par là et vous fait remarquer, très gentiment, qu&rsquo;il n&rsquo;y a pas de « contact » dans le menu de vos pages.<br />
<br />Bon, vous piquez une crise à l&rsquo;idée de refaire les 30 pages juste parce que vous avez oublié une ligne dans le menu. La crise passée et quelques heures plus tard, ce même ami vous fait remarquer que votre menu n&rsquo;est pas franchement joli et que ce serait mieux de tenter le coup avec une autre couleur&#8230; Et là, il hésite sur la couleur. Mais vous non ! Ce sera ma main dans la figure et puis c&rsquo;est tout !</p>
<p>Bon, avant d&rsquo;en arriver là, il serait bon de mettre en place ces fameux fichiers inclus.<br />
<br />En effet, un site internet est un objet éphémère, qui bouge, qui vie et qui change donc régulièrement. Si vous ne concevez pas vos pages avec un minimum de méthode, vous ne vous en sortirez pas. Alors, avant de lancer votre main sur la figure de votre ami, placez vos menus dans un fichier inclus.</p>
<h2>include</h2>
<p>Cette commande Php est simple à utiliser : </p>
<p><code><br />
<?php
include "mon_fichier.php";
?><br />
</code></p>
<p>Ce morceau de code permet tout simplement de placer (d&rsquo;inclure) le fichier « mon_fichier.php » à la place de cette ligne.<br />
<br />Imaginons que votre menu soit le suivant : </p>
<p><code><br />
Fichier : menu.php</p>
<ul>
<li><a href="index.php">Sommaire</a></li>
<li><a href="forum.php">Forum</a></li>
<ul>
</code></p>
<p>Vous pouvez placer dans le fichier d&rsquo;index  la ligne suivante en lieu et place du menu : </p>
<p><code><br />
<?php
include "menu.php";
?><br />
</code></p>
<p>Et ceci sur toutes les pages de votre site où doit s&rsquo;afficher votre menu.<br />
<br />Maintenant, si votre ami passe par là et vous propose une petite modification, vous pouvez lui dire « ok, dans 10 secondes, elle est faite ». Et vous n&rsquo;avez qu&rsquo;à modifier le fichier « menu.php ».</p>
<h2>Quelques règles tout de même</h2>
<p>Vous pouvez très bien placer du php dans le fichier inclus. Vous pouvez, par exemple, définir que le menu en cours n&rsquo;est pas cliquable.</p>
<p>Il est de bon goût d&rsquo;inclure un fichier de configuration au début de toutes vos pages Php. Ce fichier contient les paramètres de votre site, mais peu aussi contenir des fonctions que vous utilisez régulièrement. Vous pouvez aussi inclure 2 fichiers, l&rsquo;un pour la configuration et l&rsquo;autre pour les fonctions.</p>
<p>N&rsquo;hésitez pas à nommer vos fichiers avec l&rsquo;extension « .inc.php » afin de les reconnaître plus facilement. Il est bon également de les placer dans un dossier. Moins il y aura de fichiers à la racine de votre site et plus votre site sera simple à maintenir, sans compter que cela déchargera aussi votre serveur.</p>
<p>Ne tentez pas le diable en essayant d&rsquo;inclure un fichier dont le nom est indiqué par une variable : </p>
<p><code><br />
<?php
include $fichier;
?><br />
</code></p>
<p>C&rsquo;est une piste que les pirates aiment bien. On ne va pas rentrer dans les détails, mais sachez que c&rsquo;est une porte ouverte. Si toutefois vous tenez absolument à faire un include avec une variable, vérifiez et revérifier avec des tests que la page que vous allez charger soit bien une page à vous, se trouvant sur votre serveur.</p>
<h2>Et require alors ?</h2>
<p>Php nous propose une autre fonction pour inclure un fichier : <strong>require</strong> </p>
<p>Avec require, un fichier ne sera inclus qu&rsquo;une seule fois dans un autre. Par exemple, dans une boucle, un fichier include sera inclus autant de fois que la boucle tournera alors qu&rsquo;une fichier chargé avec require ne sera chargé que la première fois.</p>
<p>require fonctionne de la même façon que include d&rsquo;un point de vue syntaxique.</p>
<h2>Mon site en morceau</h2>
<p>Si vous souhaitez maintenir un site très facilement, faites en sorte de regrouper tous ce qui peut être regrouper et de les inclure dans vos pages à l&rsquo;aide de require ou include.<br />
<br />Par exemple, les entêtes des pages html peuvent être mis dans un fichier « entete.php ».</p>
<h2>Un peu plus loin</h2>
<p>Il est tout à fait possible d&rsquo;appeler un fichier distant et de lui passer des paramètres.<br />
<br />L&rsquo;appel suivant fonctionne très bien : </p>
<p><code><br />
<?php
include "http://www.mon-domaine.fr/mon_fichier.php?toto=1&#038;titi=2;"
?><br />
</code></p>
<p>Par contre, pour un fichier en local, ce n&rsquo;est pas pareil. L&rsquo;appel suivant ne fonctionnera pas : </p>
<p><code><br />
<?php
//--- ceci n'est pas correct
include "mon-fichier.php?toto=1&#038;titi=2";
?><br />
</code></p>
<p>En effet, il faut faire comme cela : </p>
<p><code><br />
<?php
//----- ceci est correct
$toto = 1;
$titi = 2;
include "mon-fichier.php";
?><br />
</code></p>
<p>Tout simplement.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Génération de nombres aléatoires</title>
		<link>https://www.magazine-jeux.com/generation-de-nombres-aleatoires/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Mon, 07 Aug 2006 05:17:00 +0000</pubDate>
				<category><![CDATA[Programmation]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/generation-de-nombres-aleatoires/</guid>

					<description><![CDATA[Pour commencer, parcequ&#8217;il faut bien commncer un jour et par quelque chose, une machine ne sortira jamais réellement un nombre aléatoire au sens propre du terme. Les nombres générés par un programme sont en fait issus d&#8217;une suite mathématique. Une méthode de calcul Pour exemple simple, nous avons la méthode de Von Neumann qui consiste [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Pour commencer, parcequ&rsquo;il faut bien commncer un jour et par quelque chose, une machine ne sortira jamais réellement un nombre aléatoire au sens propre du terme. Les nombres générés par un programme sont en fait issus d&rsquo;une suite mathématique. </p>
<h2>Une méthode de calcul</h2>
<p>Pour exemple simple, nous avons la méthode de Von Neumann qui consiste à prendre un nombre que l&rsquo;on élève au carré puis, à prendre les chiffres du milieu comme résultat. Ce résultat est utilisé pour le prochain nombre et ainsi de suite.</p>
<p>Un exemple avec de vrais chiffres histoire de comprendre un peu mieux :</p>
<p><code><br />
On commence avec le nombre suivant : 123<br />
Elevé au carré : 123² = 15129<br />
Donc, nous prenons les chiffres du milieu : 512 (c'est le premier résultat)<br />
Et c'est reparti :<br />
 512² = 262144<br />
Donc, nous avons comme résultat : 6214<br />
</code></p>
<p>Au passage, on constate que cette méthode utilise le résultat en terme de chaîne de caractères et non en nombre. Bref&#8230;</p>
<p>Il existe évidement beaucoup de méthodes.</p>
<h2>La graine</h2>
<p>Le nombre de départ est appelé <strong>la graine</strong>. C&rsquo;est lui qui va déterminer la suite logique de nombres pseudos aléatoires. Il va donc de soit que ce nombre doit être lui-même aléatoire. Mais là, nous tournons en rond.</p>
<p>Pour sélectionner une graine de « qualité », nous prenons généralement le temps écoulé depuis l&rsquo;allumage de l&rsquo;ordinateur.</p>
<h2>Et en Php ?</h2>
<p>Pour faire simple et aller droit au but qui vous interresse, voici les fonctions vous permettant de générer des nombres aléatoires à partir du Php : </p>
<p><code><br />
// Initialisation du générateur<br />
srand((float) microtime()*1000000);</p>
<p>// Génération d'un nombre compris entre 1 et 6<br />
$resultat = rand(1, 6) ;<br />
</code></p>
<p>Il s&#8217;emblerait d&rsquo;après la documentation du Php que cette fonction (rand) soit un peu lente. Il existe donc une fonction qui serait plus « rapide » et, en tout cas, meilleure en terme de résultats.</p>
<p><code><br />
// Initialisation du générateur<br />
mt_srand((float) microtime()*1000000);</p>
<p>// Génération d'un nombre compris entre 1 et 6<br />
$resultat = mt_rand(1, 6) ;<br />
</code></p>
<p>La seule différence est <strong>mt_</strong> devant les fonctions.<br />
<br />Bon, d&rsquo;accord, ce serait plus rapide. Alors testons !</p>
<h2>Les tests</h2>
<p>Nous avons donc effectuer une boucle de 1 million de générations et regardé en moyenne le temps écoulé :</p>
<p><strong>rand</strong> : 1058 ms<br />
<br /><strong>mt_rand</strong> : 1058 ms</p>
<p>Difficile de dire que l&rsquo;une est plus rapide que l&rsquo;autre. Les tests ont été fait sur des serveurs Windows et Linux en Php 4 et 5, avec des valeurs de recherche comprises entre 1 et 6 puis, entre 1 et 6000. Nous retrouvons toujours les mêmes valeurs.</p>
<p>Les suites générées sont très certainement plus « aléatoires » avec la fonction mt_rand. Il est également fort possible que la fonction <em>rand</em> ait été reprogrammée.</p>
<h2>Une utilisation particulière</h2>
<p>L&rsquo;aléatoire n&rsquo;étant pas vraiment aléatoire, il est possible d&rsquo;utiliser cette possibilité pour effectuer des calculs en fonction d&rsquo;évènements extérieurs comme par exemple en fonction du jour du mois.</p>
<p>Voici une boucle qui affiche 10 valeurs aléatoires, 5 fois de suite : </p>
<p><code><br />
for($j=1;$j<=5;$j++) <em><br />
srand((float) microtime()*1000000);<br />
	for($i=1;$i<=10;$i++) <em><br />
		// Génération d'un nombre compris entre 1 et 6<br />
		$resultat = rand(1, 60000) ;<br />
		echo $resultat.":";<br />
	</em><br />
	echo "<br />";<br />
</em><br />
</code></p>
<p>Voici le résulat (un des résultats en fait) :</p>
<p><code><br />
9299:40472:54969:40395:10961:445:15792:18826:33600:26864:<br />
8548:28017:19267:13263:10386:37151:28547:23509:51014:14292:<br />
31035:3183:15109:46568:35950:32619:2386:43602:1372:17407:<br />
51805:55513:38735:27750:52002:5818:55749:41044:4739:2881:<br />
59050:23083:5448:9808:332:16542:31280:9302:53996:19728:<br />
</code></p>
<p>Voici la même boucle, mais en remplaçant l&rsquo;initialisation du générateur par la ligne suivante : </p>
<p><code><br />
srand(date("j"));<br />
</code></p>
<p>Le générateur est donc initialisé avec le jour du mois.<br />
<br />Nous avons le résultat suivant : </p>
<p><code><br />
49653:63:12516:11456:26208:58327:22059:35442:49521:53193:<br />
49653:63:12516:11456:26208:58327:22059:35442:49521:53193:<br />
49653:63:12516:11456:26208:58327:22059:35442:49521:53193:<br />
49653:63:12516:11456:26208:58327:22059:35442:49521:53193:<br />
49653:63:12516:11456:26208:58327:22059:35442:49521:53193:<br />
</code></p>
<p>Vous aurez donc pour un même jour les mêmes résultats.<br />
<br />Ce système peut-être utilisé lorsque vous souhaitez afficher une information aléatoire chaque jour par exemple, mais que cette information soit la même toute la journée.</p>
<p>A vous de compliquez un peu le système pour trouver d&rsquo;autres utilisations pertinentes.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Les sessions en PHP</title>
		<link>https://www.magazine-jeux.com/les-sessions-en-php/</link>
		
		<dc:creator><![CDATA[Jean-François Renauld]]></dc:creator>
		<pubDate>Mon, 03 Jul 2006 05:10:00 +0000</pubDate>
				<category><![CDATA[Programmation]]></category>
		<guid isPermaLink="false">https://www.magazine-jeux.com/les-sessions-en-php/</guid>

					<description><![CDATA[Introduit à partir de la version 4 du PHP, vous devez donc avoir la version 4.1.0 au moins afin de faire fonctionner correctement les exemples de cet article. Normalement, tout les hébergeurs ont au moins cette version. Le principe Les sessions permettent de conserver des informations côté serveur et de les utiliser ensuite à chaque [&#8230;]]]></description>
										<content:encoded><![CDATA[<p class="post_excerpt">Introduit à partir de la version 4 du PHP, vous devez donc avoir la version 4.1.0 au moins afin de faire fonctionner correctement les exemples de cet article. Normalement, tout les hébergeurs ont au moins cette version.</p>
<p><span id="more-120"></span></p>
<h2>Le principe</h2>
<p>Les sessions permettent de conserver des informations côté serveur et de les utiliser ensuite à chaque fois qu&rsquo;une page dynamique est créée. Ces informations sont propres à l&rsquo;utilisateur. C&rsquo;est-à-dire que chaque utilisateur dispose d&rsquo;une session pouvant contenir plusieurs informations.</p>
<p>Contrairement aux cookies qui sont stockés côté client, les sessions sont stockées côté serveur. Evidement cela implique une sécurité accrue, mais en contre partie, cela demande d&rsquo;avoir un peu plus la main sur les paramètres du serveur.</p>
<p>La durée de vie d&rsquo;une session est définie au niveau serveur. Cette valeur peut-être consultée en utilisant la fonction phpinfo() puis, en consultant la ligne <strong>session.cache_expire</strong>.<br />
<br />Cette valeur est modifiable dans le fichier de configuration.</p>
<h2>Création d&rsquo;une session</h2>
<p>Allez, c&rsquo;est parti, voici le code permettant de démarrer une session : </p>
<p><code><br />
<?php 
session_start();
?><br />
</code></p>
<p>Vous devez démarrer toutes vos pages dynamiques par cette commande. Elle est obligatoire sauf si la directive <strong>session.auto_start</strong> est à ON.</p>
<p>Les sessions sont alors sauvegardées dans un dossier. Vous pouvez consulter le nom de ce dossier en regardant la directive <strong>session.save_path</strong> dans le résultat d&rsquo;un phpinfo().</p>
<p>Vous pouvez obtenir l&rsquo;identifiant d&rsquo;une session par la commande <strong>session_id()</strong>.<br />
<br />Dans certains cas, notamment si votre jeu se trouve dans un sous domaine avec d&rsquo;autres jeux, il est conseillé de nommer vous-même les sessions en utilisant la commande <strong>session_name()</strong> :</p>
<p><code><br />
<?php 
session_name('MonJeu');
session_start();
?><br />
</code></p>
<h2>Enregistrement d&rsquo;informations dans la session</h2>
<p>La session est crée, il faut maintenant enregistrer des informations dedans : </p>
<p><code><br />
$_SESSION['s_Pseudo'] = $Pseudo;<br />
</code></p>
<p>Cette ligne permet d&rsquo;enregistrer dans une variable de session le contenu de la variable $Pseudo.</p>
<p>Vous pouvez évidement enregistrer plusieurs informations : </p>
<p><code><br />
$_SESSION['s_Pseudo'] = $Pseudo;<br />
$_SESSION['s_Identifiant'] = $Id;<br />
$_SESSION['s_Age'] = $Age;<br />
</code></p>
<p>Essayez de nommer vos variables de session d&rsquo;une façon logique. Ici, nous utilisons <strong>s_</strong> devant le nom de la variable. Ceci afin d&rsquo;éviter de les mélanger avec d&rsquo;autres variables suivant la configuration de votre serveur.</p>
<h2>Lecture des informations d&rsquo;une session</h2>
<p>Vous voilà donc avec une session et pleins d&rsquo;informations dedans. Voyons comment relire les variables :</p>
<p><code><br />
$Pseudo = $_SESSION['s_Pseudo'];<br />
</code></p>
<p>Tout simplement en faisant l&rsquo;inverse que pour l&rsquo;enregistrement.</p>
<h2>Effacement et suppression des sessions</h2>
<p>Pour effacer une variable de session, vous pouvez utiliser la commande classique <strong>unset()</strong> :<br />
<code><br />
unset($_SESSION['s_Pseudo']);<br />
if(isset($_SESSION['s_Pseudo'])) <em><br />
	echo "erreur lors de la suppression.";<br />
</em>else <em><br />
	echo "La variable a bien été supprimée.";<br />
</em><br />
</code><br />
Pour enfin supprimer complètement une session (utile pour proposer au joueur de se déconnecter par exemple) : </p>
<p><code><br />
session_destroy();<br />
</code></p>
<p>N&rsquo;oubliez pas de placer en haut de chaque page la commande de création de votre session.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
