JeuWeb - Crée ton jeu par navigateur
[Résolu]Soucis avec gestion boutique - 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]Soucis avec gestion boutique (/showthread.php?tid=3961)



[Résolu]Soucis avec gestion boutique - rwk - 11-05-2009

Bonjour,

Ca fait un petit bail que je n'ai pas posté ici.
J'ai un soucis, j'vais essayer d'expliquer.

Sur mon jeu, un joueur peut avoir jusqu'à 2 personnages (appelons-les P1 et P2).
Pour acheter un objet dans une boutique, il faut être collé à une case adjacente de cette dernière. (la carte est un damier normal, les positions sont en X/Y. )

Imaginons que P1 entre dans la boutique. Il est à portée de celle ci, donc tout s'affiche normalement.

Le joueur ouvre un nouvel onglet dans son navigateur web, il se connecte à son P2.
Il revient sur la fenetre de la boutique (instanciée par P1). S'il achète un objet, il va y avoir un flux d'erreur avec une redirection sur la page avec "vous n'êtes pas à portée de la boutique" ... sauf que l'objet sera quand même envoyé dans l'inventaire de P2... alors que c'est P1 qui l'a acheté...

En gros, y a un meli mélo dans ma session...

Voici les parties du script qui nous intéressent

Code PHP :
<?php
/*
Fichier : action sur batiment.php
Fait par :xxxx
Date : 06/04/2009 18h30
Description :
----------
modifications :

*/

/*************************************/
/***** Anti-hacking / Insclusion *****/
/*************************************/

// je définis une variable pour prévenir l'éventuel hacking
define('INC_OK',true);

//les inclusions
require("libs/template.class.php");
require(
"libs/drsql.class.php");
require(
"libs/Joueur.class.php");
require(
"libs/config.inc.php");
require(
"libs/pj.class.php");
require(
"libs/batiment.class.php");
require(
"libs/inventaire.class.php");
require(
"libs/sort.class.php");
require(
'libs/messagerie.class.php');

$TPL = new template();
$DB = new DrSql($host,$user,$pass,$bdd);
$Joueur = new Joueur($TPL); //IL YA UN SESSION START DANS LE CONSTRUCTEUR DE LA CLASSE
$PJ = new pj($DB,$_SESSION['P_Id']);
$Messagerie = new messagerie($DB, $TPL, $PJ, true);

/*************************/
/***** Les variables *****/
/*************************/

if(isset($_GET['type']))
$Type = htmlspecialchars($_GET['type'],ENT_QUOTES);

if(isset(
$_GET['a']))
$Action = htmlspecialchars($_GET['a'],ENT_QUOTES);
elseif(isset(
$_POST['a']))
$Action = htmlspecialchars($_POST['a'],ENT_QUOTES);

/*******************************/
/***** ACTIONS DE SESSIONS *****/
/*******************************/

$Joueur->EtrePageSecurisee(true,'index.php'); //si le joueur n'est pas reconnu comme connecté, on redirige sur index.php

/*****************/
/***** DEBUT *****/
/*****************/

/***** actions sur boutique *****/
if($Type == 'boutique')
{
$PosX = htmlspecialchars($_GET['x'],ENT_QUOTES);
$PosY = htmlspecialchars($_GET['y'],ENT_QUOTES);
$QuartierId= htmlspecialchars($_GET['q'],ENT_QUOTES);

$Batiment = new batiment($DB,'',$PosX,$PosY,$QuartierId);

$TPL->assign(
array
(
'Type' => 'boutique',
'Distance' => $Batiment->EtreAPortee($PJ->Pe_Caracs->Pe_PosX,$PJ->Pe_Caracs->Pe_PosY,$PJ->Pe_Caracs->Qu_Id),
'BaId' => $Batiment->BatimentInfos->Ba_Id
)
);

//affichage
$TPL->parse('actions_sur_batiment.tpl');
}
elseif(
$Action == 'entrer')
{
//affichage
$BoutiqueId = htmlspecialchars($_GET['boutique_id'],ENT_QUOTES);
$Batiment = new batiment($DB,$BoutiqueId);

//on vérifie la distance
if(!$Batiment->EtreAPortee($PJ->Pe_Caracs->Pe_PosX,$PJ->Pe_Caracs->Pe_PosY,$PJ->Pe_Caracs->Qu_Id))
header("location:jouer.php?Erreur=Ce batiment n'est pas à votre portée");

//on assigne les valeurs de la page
$TPL->assign(
array
(
'BoutiqueNom' => $Batiment->BatimentInfos->Ba_Nom,
'BoutiqueDesc' => $Batiment->BatimentInfos->Ba_Description,
'Pe_Thor' => $PJ->Pe_Caracs->Pe_Thor
)
);

//on assigne l'infobulle
//assignation bubulle
$TPL->assign("ToolTips",'<script type="text/javascript">
window.onload=function(){enableTooltips("boutique")};
</script>
'
);

//on affichage tous les éléments de la boutique
$StockObjets = $Batiment->DonnerObjetsStock();

foreach(
$StockObjets AS $Objet)
{

//Si un objet à des specificités, on les affiche
if($Objet['type'] == "Parchemin" AND !empty($Objet['typebis']))
{
$Sort = new sort($DB, $Objet['typebis']);
$Specificite = 'Classe - '.$Sort->DonnerClasse();
}
else
$Specificite = '-';

$TPL->assign_block(
'Afficher_ObjetsBoutique',
array
(
'Ob_Nom' => $Objet['nom'],
'Ob_Prix' => $Objet['prix'],
'Ob_Stock' => $Objet['stock'],
'Ob_Specif' => $Specificite,
'Ob_Desc' => $Objet['desc'],
'Ob_Image' => '<img src="'.$Objet['image'].'" alt="'.$Objet['nom'].'" height="40" width="40" />',
'Ob_Id' => $Objet['id'],
'Bo_Id' => $BoutiqueId
)
);

for(
$i=1; $i <= $Objet['stock']; $i++)
{
$TPL->assign_block(
'Afficher_ObjetsBoutique.Afficher_Stockb',
array
(
'i' => $i
)
);
}

}

//on affiche tous les éléments de l'inventaire
$Inventaire = new inventaire($DB, $PJ);

$InventaireObjet = $Inventaire->DonnerListeObjets();

foreach(
$InventaireObjet['id'] AS $Objet)
{
//on récupère les infos de l'objet
$ObjetInfos = $Inventaire->DonnerObjetInventaireInfos($Objet);

if(
$Batiment->PouvoirAcheterObjet($ObjetInfos->Ob_Type))
{
//test sur les pack de munitions qu'on ne vend que par 10
if($ObjetInfos->Ob_Type == 'Munition')
{
$Quantite = FLOOR($ObjetInfos->In_Quantite/10);
}
else
$Quantite = $ObjetInfos->In_Quantite;


$TPL->assign_block(
'Afficher_ObjetsInventaire',
array
(
'In_Affichage' => true,
'In_Nom' => $ObjetInfos->Ob_Nom,
'In_Prix' => $ObjetInfos->Ob_PrixVente,
'In_Stock' => $Quantite,
'In_Desc' => $ObjetInfos->Ob_Description,
'In_Image' => '<img src="'.$ObjetInfos->Ob_Icone.'" alt="'.$ObjetInfos->Ob_Nom.'" height="40" width="40" />',
'In_Id' => $ObjetInfos->In_Id,
'Bo_Id' => $BoutiqueId
)
);


for(
$i=1; $i <= $Quantite; $i++)
{
$TPL->assign_block(
'Afficher_ObjetsInventaire.Afficher_Stocki',
array
(
'j' => $i
)
);
}
}
else
{
$TPL->assign_block(
'Afficher_ObjetsInventaire',
array
(
'In_Affichage' => false,
'In_Nom' => null,
'In_Prix' => null,
'In_Stock' => null,
'In_Desc' => null,
'In_Image' => null,
'In_Id' => null,
'Bo_Id' => null
)
);

$TPL->assign_block(
'Afficher_ObjetsInventaire.Afficher_Stocki',
array
(
'j' => null
)
);
}
}

//assignation lien pour revenir sur son personnage : OUI
$TPL->assign('retour', true);

//assignation du feedback
if(isset($_GET['Alerte']))
$TPL->assign('Alerte', htmlspecialchars($_GET['Alerte'],ENT_QUOTES));
else
$TPL->assign('Alerte',null);

$TPL->parse('boutique.tpl');
}
elseif(
$Action == "acheter")
{
$BoutiqueId = htmlspecialchars($_GET['boutique_id'],ENT_QUOTES);
$ObjetId = htmlspecialchars($_GET['objet_id'],ENT_QUOTES);
$Quantite = htmlspecialchars($_POST['nombre'],ENT_QUOTES);

$Batiment = new batiment($DB,$BoutiqueId);
$Inventaire = new inventaire($DB, $PJ);

//on vérifie la distance
if(!$Batiment->EtreAPortee($PJ->Pe_Caracs->Pe_PosX,$PJ->Pe_Caracs->Pe_PosY,$PJ->Pe_Caracs->Qu_Id))
header("location:jouer.php?Erreur=Ce batiment n'est pas à votre portée");

//on vérifie si la boutique possède bien l'objet en stock
if(!$ObjetInfos = $Batiment->EtreEnStock($ObjetId,$Quantite,$BoutiqueId))
header("location:actions_sur_batiment.php?a=entrer&Alerte=Vérifiez la disponibilité de ce produit.");

$Prix = $ObjetInfos->BAS_PrixAchat * $Quantite;

//on vérifie que le joueur peut payer.
if($PJ->Pe_Caracs->Pe_Thor < $Prix)
header("location:actions_sur_batiment.php?a=entrer&boutique_id={$BoutiqueId}&Alerte=Vous n'avez pas assez de Thor pour payer.");

//il peut payer, on retire les sous du joueur, on retire l'objet de la boutique et on ajoute l'objet à l'équipement
$Thor = $PJ->Pe_Caracs->Pe_Thor - $Prix;
$DB->query("UPDATE Personnage SET Pe_Thor={$Thor} WHERE Pe_Id='{$PJ->PjId}'");

if(
$ObjetInfos->Ob_Type == 'Munition')
$QuantiteInventaire = $Quantite * 10;
else
$QuantiteInventaire = $Quantite;

$Batiment->RetirerStock($ObjetId,$Quantite,$BoutiqueId);
$Inventaire->AjouterObjet($ObjetId,$QuantiteInventaire);

header("location:actions_sur_batiment.php?a=entrer&boutique_id=".$BoutiqueId);
}

/***************/
/***** FIN *****/
/***************/
?>



RE: Soucis avec gestion boutique - keke - 11-05-2009

Coucou,

Ca n'a rien avoir, mais les GET, ça n'est pas très recommandé. Ca peut attirer des bugs et des failles de sécurité.

Sinon, le message d'erreur est-il ?
"Ce batiment n'est pas à votre portée" ?

Dans ton code, tu fais les tests suivant en batterie.
//on vérifie la distance
//on vérifie si la boutique possède bien l'objet en stock
//on vérifie que le joueur peut payer.

Mais si le test est négatif, tu continues ta page... il faudrait rajouter la fonction exit(); dans ton code. Je modifierais ton code ainsi.

Code :
//on vérifie la distance
  if(!$Batiment->EtreAPortee($PJ->Pe_Caracs->Pe_PosX,$PJ->Pe_Caracs->Pe_PosY,$PJ->Pe_Caracs->Qu_Id))
{
    header("location:jouer.php?Erreur=Ce batiment n'est pas à votre portée");
    exit ();
}

  //on vérifie si la boutique possède bien l'objet en stock
  if(!$ObjetInfos = $Batiment->EtreEnStock($ObjetId,$Quantite,$BoutiqueId))
{
    header("location:actions_sur_batiment.php?a=entrer&Alerte=Vérifiez la disponibilité de ce produit.");
    exit ();
}
  $Prix = $ObjetInfos->BAS_PrixAchat * $Quantite;

  //on vérifie que le joueur peut payer.
  if($PJ->Pe_Caracs->Pe_Thor < $Prix)
{
    header("location:actions_sur_batiment.php?a=entrer&boutique_id={$BoutiqueId}&Alerte=Vous n'avez pas assez de Thor pour payer.");
    exit ();
}



RE: Soucis avec gestion boutique - Ter Rowan - 11-05-2009

A ma connaissance, tu ne peux pas savoir grâce à la session si le joueur, ayant ses deux personnages "connectés" chacun sur une page différente, quel personnage lance l'action sur une des pages

tu ferais mieux,

soit de refuser à ce que deux personnages soient joués simultanément,

soit de mettre un champ de formulaire où tu stockerais, dans chaque page, l'id du personnage que le joueur manipule (ou quelquechose d'équivalent, ex via de l ajax), id que tu récupèrerais côté serveur pour lancer tes calculs

mais dans tous les cas il te faut, côté serveur contrôler les id des personnages, pour chaque action


RE: Soucis avec gestion boutique - rwk - 11-05-2009

Bonjour,

J'ai toujours pensé que la fonction header() était "implicitement" une "fin de script" dans le type header("location:xxx");
Je sais que cette fonction est souvent aussi utilisé pour les types mime de fichier.

Alors si un header("location:xxx") ne termine pas le script comme un exit... effectivement, y a un soucis venant de moi. (Et ça fait 5ans que je bosse comme ça ! C'te honte xD).
J'vais tester avec un Exit(); ! S'il s'avère que ça règle le problème, alors ben c'est cool.
J'avais déjà eu affaire parfois à des problèmes similaires de scripts qui continuent après un header(); et j'n'avais pas spécialement tilté...

Du coup, je viens de tester le "Ce bâtiment n'est pas à votre portée" correspond à la ré-entrance dans le Action==Entrer. Donc ce n'est effectivement pas celui de la partie Action==Acheter.

J'ai testé en mettant le code suivant dans la partie Action==Acheter, ça ne change rien.

Code PHP :
<?php 
//on vérifie la distance
if(!$Batiment->EtreAPortee($PJ->Pe_Caracs->Pe_PosX,$PJ->Pe_Caracs->Pe_PosY,$PJ->Pe_Caracs->Qu_Id))
{
header("location:jouer.php?Erreur=Ce batiment n'est pas à votre portée");
exit ();
}

Ca veut donc dire qu'il considère que le personnage est à portée. Que seulement l'ID de session change (puisqu'il n'y avait pas de refresh de ladite page et donc de la session) et que donc à la ré-entrance, l'ID a changé, le personnage n'est plus à portée...

Donc la seule solution que je vois (peut-être y en a-t-il d'autres) est effectivement d'envoyer l'ID de celui qui fait l'action par le biais d'un champs Hidden dans le formulaire et de comparer ensuite l'ID de l'envoi et l'ID du perso en cours.
Mais j'ai peur que ça ne change rien vu que lors de l'achat la session ne sera pas régénérée...
Oula, je viens de dire une énorme connerie avec ma "régénération" de session.
En postant le form, et en pointant sur le script, je la régénère forcément.

Faut que j'arrête le vin de table moi ... ça me réussit pas sur mes heures de boulot !


RE: Soucis avec gestion boutique - Allwise - 11-05-2009

Pour compléter la pensée de keke :
l'envoie de l'entête location avec la fonction header cesse l'exécution de la page et redirige l'internaute. A priori, pas besoin de faire un exit après, sauf si y a un risque que des données soient écrites avant l'appel de header(). Dans ce cas-là, exit est effectivement plus sûr.

Sinon, pareil que Ter Rowan :
Si tu n'utilises que les variables session pour identifier les personnages de tes joueurs ($_SESSION['P_Id']) ça ne pourra pas fonctionner car tous les onglets d'un même navigateur partagent la même session. Il faudrait soit que l'internaute utilise 2 navigateurs différents, soit que l'identifiant du personnage soit transmis autrement : GET ou POST. Mais dans ce cas-là, il faudra être vigilant et vérifier que le joueur qui possède ces persos soit bien connecté, et que c'est bien lui qui essaie d'instancier les persos en question.


RE: Soucis avec gestion boutique - rwk - 11-05-2009

Ah bon, alors la fonction Header marche bien comme je pensais, donc pas besoin du exit(); =) J'suis rassuré, m'enfin j'aurais pu zapper une MAJ.

Donc, j'ai rajouté un champs HIDDEN dans le formulaire.
Et j'ai fais un test entre le Champs Hidden et la valeur de la variable dans la session.

Si elles diffèrent : BOOM

Code PHP :
<?php 
$Acteur
= htmlspecialchars($_POST['P_Id'],ENT_QUOTES);

$Batiment = new batiment($DB,$BoutiqueId);

//on vérifie que c'est bien le personnage qui poste qui agit
if($Acteur != $PJ->PjId)
{
exit(
"Usurpation d'ID");
}

Maintenant, je sais que c'n'est peut être pas la meilleure façon de procéder. A priori, il est possible de modifier la valeur d'une variable postée (même hidden)... Donc je suppose qu'un joueur connaissant les ID de son perso et avec un logiciel adapté pourra contourner le truc... (enfin on m'a dit ça... j'ai jamais vraiment trouvé comment faire xD)

M'enfin, pour les 99% des joueurs qui essayent de jouer "normalement"... ça devrait le faire.

J'vais pas m'amuser à crypter l'ID de l'user avec un algo de mon cru quand même avec un cryptosystème, ça va devenir chiant xD

Bon ben si on part du principe que le problème est résolu, je vous remercie à tous pour vos interventions et pistes. J'vais repasser plus souvent par ici.


RE: Soucis avec gestion boutique - pascal - 11-05-2009

(11-05-2009, 02:49 PM)rwk a écrit : Donc, j'ai rajouté un champs HIDDEN dans le formulaire.
Et j'ai fais un test entre le Champs Hidden et la valeur de la variable dans la session.

Si elles diffèrent : BOOM

il faut aussi re-vérifier que le joueur est à la bonne distance de la boutique, sinon on peut encore tricher.

A+

Pascal


RE: [Résolu]Soucis avec gestion boutique - rwk - 11-05-2009

Oui, aucun soucis, c'est la ligne du dessous Wink


RE: [Résolu]Soucis avec gestion boutique - Ter Rowan - 11-05-2009

pour moi tu devrais plutôt tester ainsi :

est ce que l'id du personnage appartient bien au joueur de la session
est ce que ce personnage peut faire l'action


du coup même si le joueur arrive à envoyer l'id d'un autre personnage, cela fera boom

par contre, si il a ouvert deux onglets / fenêtres basé(e)s sur la même session, pour jouer ses deux personnages, cela marchera dans tous les cas (tu ne testes pas le personnage mais le joueur en session)

alors qu en testant perso(hidden) versus perso(session) tu te prives de cette fonctionnalité (sans compter le message de retour incompréhensible pour le joueur honnête)