[résolu] Système de matchmaking : Bug boucle infini avec while en PHP
#1
Hey, j'suis en train de faire un système de matchmaking un peu comme sur League of Legends : C'est un système de combat en 3v3, le joueur s'inscrit dans la file d'attente et dès qu'il y a les 6 joueurs inscrit on lance la partie.

Alors j'ai bidouiller et j'ai réussi à parvenir à mes fins, le seul problème étant qu'il y a une boucle infini dans mon script ce qui fait... planter le jeu.

Grosso modo le joueur est bien enregistrer dans la base de donnée, mais comme je vous l'ai dit il y a une boucle infinie... Dans ma table combat_connexion, je réunit l'id des 6 joueurs ainsi que l'id du combat. je vous montre donc ma boucle :

$req_co = $bdd->prepare("SELECT * FROM combat_connexion WHERE id_joueur1 = " . $id . " 
                            OR id_joueur2 = " . $id . "
                            OR id_joueur3 = " . $id . "
                            OR id_joueur4 = " . $id . "
                            OR id_joueur5 = " . $id . "
                            OR id_joueur6 = " . $id . " ");
                        $req_co->execute();
                        $donnee = $req_co->fetch();

while(!isset($donnee['id_joueur1']) OR !isset($donnee['id_joueur2']) OR !isset($donnee['id_joueur3']) OR !isset($donnee['id_joueur4']) OR !isset($donnee['id_joueur5']) OR !isset($donnee['id_joueur6']))

{

Tant que le membre n'est pas enregistrer en BDD, on cherche une partie ou le niveau du joueur est équivalent à celui des autres joueurs inscrits. (pour équilibrer les combats)

Si j'enlève ma boucle, le joueur est obligé de s'inscrire plusieurs fois jusqu'à trouver un combat... La boucle s'occupe donc de chercher un combat automatiquement.

Le problème est que la boucle ne s'arrête jamais, malgrès le fait que j'actualise les valeurs de $donnee['id_joueur1'], $donnee['id_joueur2'] etc... dans ma boucle

pouvez-vous m'aider svp? je peux vous montrer l'intégralité du script si nécessaire
Répondre
#2
Salut,

tu parles de boucle infinie, mais on n'a ici que le début, donc, on ne pourra pas t'aider sur l'exact fix à utiliser.

Toutefois, le fix exact ne me semble pas intéressant du tout. En effet, là, t'es grosso-modo dans le cadre des tâches CRON à la seconde (cf https://toile.reinom.com/non-aux-taches-...a-seconde/ ). Tu dois repenser ton problème, pour ne pas concevoir les choses comme une "interrogation en continue de la BDD", mais comme "une réponse de la BDD dès qu'une condition est atteinte".

Pour ça, je vois plusieurs approches:
* T'as l'air parti sur du jeu très "temps réel", avec des actions multi-joueurs au sein de parties ponctuelles qui n'ont éventuellement pas besoin d'un serveur centralisé. Ca me semble plus adapté à du webRTC. A défaut, à de la stack node/JS et ses events à tout va.
* Si tu ne veux pas changer la techno sur laquelle tu pars (qui me semble peu adapté au besoin fixé), alors il te faudra forcément un temps d'attente dans ta boucle (sleep), de sorte que le client ou le PHP aille "pinger" la BDD régulièrement, jusqu'à avoir une place. Je pense que tu peux le faire avec un simple "sleep(1)", itéré disons 10x dans une page PHP dédiée. En début de page, tu inscris le joueur dans la BDD avec le moment d'inscription. En fin de page, ou quand le client se déconnecte, tu supprimes le joueur. Dans chaque itération de la boucle, tu cherches 6 joueurs dans la BDD dont l'inscription date de moins de 10 secondes. Côté client, quand le PHP a répondu favorablement et a trouvé des joueurs, tu "rejoins la partie" (à toi de définir comment c'est fait). Sinon, tu relances (en AJAX, car sinon dans une page classique, ce sera chiant pour l'UX) la requête, jusqu'à temps de trouver une place.

Mais bon, vu ce sur quoi tu pars en terme de techno face à ce que tu veux faire, et vu la façon dont tu as.... "préparé" la query SQL (elle est préparée, c'est sûr, mais elle n'est clairement pas paramétrisée et donc encore totalement ouverte à l'injection), je pense que tu n'auras pas le niveau pour réussir le type de jeu que tu envisages :/ Tu risques d'être déçu!
Répondre
#3
merci pour ta réponse, pour la requête c'était pour faire simple, j'utilise bindValue pour me "protéger" des injections, je ne sais pas si c'est le plus adapté.

Je compte rester sur du PHP, et sinon mon script fonctionne actuellement, c'est juste la boucle infinie qui ralenti le jeu. Je ne connaissais pas la fonction sleep, je vais voir ce que je peux faire avec, merci.

Sinon je ne comprend pas pourquoi la boucle tourne à l'infini sachant que j'actualise mes données dans la boucle ? Normalement, dès que le joueur est insérer en BDD, la boucle devrait s'arrêter ???
Répondre
#4
Bon bah il fallait simplement utiliser la fonction "break;" et ça fonctionne.

Désolé du dérangement lol34 je reste toutefois sur le forum si je peux aider ou demander de l'aide.

merci...
Répondre
#5
J'ai l'impression que tout cela sent le jeu de fléchettes... (aka un code fait à coup de "je tente des trucs et si j'arrive à approcher ce que j'envisageai dans un cas donné, alors je considère que le code est bon sans forcément pouvoir le comprendre/l'expliquer clairement"). M'enfin, je te laisse gérer comme tu le sens. Je ne vois typiquement pas comment bindValue peut te protéger de quoi que ce soit dans l'exemple précédent. Je ne vois même pas comment tu peux y recourrir...
Répondre
#6
Tu peux penser ce que tu veux, certes tu as raison je suis novice en développement web... Comme je l'ai dis sur ma présentation je dev en procédural donc j'ai des scripts qui sont très long...

Mais tant que mon jeu fonctionne ça me va, je comprend l'essentiel de mes scripts... Je n'avais simplement pas connaissance du "break;" qui m'est fort utile.

Dire que ma solution à mon problème c'est un simple "break;" et que j'ai chercher pendant des semaines entières la solution en me prenant la tête...

J'en viens à me dire que plus on programme simple, mieux c'est.
Répondre
#7
La priorité, tu as raison, c'est de faire du code qui fonctionne, puis du code propre, puis du code rapide.

Le procédural n'est pas un problème. Tu peux créer des fonctions (avec des noms clairs et faciles à comprendre) qui s'appellent les unes les autres : ton code devient facile à suivre et les fichiers ne sont pas trop gros (donc il est facile d'y trouver ce qu'on y cherche).

Ici, le code n'est pas tellement un problème. Ce qui importe ici, c'est l'architecture.

Pour un système de match making simple et pas trop cracra, tu peux avoir une table joueurs (j'imagine que tu l'as déjà) avec une colonne numérique pour le niveau de ce joueur, et une colonne booléenne qui indique si le joueur est en recherche d'un combat ou non.

A chaque fois qu'un joueur s'inscrit à la recherche, tu peux chercher s'il y a 6 joueurs du même niveau dans la recherche. Si oui, tu crées un combat (dans une table idoine) ainsi que 6 combattants (la table combattant contient une colonne pour l'ID du joueur, une autre pour l'ID du combat et toute les informations dont leur personnage aura besoin) et tu modifies les joueurs pour qu'ils ne soient plus en recherche. Si non, il ne se passe rien et le script sera relancé quand un joueur s'inscrira en file d'attente.
Répondre
#8
Une proposition de système alternatif simple tel que proposé plus haut (c'est codé de tête et j'ai pas écrit de PHP depuis la nuit des temps).

<?php 

define("DEFAULT_HEALTH", 100);

function try_to_start_a_fight() {
   $results = fetch(
       "SELECT level, COUNT(*) AS count, GROUP_CONCAT(id) AS player_ids
       FROM players
       WHERE looking_for_a_fight = 1
       GROUP BY level
       HAVING count = 6;"
   );

   if (empty($results)) {
       return null;
   }
   else {
       $level = $results[0]['level'];
       $player_ids = $results[0]['player_ids'];
       $game_id = create_game($level, $player_ids);
       return $game_id;
   }
}


function create_game($level, $player_ids) {
   $game_id = insert("INSERT INTO games (level) VALUES(?)", $level);
   players_stops_looking_for_a_fight($player_ids);
   foreach ($player_ids as $player_id) {
       insert(
           "INSERT INTO fighters (game_id, player_id, health, max_health)
           VALUES (?, ?, ?, ?)",
           $game_id,
           $player_id,
           DEFAULT_HEALTH,
           DEFAULT_HEALTH
       );
   };

   return $game_id;
}


function player_starts_looking_for_a_fight($player_id) {
   query(
       "UPDATE players
       SET looking_for_a_fight = 1
       WHERE id = ?",
       $player_id
   );

   return $try_to_start_a_fight();
}


function players_stops_looking_for_a_fight($player_ids) {
   query(
       "UPDATE players
       SET looking_for_a_fight = 1
       WHERE id IN (?)",
       join(", ", $player_ids)
   );
}

/*
CREATE TABLE players (
   `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
   name VARCHAR(255) NOT NULL,
   level INT NOT NULL,
   looking_for_a_fight TINYINT NOT NULL
) ENGINE=InnoDB;

CREATE TABLE games (
   `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
   level INT NOT NULL,
) ENGINE=InnoDB;

CREATE TABLE fighters (
   `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
   game_id INT NOT NULL,
   player_id INT NOT NULL,
   health INT NOT NULL,
   max_health INT NOT NULL,
) ENGINE=InnoDB;

INSERT INTO players (id, name, level, looking_for_a_fight) VALUES(NULL, "Corwin", 1, 1);
INSERT INTO players (id, name, level, looking_for_a_fight) VALUES(NULL, "Mandor", 1, 1);
INSERT INTO players (id, name, level, looking_for_a_fight) VALUES(NULL, "Random", 1, 1);
INSERT INTO players (id, name, level, looking_for_a_fight) VALUES(NULL, "Eric", 1, 1);
INSERT INTO players (id, name, level, looking_for_a_fight) VALUES(NULL, "Caine", 1, 1);
INSERT INTO players (id, name, level, looking_for_a_fight) VALUES(NULL, "Benedict", 1, 1);


Note : C'est la merde, les balises code ne marchent plus.
Répondre
#9
Merci pour ta contribution c'est cool 2 C'est quoi tout ces points d'interrogation je suis perdu u_u

et

"define("DEFAULT_HEALTH", 100);"

what is it ?
Répondre
#10
define c'est pour… définir une constante. Quand tu tombes sur une fonction que tu ne connais pas, il faut avoir le réflexe de la chercher dans la documentation de PHP.

Pour les points d'interrogations, ce sont des placeholders. Ça évite de faire trainer des variables dans tes requêtes. Ici, les fonctions sont fetch, query et insert sont "à imaginer". Mais c'est l'idée du code que tu dois essayer de comprendre. J'ai utilisé des noms explicites pour que tu puisses facilement comprendre à quoi servent les fonctions et comment s'en servir.

Regarde cette fonction, tu comprendras mieux comment ça marche :

<?php
$name = "Romain";
$age = 28;
print("Je m'appelle ? et j'ai ? ans.", $name, $age);

Si ça t'intéresse, je te laisse lire le code et faire le maximum pour le comprendre, et je pourrais répondre à des questions précises.
Répondre


Sujets apparemment similaires...
Sujet Auteur Réponses Affichages Dernier message
  Problême avec une boucle dans un tableau Lindis 8 4 103 01-28-2014, 01:39 PM
Dernier message: Lindis
  [Résolu][Rails] Une incrémentation de sequence SQL dans une boucle for .. in Maz 3 1 901 09-25-2011, 03:06 PM
Dernier message: Sephi-Chan
  Système de panier avec possibilité de réduction Globe 3 1 977 10-04-2010, 05:55 PM
Dernier message: Globe
  [Résolu] Tableau avec boucle fenix 3 1 551 08-23-2008, 12:42 PM
Dernier message: Sephi-Chan
  [Résolu] Performances dans une boucle avec Update zeppelin 11 4 241 03-31-2008, 03:32 PM
Dernier message: Mysterarts



Utilisateur(s) parcourant ce sujet : 1 visiteur(s)