JeuWeb - Crée ton jeu par navigateur
[Résolu] Structure des tables pour un Ogame-like - 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 : [Résolu] Structure des tables pour un Ogame-like (/showthread.php?tid=4581)

Pages : 1 2 3 4 5


[Résolu] Structure des tables pour un Ogame-like - QuentinC - 11-02-2010

Bonjour,
JE m'interroge sur la meilleure structure à adopter pour mes tables, pour un jeu dont les principes de base ressemblent à ogame (l'époque n'est pas la même, le gameplay sera j'espère suffisament différent, mais les structures de base sont les mêmes)
Chaque joueur contrôle un empire constitué de plusieurs villes. Dans chaque ville, il y a un certain nombre d'unités de chaque type, et un certain nombre de bâtiments de chaque type. Ca paraît stupide, et pourtant, j'hésite entre les deux structures suivantes :

Première :
Code :
create table Villes (
id int unsigned auto_increment,
joueur int unsigned not null,
-- ...
batiment0 int unsigned not null,
batiment1 int unsigned not null,
batiment2 int unsigned not null,
-- ...
unite0 int unsigned not null,
unite1 int unsigned not null,
unite2 int unsigned not null,
--- ...
primary key(id));

Seconde :
Code :
create table Villes (
id int unsigned auto_increment,
joueur int unsigned not null,
-- ...
primary key(id));

create table Batiments (
ville int unsigned not null,
type int unsigned not null,
nombre int unsigned not null,
primary key(ville,type));

create table Unites (
ville int unsigned not null,
type int unsigned not null,
nombre int unsigned not null,
primary key(ville,type));

Avantages et inconvénients selon moi :
- Une seule requête, sans jointure, une seule ligne retournée pour savoir combien il y a de bâtiments ou d'unités de chaque type dans une ville donnée dans la première structure. Dans la seconde, il faut trois requêtes et ça me paraît stupide de multiplier les requêtes pour ça alors que j'ai besoin de ces informations presque systématiquement, p.ex. pour mettre à jour les ressources. En plus c'est moins simple de savoir s'il existe un bâtiment d'un type donné dans une ville donnée (ça fait encore une requête inutile de plus)
- Nombreuses requêtes nécessaires pour faire des update dans la deuxième structure : une pour la ville, une pour chaque type de bâtiment et chaque type d'unité mis à jour. Si j'étends la structure aux ressources et aux technologies, le nombre de requêtes par page va juste exploser, et je ne parle même pas encore des batailles...
- Il faut utiliser des variables variables pour accéder aux données dans la première stucture en php, p.ex. $this->{"unite$i"} dans une boucle, ce qui est lent et peu performant. Pas besoin de ce genre de chose dans la seconde, on peut gérer avec des tableaux ou des objets normaux.
- La seconde structure est plus logique, plus facile à comprendre, mieux normalisée, sans doute plus simple à maintenir (ça reste à prouver)
- La seconde permet de pofiter plus facilement de la POO (encore que... pas sûr, j'ai du mal à voir comment exactement)
- Le jour où j'ajoute une nouvelle unité ou un nouveau bâtiment, c'est plus facile avec la première structure (il suffit d'ajouter une colonne, contre autant de lignes qu'il y a de villes pour la deuxième structure)
- S'il existe 10 types de bâtiments, 10 types d'unités, si j'ai 1000 joueurs, que chacun possède en moyenne 10 villes, les tables Batiments et Unites vont être immenses (100000 lignes chacune). Par contre dans la première structure, la table Villes aura un grand nombre de colonnes, genre 30 ou 40.

J'aurais besoin de vos lumières pour trancher (ou pour choisir une troisième solution au cas où vous auriez encore mieux à proposer).....
Merci.


RE: Structure des tables pour un ogame-like - My Hotel - 11-02-2010

De toute façon, la première structure est à proscrire. Une ville possède n bâtiments, même si n peut être fini, c'est une relation 1->n, ça suppose une jointure : dans la table bâtiments tu met un ville_id, pareil pour les unités.
C'est beaucoup plus maintenable.
Ensuite, tu dis
Citation :- Nombreuses requêtes nécessaires pour faire des update dans la deuxième structure : une pour la ville, une pour chaque type de bâtiment et chaque type d'unité mis à jour. Si j'étends la structure aux ressources et aux technologies, le nombre de requêtes par page va juste exploser, et je ne parle même pas encore des batailles...
Là, il faut que tu comprenne que il faut accepter de perdre un peu de performance, et de produire un code acceptable. Parce que la première structure est vraiment mer**ique, et peut-être même pas plus rapide d'ailleurs. Tu dis 100000 lignes par table, et tu penses que c'est gros. Pour un SGBD c'est ridicule, par contre 70 colonnes, ça fait mal!

Enfin :
Citation :- Le jour où j'ajoute une nouvelle unité ou un nouveau bâtiment, c'est plus facile avec la première structure (il suffit d'ajouter une colonne, contre autant de lignes qu'il y a de villes pour la deuxième structure)
Là tu te trompes. Structure 1, tu ajoutes un bâtiment, faut rajouter une colonne, et changer toutes tes requêtes, car une bonne requête précise les noms de champs genre : SELECT champ1, champ2...
Alors que la 2, si tu offre un nouveau possible, eh ben tu rajoute RIEN! Tu donne seulement au joueur la possibilité d'acheter le nouveau bâtiment, auquel cas tu ajoute une ligne dans ta table bâtiment. Mais faut pas mettre de lignes avec nombre = 0!
Le mieux, c'est une table liste_batiment, il te suffit de rajouter un bâtiment à cette table, et il apparaît dans la liste des constructions automatiquement, etc... Faut pas faire un if($_POST['batiment'] == 1)... elseif...
C'est plus une requete INSERT ...ON DUPLICATE KEY UPDATE pour moi.

Voilà, en gros c'est à peu près tout, système 2 c'est bien mieux pour moi Wink


RE: Structure des tables pour un ogame-like - Anthor - 11-02-2010

Citation :Dans la seconde, il faut trois requêtes et ça me paraît stupide de multiplier les requêtes pour ça alors que j'ai besoin de ces informations presque systématiquement, p.ex. pour mettre à jour les ressources.

Faux et horrible !
Tu enregistres uniquement le ratio pour un certain temps et tu le calcules lors de la modification.
Faut pas hésiter à rajouter 2/3 champs, uniquement pour limiter les calculs de ce genre.


RE: Structure des tables pour un ogame-like - QuentinC - 11-02-2010

Citation :Là, il faut que tu comprenne que il faut accepter de perdre un peu de performance, et de produire un code acceptable. Parce que la première structure est vraiment mer**ique, et peut-être même pas plus rapide d'ailleurs. Tu dis 100000 lignes par table, et tu penses que c'est gros. Pour un SGBD c'est ridicule, par contre 70 colonnes, ça fait mal!
Ah, c'est bien pire à gérer 70 colonnes que 100000 lignes ? J'avoue mon ignorance sur ce point...


Citation :Là tu te trompes. Structure 1, tu ajoutes un bâtiment, faut rajouter une colonne, et changer toutes tes requêtes, car une bonne requête précise les noms de champs genre : SELECT champ1, champ2...
Donne-moi une bonne raison d'expliciter tous les champs plutôt qu'utiliser *. Je n'ai pas la possibilité de construire des objets partiels, et d'ailleurs quel est l'intérêt ?
Sachant que j'utilise un ORM maison ultra-simplifié et que pour récupérer une ligne, j'ai juste à faire Record::findFirst('Ville', array('id'=>7)); et hop j'ai un objet Ville avec toutes les propriétés remplies (vive PDO).

Citation :Alors que la 2, si tu offre un nouveau possible, eh ben tu rajoute RIEN! Tu donne seulement au joueur la possibilité d'acheter le nouveau bâtiment, auquel cas tu ajoute une ligne dans ta table bâtiment. Mais faut pas mettre de lignes avec nombre = 0!
C'est vrai, dans l'absolu tu as raison, je ne suis pas obligé de mettre des lignes avec nombre = 0. Bien vu.

Citation :Le mieux, c'est une table liste_batiment, il te suffit de rajouter un bâtiment à cette table, et il apparaît dans la liste des constructions automatiquement, etc... Faut pas faire un if($_POST['batiment'] == 1)... elseif...
J'ai de toute façon écarté la longue liste de if...elseif...else depuis longtemps. Je ne suis pas un imbécile quand même, je fais de la programmation depuis assez longtemps pour savoir que c'est un cauchemar.
J'aurais en fait utilisé des tableaux constants du genre :
Code :
$prixBatiments = array(
array(50, 50, 0, 0),
array(100, 150, 50, 0),
array(100, 0, 100, 50),
//...
);
Par contre j'ai du mal à voir l'avantage de stocker les coûts et ce genre de propriété permanente dans la base plutôt que définir des constantes php (on s'entend sur le terme de constantes, puisqu'un tableau ne peut techniquement pas être constant en php)
Là aussi j'aurais besoin d'un argument pour me démolir.

Citation :C'est plus une requete INSERT ...ON DUPLICATE KEY UPDATE pour moi.
Replace into tu veux dire, non ? Ou bien c'est pas pareil. Je sais pas, j'ai jamais utilisé insert ... on duplicate key update, mais j'ai l'impression que c'est la même chose.
Enfin, ça m'arrangerais bien.... je ne fais plus d'insert ni d'update depuis que j'ai découvert replace. Je ne sais pas si ça a des influences négatifs en fait.

Citation :Faux et horrible !
Tu enregistres uniquement le ratio pour un certain temps et tu le calcules lors de la modification.
Faut pas hésiter à rajouter 2/3 champs, uniquement pour limiter les calculs de ce genre.
J'ai du mal à te suivre.
A chaque mise à jour des ressources, il faut bien que je connaisse le nombre d'unités et le nombre de bâtiments de chaque type pour pouvoir calculer combien de ressources ont été produites et combien ont été consommées.
« Ajouter quelques champs » consisterait à cumuler les deux structures ? je comprends pas trop où tu veux en venir...

EDIT : suppression. Je m'apprêtais à dire une grosse bêtise


RE: Structure des tables pour un ogame-like - Sephi-Chan - 11-02-2010

La première forme proposé, avec des colonnes bâtiment1, bâtiment2, etc. est à proscrire. Ce serait envisageable s'il s'agissait de deux ou trois bâtiments, mais là c'est clairement une relation 1, n qu'il convient de traiter comme telle, avec une table de liaison. C'est une question de modélisation.

Avec ta solution, tu es amené à changer ton schéma régulièrement. Autant adopter l'approche souple et économique de la relation 1, n.

Et une modélisation de relation 1, n, ça passe par une table par ressource (en l'occurrence tes villes et tes modèles de bâtiments) et une table de liaison qui contient des paires (city_id; building_model_id) d'id pour chaque bâtiment dans la ville. Ainsi, chaque ligne dans cette table de relation représente une instance.

Autre avantage capital de la modélisation 1, n : les associations riches. Exemple :

Image que tu développes un jeu comme Pokémon. Avec ta méthode, les 6 Pokémons actifs de l'équipe d'un joueur sont directement inscrits dans la ligne du joueur via des colonnes pokemon_1, pokemon_2, etc.)

Si dans la table des Pokémons disponibles (pokemon_models) on a Salamèche (d'id 7) et que les joueurs A et B ont un Salamèche dans leur équipe ? Ils auront tous les deux le nombre 7 dans une des colonnes pokemon_n. Le problème ? Tu ne peux pas différencier les Pokémons du joueur A de celui du joueur B, leur niveau de PV, etc. A moins de faire une table supplémentaire qui contient toutes les instances de Pokémons (avec une colonne pokemon_model_id, par exemple), ce qui au final revient à faire du 1, n.

Alors qu'avec une modélisation 1, n, tu as directement une table pokemons qui contient des lignes avec des colonnes player_id, pokemon_type_id, nickname, health, etc. Chaque ligne de cette table modélisant une instance de Pokémon (d'où le nommage de la table).

Voilà donc pourquoi il est préférable de faire du 1, n : pouvoir faire des associations riches. Et quand bien même tu n'aurais besoin que d'associations simples, ta table de liaison ne contiendra qu'une paire d'id : pas de champs "custom". Dans ce cas d'associations simples, un bon nommage de la table est players_pokemon_models : le nom de chaque tables (des pluriels) dans un ordre précis (alphabétique, en l'occurrence) concaténé par des underscores. Ce genre de convention est capital quand on utilise un ORM, car la classe associée prendra généralement le nom de la table mis au singulier (Player, PokemonModel).

Et concernant le stockage des coûts en dur, c'est horrible. Si on suit le raisonnement, il y a beaucoup de choses qu'on peut stocker en dur dans des variables... Et pourtant, la base de données est faîte pour ça... Séparer les sources de données (ah ça c'est en DB, ça c'est en fichier, ça c'est donné par un web service, etc.), c'est mélanger les torchons et les serviettes : c'est mal et dangereux.

J'espère avoir été convaincant. Smile


Sephi-Chan


RE: Structure des tables pour un ogame-like - My Hotel - 11-02-2010

Alors, pour le INSERT ON DUPLICATE KEY UPDATE, je m'explique. Imaginons le joueur n'a pas de ferme. Il en construit une, on rajoute une ligne pour la ferme. Il en construit une seconde, cette fois on modifier la ligne et on augmente le nombre de 1.
Ca se traduit pas le on duplicate key, c'est à dire on insère, et si la ligne existe déjà (même ville et même type de bâtiment) on update la ligne avec nombre=nombre+1.

Pour la liste des champs plutôt que le * : regarde http://www.vulgarisation-informatique.com/mysql-select.php.

Pour les tableaux, c'est selon ce que tu veux faire, perso je stocke tout en BDD pour récupérer tout ce que je veux avec des jointures, mais c'est à toi de voir. L'avantage, c'est que dans ton interface d'admin tu peux rajouter un bâtiment facilement, et il apparaît tout de suite dans la page d'achat.

Voilà, bye et bon courage.

P.S : pour l'ORM, sache quand même que ça ralentit globalement les performances, mais après c'est le vieux débats perfs vs qualité du code Wink


RE: Structure des tables pour un ogame-like - Anthor - 11-02-2010

(11-02-2010, 04:55 PM)QuentinC a écrit :
Citation :Faux et horrible !
Tu enregistres uniquement le ratio pour un certain temps et tu le calcules lors de la modification.
Faut pas hésiter à rajouter 2/3 champs, uniquement pour limiter les calculs de ce genre.
J'ai du mal à te suivre.
A chaque mise à jour des ressources, il faut bien que je connaisse le nombre d'unités et le nombre de bâtiments de chaque type pour pouvoir calculer combien de ressources ont été produites et combien ont été consommées.
« Ajouter quelques champs » consisterait à cumuler les deux structures ? je comprends pas trop où tu veux en venir...

Si tu as le ratio, alors tu n'as jamais besoin de charger tous tes bâtiments à chaque modification. Te suffit juste de soustraire le ratio de l'ancien niveau et de rajouter celui du nouveau.

My Hotel a écrit :Pour la liste des champs plutôt que le * : regarde http://www.vulgarisation-informatique.co...elect.php.
J'ai pas vu où, et le profiling aurait plutôt tendance à me prouver que ça ne change que dalle.


RE: Structure des tables pour un ogame-like - QuentinC - 11-02-2010

Citation :Voilà donc pourquoi il est préférable de faire du 1, n : pouvoir faire des associations riches. Et quand bien même tu n'aurais besoin que d'associations simples, ta table de liaison ne contiendra qu'une paire d'id : pas de champs "custom".
Vous m'avez convaincu, Séphi en particulier, pour l'utilisation de ma seconde proposition plutôt que la première, donc l'association 1:n. JE vais écarter l'autre. Merci.
Si j'avais d'emblée eu des liaisons riches, je n'aurais même pas posé la question en fait, puisqu'on ne peut pas faire autrement dans ce cas-là. Ca aurait été évident. Mais je pensais que les inconvénients pour la solution 1 n'étaient pas si terribles que ça. Maintenant au moins c'est clair, ma première idée était pourrie.

Par contre Séphi, je ne suis pas tout à fait d'accord sur la suite, je cite :
Citation :Ainsi, chaque ligne dans cette table de relation représente une instance.
Dans ce cas ça signifie que si 1000 villageois habitent dans une ville, alors il y a 1000 lignes rigoureusement identiques dans la table de liaison. Étant donné que je n'ai pas d'informations additionnelles comme les pv, je ne vois pas ce que ça change avec un stockage unique villeId, type, nombre, donc avec un champ custom.
Autre point : un array de 1000 objets, c'est le bon truc pour faire exploser la RAM cette fois-ci. JE pense ne pas avoir besoin de manipuler chaque unité indépendament des autres, même en combat.
Ca pourrait certes être sympa de stocker les pv, mais après ça sera juste ingérable pour le joueur s'il a 1000 unités à disposition et s'il doit en choisir une bien spécifique parmi ces 1000 (je veux celle-là qui a presque plus de pv, pas l'autre). Je prévoyais plutôt qu'il puisse en sélectionner une de tel type, métaphoriquement « n'importe laquelle des unités de ce type ».
Imaginez une seconde le bordel si chaque vaisseau sur ogame avait des pv..... ce serait juste impossible de jouer sachant que certains ont des armées de 10000, 100000 vaisseaux.

Citation :Dans ce cas d'associations simples, un bon nommage de la table est players_pokemon_models : le nom de chaque tables (des pluriels) dans un ordre précis (alphabétique, en l'occurrence) concaténé par des underscores. Ce genre de convention est capital quand on utilise un ORM, car la classe associée prendra généralement le nom de la table mis au singulier (Player, PokemonModel).
Je suis allergique aux underscores, je préfère camelCase. Cela mis à part, c'est mon propre ORM ultra basique donc je programme ce que je veux. En l'occurence, je ne gère pas les liaisons de manière automatique (trop bourrin pour un avantage que je ne juge pas capital, encore une fois je ne suis pas convaincu par la chose mais ouvert aux discussions), et ma seule convention c'est une classe au singulier = une table au pluriel, + un fichier de définition contenant la structure de la table et des informations pour la validation automatique (là non plus je ne fais pas comme tout le monde, je n'ai pas opté pour du XML, trop malpratique à lire)

Citation :Et concernant le stockage des coûts en dur, c'est horrible. Si on suit le raisonnement, il y a beaucoup de choses qu'on peut stocker en dur dans des variables... Et pourtant, la base de données est faîte pour ça... Séparer les sources de données (ah ça c'est en DB, ça c'est en fichier, ça c'est donné par un web service, etc.), c'est mélanger les torchons et les serviettes : c'est mal et dangereux.
Tu peux développer ? Pourquoi est-ce mal ou dangereux ?
Mon argument contre est de dire qu'il s'agit de données constantes. Autrement dit une fois qu'elles ont été fixées, elles ne vont plus changer (sauf cas exceptionnel). Donc pourquoi sans cesse interroger la base de données sur des données constantes ? Je n'y vois là que des requêtes inutiles en plus, mais si tu me proposes cette possibilité, c'est qu'il y a sans doute un avantage que j'ignore encore.

Citation :Alors, pour le INSERT ON DUPLICATE KEY UPDATE, je m'explique. Imaginons le joueur n'a pas de ferme. Il en construit une, on rajoute une ligne pour la ferme. Il en construit une seconde, cette fois on modifier la ligne et on augmente le nombre de 1.
Ca se traduit pas le on duplicate key, c'est à dire on insère, et si la ligne existe déjà (même ville et même type de bâtiment) on update la ligne avec nombre=nombre+1.
Aha, pratique ! Mais alors ça fait presque la même chose que replace into. Je prends note, je ne connaissais pas. Merci.

Citation :L'avantage, c'est que dans ton interface d'admin tu peux rajouter un bâtiment facilement, et il apparaît tout de suite dans la page d'achat.
Pas faux ! Mais en même temps c'est pas censé arriver très souvent. Remarque ça peut être pratique pour des co-admin, si d'aventure ça arrivait...

Sinon, MyHotel, ton lien mène vers une page 404.

Citation :Si tu as le ratio, alors tu n'as jamais besoin de charger tous tes bâtiments à chaque modification. Te suffit juste de soustraire le ratio de l'ancien niveau et de rajouter celui du nouveau.
Je n'ai pas compris le ratio de quoi ? Désolé mais je ne te suis toujours pas.


RE: Structure des tables pour un ogame-like - Anthor - 11-02-2010

Ratio de production par heure, ou autre


RE: Structure des tables pour un ogame-like - Sephi-Chan - 11-02-2010

(11-02-2010, 09:02 PM)QuentinC a écrit : Par contre Séphi, je ne suis pas tout à fait d'accord sur la suite, je cite :
Citation :Ainsi, chaque ligne dans cette table de relation représente une instance.
Dans ce cas ça signifie que si 1000 villageois habitent dans une ville, alors il y a 1000 lignes rigoureusement identiques dans la table de liaison. Étant donné que je n'ai pas d'informations additionnelles comme les pv, je ne vois pas ce que ça change avec un stockage unique villeId, type, nombre, donc avec un champ custom.

Bien entendu. Une relation 1, n n'est utile que si tu as besoin d'une gestion fine de chaque ressource. Smile


(11-02-2010, 09:02 PM)QuentinC a écrit :
Citation :Et concernant le stockage des coûts en dur, c'est horrible. Si on suit le raisonnement, il y a beaucoup de choses qu'on peut stocker en dur dans des variables... Et pourtant, la base de données est faîte pour ça... Séparer les sources de données (ah ça c'est en DB, ça c'est en fichier, ça c'est donné par un web service, etc.), c'est mélanger les torchons et les serviettes : c'est mal et dangereux.
Tu peux développer ? Pourquoi est-ce mal ou dangereux ?
Mon argument contre est de dire qu'il s'agit de données constantes. Autrement dit une fois qu'elles ont été fixées, elles ne vont plus changer (sauf cas exceptionnel). Donc pourquoi sans cesse interroger la base de données sur des données constantes ? Je n'y vois là que des requêtes inutiles en plus, mais si tu me proposes cette possibilité, c'est qu'il y a sans doute un avantage que j'ignore encore.

Déjà, une précision que beaucoup des gen oublient souvent : Une base de données est faîte pour être interrogée. Arrêtez d'essayer de vous substituer à la base de données : c'est bancal et ça ne tourne pas plus rond. Vous n'y gagnez pas en performances et c'est la loose totale en maintenance. Les données propres aux modèles de bâtiment, tu seras de toute manière amené à les récupérer, rien que pour la sécurité : est-ce que le bâtiment que Robert cherche à contenir existe bien ?

De plus, si tu souhaites interroger ta base de données depuis une autre application (par exemple un site externe comme World of Warcraft et son armurerie) ? Comment tu fais ? Tu vas parser tes fichiers PHP ?

Bref, ça n'a aucun intérêt de stocker les informations ailleurs, à part à diminuer la qualité de l'application en ayant plusieurs sources de données fragmentées (un bout des infos dans la base, un bout dans des fichiers).


Sephi-Chan