JeuWeb - Crée ton jeu par navigateur
Petite solution pour alléger le code de certains - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Petite solution pour alléger le code de certains (/showthread.php?tid=4322)

Pages : 1 2 3


RE: Petite solution pour alléger le code de certains - lemouix - 09-09-2009

Voilà un trigger vraiment tout bête:
Code PHP :
<?php 
CREATE trigger majdelete ON DELETE ON terre
BEGIN
UPDATE continent SET TailleRestante
= (TailleRestante + 1) WHERE Id = OLD.Continent_Id;
END;

On dispose des tables suivantes:
Terre (une terre ne peut appartenir qu'à un seul continent):
Id_Terre
Nom
Description
Id_Continent (FK)

Continent:
Id_Continent
Nom
Description
TailleRestante

Lorsqu'on supprime un espace de la table Terre qui fait référence à un continent, alors l'espace de 1 est réalloué au continent.

Tu peux prendre ce principe pour les ressources ou autre, il s'agit d'un trigger vraiment simple.


RE: Petite solution pour alléger le code de certains - jo_link_noir - 09-09-2009

@lemouix : Pour un système comme le tien je faisais 2 requêtes, mais un trigger serait plus simple, faudrait que je regarde ça plus en détail.

@Sephi-Chan : Bah justement dans le cas où le coût se calcule selon le niveau ?

Pour le moment le seul moyen que j'ai trouvé c'est d'écrire le calcule puis de l'interpréter.
Par exemple (au hasard) avec le tableau sérialisé en json
 {
gold: pow(lvl),
wood: lvl*5
}
Mais bon bof, doit y avoir une autre solution ?


RE: Petite solution pour alléger le code de certains - QuentinC - 09-09-2009

Bonjour,

Premièrement, que pensez-vous de la solution double array ?
Code :
<?php
$prixBatiments = array(
array(100, 100, 200), // Caserne
array(200, 100, 50), // Tour
array(400, 400, 800), // Fortifications
....
);?>
En sachant que les noms des bâtiments et les noms des ressources sont enregistrés ailleurs, typiquement dans un fichier de langue.
Ca me paraît stupide d'utiliser la base SQL pour stocker ça parce que les données ne changent pas souvent une fois qu'on a trouvé les bonnes valeurs. Dommage que les define ne marchent pas avec les array...

Sinon pour le prix des bâtiments en fonction du niveau, je pense qu'il suffit de définir un prix de base (prix au niveau 1) puis de déduire le prix des niveaux suivants à partir de là avec une formule simple. Typiquement, quelque chose du genre prix au niveau N = prix de base * multiplicateur ^N. C'est ce que font tous les ogame-like ou presque, il me semble... mais rien n'interdit de définir une autre fonction, par exemple prix au niveau N = prix de base * fibonacci(N+2) ou encore prix au niveau N = prix de base * (N+1)^2/2 ...


RE: Petite solution pour alléger le code de certains - Sephi-Chan - 09-09-2009

Quelqu'un pourrait m'expliquer l'intérêt de stocker ça autre part que dans la table qui contient les bâtiments ? Je ne comprends pas pourquoi vous vous emmerdez à vouloir mettre ça ailleurs.

Voilà ce que je ferais si je développais un Ogame-like :

Il suffit de stocker le prix de base dans la table, pour chaque modèle de bâtiment. Notre table building_models contient donc (au minimum) les champs suivant :
  • id - L'identifant du type de bâtiment ;
  • type - Le nom de la classe qui sert à représenter ce genre de bâtiment (cf. plus bas) ;
  • gold_cost - Le coût de base en or ;
  • wood_cost - Le coût de base en bois ;
  • duration - Durée de création du bâtiment;

Ensuite, vous instanciez vos bâtiments pour chaque utilisateur grâce à la table de jointure qui contient au moins quatre champs :
  • id - L'identifiant cette instance ;
  • building_model_id - L'identifiant du modèle de bâtiment (la table des modèles s'appelle building_models donc on le retrouve dans la clé étrangère, au singulier) ;
  • user_id - L'identifiant de votre joueur ;
  • level - Le niveau actuel du bâtiment ;
  • upgrade_started_at - Le timestamp (champ de type DATETIME) du moment où l'amélioration a été lancée.

À côté de ça, j'ai une classe Building et une table pour chaque type de bâtiment (cela servira si le bâtiment a des comportements différent de n'importe quel Building).

Cette classe Building dispose d'au moins deux méthodes :
  • Une méthode costForLevel(level) qui renvoie un tableau associatif qui associe la somme nécessaire pour chaque ressource pour évoluer vers le niveau indiqué.
  • Une méthode costForLevel(level) qui renvoie la durée (en secondes) pour évoluer vers le niveau indiqué.

Ainsi, si la caserne (la classe s'appellera Barracks) n'a pas la même courbe d'évolution que les autres, il suffira de surcharger la méthode de la classe Barracks.

Quand vous instancierez vos bâtiments, vous utiliserez une Factory (qui créera un objet Barracks si le champ type était Barracks, etc.).

Cette approche à un nom : Single Table Inheritance.

Ainsi, vous avez un système robuste, qui repose sur des design patterns (qui sont des solutions éprouvées dans l'industrie, donc généralement (toujours ?) meilleures que celles que vous pouvez imaginer) et qui est très souple.


Sephi-Chan


RE: Petite solution pour alléger le code de certains - QuentinC - 09-09-2009

Citation :Quelqu'un pourrait m'expliquer l'intérêt de stocker ça autre part que dans la table qui contient les bâtiments ? Je ne comprends pas pourquoi vous vous emmerdez à vouloir mettre ça ailleurs.
Perso je pars du principe que tout ce qui est stocké en base sont des données qui sont supposées changer plus ou moins fréquemment. Le prix des unités, bâtiments et autres ne répondent pas à ce critère donc pour moi ça n'a rien à faire dans une table.

Par contre, le coup du design pattern est pas mal. Je fais pas vraiment de la POO quand je fais du php donc ce ne sera pas ma solution ici, mais elle a l'air bien sympathique quand même. JE ferais du java, je prendrais probablement une idée dans ce genre.... mais avec les prix stockés en tant que public static final ou réunis dans une autre classe. Pas en base en tout cas, ça sert à rien d'ajouter un select inutile sur toutes les pages.
Le mieux, ce serait de les avoir en mémoire et de les conserver ainsi entre deux requêtes HTTP et indépendamment des sessions, mais bon, faut pas rêver, je vois pas comment c'est faisable en php de manière simple, ou alors j'ai loupé un chapitre.


RE: Petite solution pour alléger le code de certains - Sephi-Chan - 09-09-2009

(09-09-2009, 10:56 PM)QuentinC a écrit :
Citation :Quelqu'un pourrait m'expliquer l'intérêt de stocker ça autre part que dans la table qui contient les bâtiments ? Je ne comprends pas pourquoi vous vous emmerdez à vouloir mettre ça ailleurs.
Perso je pars du principe que tout ce qui est stocké en base sont des données qui sont supposées changer plus ou moins fréquemment. Le prix des unités, bâtiments et autres ne répondent pas à ce critère donc pour moi ça n'a rien à faire dans une table.

Par contre, le coup du design pattern est pas mal. Je fais pas vraiment de la POO quand je fais du php donc ce ne sera pas ma solution ici, mais elle a l'air bien sympathique quand même. JE ferais du java, je prendrais probablement une idée dans ce genre.... mais avec les prix stockés en tant que public static final ou réunis dans une autre classe. Pas en base en tout cas, ça sert à rien d'ajouter un select inutile sur toutes les pages.
Le mieux, ce serait de les avoir en mémoire et de les conserver ainsi entre deux requêtes HTTP et indépendamment des sessions, mais bon, faut pas rêver, je vois pas comment c'est faisable en php de manière simple, ou alors j'ai loupé un chapitre.

Dans tous les cas, tu dois sélectionner dans la table… Que tu ramasses les ressources ou non, ça ne change rien.

Enfin, je pense que vous oubliez le rôle d'une base de données : contenir des données et les donner quand on lui demande. De plus, les requêtes de ce type (des SELECT vraiment basiques) sont très rapides puisque très optimisés par MySQL. Bref, ce n'est pas sur ces requêtes là qu'il faut grapiller.

Vous vous compliquez pour rien et manquez de pragmatisme sur ce sujet. M'est avis que vous feriez mieux de passer du temps

Si tu veux faire persister les données pour optimiser encore tout ça, tu peux utiliser Memcached. Ça fait exactement ce que tu indiques : stocker ce que tu veux en RAM (donc de manière distribuée). Ça implique toutefois d'avoir un environnement dédié (puisque Memcached est un serveur qui tourne à côté (Memcached, d comme daemon).

Exemple de pseudocode issu de Wikipedia - Memcached :


function get_foo(int userid){
result = db_select("SELECT * FROM users WHERE userid = ?", userid);
return result;
}

function get_foo(int userid){
result = memcached_fetch("userrow:" + userid);
if(!result){
result = db_select("SELECT * FROM users WHERE userid = ?", userid);
memcached_add("userrow:" + userid, result);
}
return result;
}

En adaptant ça (très simple) et en le foutant dans une méthode statique find_by_id de ta classe BuildingModel, ça roule et ton ancien code continue de fonctionner au poil. Si tu cesses d'utiliser Memcached, ça continue de fonctionner à merveille. Smile

Et si vraiment tu n'es pas sur un dédié (donc pas de Memcached) ou que ça te fait vraiment chier d'interroger ta base, tu peux toujours mettre en cache les données de la table building_models dans un fichier JSON (ma préférence, car très simple et performant) et le tour est joué. C'est con comme tout à implémenter avec json_encode() et json_decode().

M'enfin avant de penser performances, pensez robuste ! Vous perdrez moins de temps. Si vous avez des modèles de données bien construits, mettre des caches deviens très simple puisqu'il suffit de modifier les accesseurs comme on l'a vu dans le pseudocode donné pour illustrer la manière d'implémenter Memcached.


Sephi-Chan


RE: Petite solution pour alléger le code de certains - QuentinC - 10-09-2009

Citation :Dans tous les cas, tu dois sélectionner dans la table… Que tu ramasses les ressources ou non, ça ne change rien.
Ben si... ça fait deux select au lieu d'un seul, ou alors ça fait un select avec des jointures donc c'est plus lent.

JE suis en ce moment déjà entre 6 et 8 requêtes par page, c'est pas déjà un peu trop ?

Sinon à part ça aprè quelques recherches rapides, il semblerait qu'il existe deux autres extensions qui font aussi du caching en RAM : APC et shmop.
Quelle est la différence entre les trois (memcached, shmop et APC) ? Lequel est le plus approprié ? pourquoi ?
Bon, si la réponse est longue, ça méritera peut-être un autre topic.


RE: Petite solution pour alléger le code de certains - Sephi-Chan - 10-09-2009

Ouais mais tu t'en fous de faire une deuxième requête ou une requête avec jointure. Ce sont des requêtes facilement optimisables par MySQL, c'est bidon sur les perfs.

Concernant les différences entre les systèmes de cache, Memcached est un serveur. Tu peux t'y connecter depuis n'importe quel langage (cf. la liste des API clientes).

APC est propre à PHP, il cache l'opcode de manière totalement automatique. Tu gagnes environ 25% de performances sur les traitements PHP rien qu'en l'installant. Bien sûr, dès que tu es dans un environnement dédié, il faut l'utiliser.
Mais APC, c'est aussi un cache utilisateur qui s'utilise un peu comme Memcached : tu associe en RAM une clé à une valeur et tu peux les récupérer, les virer, etc.

Shmop (Shared Memory) est également un cache utilisateur : là aussi tu associes une clé à une valeur.


Sephi-Chan


RE: Petite solution pour alléger le code de certains - My Hotel - 10-09-2009

Alors, franchement, je pense que la technique de Sephi est la meilleure. Si on y réfléchi objectivement, elle est plus évolutive, pratique, propre. Son seul défaut est d'utiliser un peu plus de ressources.
Mais si vous préférez optimiser et avoir un code tout pourri, pourquoi vous ne codez pas comme ça tant qu'on y est :
Code PHP :
<?php if(empty($test)){ echo 'lol'; }else{throw new E();} 

Franchement, la perte de performance est là, mais elle est faible, vois négligeable avec des bons outils de cache.

D'ailleurs, en recherchant dans le forum, on voit qu'il y a plein de topics qui parlent de cache, que ce soit fichier, memcached, APC, arrays, etc....

Tu trouveras ton bonheur.

EDIT : grillé a mort, ça m'apprendra à ne pas actualiser avant de taper un message, alors que la page est ouverte depuis 1h! Smile


RE: Petite solution pour alléger le code de certains - Anthor - 10-09-2009

Je crois surtout aussi que si certains utiliser le profilage il se rendrait de la connerie de leurs optimisations...

Un connect à une base prend bien le temps de 10 à 15 requêtes... Méditez déjà cela avant de compter votre nombre de ligne Big Grin