JeuWeb - Crée ton jeu par navigateur
Reflexions sur un système d'achievements - 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 : Reflexions sur un système d'achievements (/showthread.php?tid=6133)

Pages : 1 2 3 4


RE: Reflexions sur un système d'achievements - srm - 22-05-2012

Qu'est ce qui te manque comme information concernant CouchDB ?
http://wiki.apache.org/couchdb/Reference
http://wiki.apache.org/couchdb/HTTP_database_API
http://wiki.apache.org/couchdb/HTTP_view_API
http://wiki.apache.org/couchdb/HTTP_Document_API
etc...


RE: Reflexions sur un système d'achievements - Maks - 22-05-2012

Ils filent un wrapper pour une implémentation Javascript mais pas un seul exemple d'utilisation.
Je m'en suis sorti avec mes fonctions ajax à moi.

Le non support des requêtes CORS c'est un vrai problème aussi, et là pas de doc officielle sur le sujet.

Les vues c'est pas super intuitif, ni le système de requêtes. Le système de MongoDB est bien meilleur mais ça marche pas de la même manière non plus.


RE: Reflexions sur un système d'achievements - Maks - 24-05-2012

Bon quelques bouts de ma solution après plusieurs jours de recherche, si ça intéresse toujours ^^

Sur CouchDB deux databases :

- achievements
- attribution

Exemple de document dans achievement :



{
"_id": "5",
"_rev": "2-e6ef82dc01b1a4b6e2fd2cf2075ccc68",
"name": "Ce n'est pas la taille qui compte...",
"description": "Vous avez battu un bot pour le moins imposant",
"unlock": {
"1": "(target.pv <= 0) && (target.isBot() && target.isBig())"
}
}




{
"_id": "1",
"_rev": "4-b2346d1f821165b754bb4db337a67a9b",
"name": "Bourrin !",
"description": "Vous avez attaqué 10 fois de suite",
"unlock": {
"1": "attaquer",
"2": "attaquer",
"3": "attaquer",
"4": "attaquer",
"5": "attaquer",
"6": "attaquer",
"7": "attaquer",
"8": "attaquer",
"9": "attaquer",
"10": "attaquer"
}
}


Dans attribution



{
"_id": "4b1ec22a351847fd4789a86fb500508a",
"_rev": "1-d8df72aeff2d0d0c0605acfca4bfb9d5",
"pseudo": "Maks",
"idAchievement": "2"
}


Apache :

Décommenter les extensions mod_proxy, rajoute un ProxyPass vers localhost:5984 pour palier à la non gestion des CORS

Client :

Pour le log j'ai fait grosso modo comme pour la page d'avant.

Pour vérifier un succès à débloquer :



/*
* On boucle dans les tous succès
*/

for (var id in this.achievements) {

/*
* On récupère l'objet qui décrit le cheminement pour débloquer le succès
*/

var unlock = this.achievements[id].unlock;

/*
* La variable 'stepCompleted' contient le nombre d'étapes complétées pour débloquer le succès
*/

var stepCompleted = 0;
var i = 0;

/*
* On boucle les étapes dans l'objet 'unlock'
*/

for (var step in unlock) {

/*
* Fix : si le log ne contient pas assez d'action pour boucler jusqu'au bout des étapes (cas où on a commencé à jouer)
* Alors on sort de la boucle
*/

if (!this.queue[i]) {

break;

}

/*
* On récupère l'étape
*/

var unlockStep = unlock[step];

/*
* On récupère les variables loggées qui permettront d'effectuer les tests
*
* Note : on donne une valeur par défaut pour les paramètres non renseignés
*/

var action = this.queue[i].action;
var degats = this.queue[i].params.degats || 0;
var target = this.queue[i].params.target || '';
var run = this.queue[i].params.run || 0;
var soin = this.queue[i].params.soin || 0;

/*
* Si l'action contient un opérateur (=, !, <, >, ect..) alors il faut évaluer grâce à eval() la chaîne de caractère pour savoir si l'étape est complétée
*/

var test1 = unlockStep.indexOf('=');
var test2 = unlockStep.indexOf('!');
var test3 = unlockStep.indexOf('>');
var test4 = unlockStep.indexOf('<');
var test5 = unlockStep.indexOf('&');
var test6 = unlockStep.indexOf('|');
var test7 = unlockStep.indexOf('+');
var test8 = unlockStep.indexOf('-');
var test9 = unlockStep.indexOf('/');
var test10 = unlockStep.indexOf('*');
var test11 = unlockStep.indexOf('%');

/*
* Si l'un des tests est rempli et que l'évalution de l'étape vaut vrai
* Ou que l'étape à effectuer est strictement égale à l'action loggée
* On incrémente la variable 'stepCompleted'
*/

if ((( test1 > -1 || test2 > -1 || test3 > -1 || test4 > -1 || test5 > -1 || test6 > -1
|| test7 > -1 || test8 > -1 || test9 > -1 || test10 > -1 || test11 > -1) && eval(unlockStep))
|| action === unlockStep) {

++stepCompleted

}

++i;

}

/*
* Si le nombre d'étapes complétées est égale aux nombre d'étapes totales 'step'
*
* Note : step est de type String
*/

if (stepCompleted === parseInt(step)) {

this.unlock(id);

}
}


Vous noterez l'utilisation de eval(), fonction très controversée mais bien utile si utilisée sciemment finalement... Pour les paramètres passés en argument, j'utilise le petit trick de passer un object {} afin de pouvoir utiliser des paramètres optionnels sans me soucier de l'ordre.

Dans la méthode unlock() j'ai une logique de présentation, puis :



/*
* On supprime le succès de la liste des achievements
*/

delete this.achievements[id];

/*
* On met à jour CouchDB en ajoutant dans la table attribution un doc contenant le pseudo et l'id de l'achievement
*/

ajax('POST', '/couchDB/attribution', JSON.stringify({ 'pseudo' : joueur.pseudo, 'idAchievement' : id }), { 'Content-type' : 'application/json' });


On POST vers CouchDB la nouvelle entrée. Bon c'est une fonction ajax() maison, mais avec JQuery ça se présenterait de tête :



$.ajax({ 'method' : POST,
'url' : '/couchDB/attribution'
'data' : JSON.stringify({ 'pseudo' : joueur.pseudo, 'idAchievement' : id }),
'headers' : { 'Content-type' : 'application/json' },
'async' : true
});


On note déjà que le scénario en écriture via requête HTTP c'est pas la panacée si y'a beaucoup d'écritures, comme l'a dit Sephi-Chan.

Pour récupérer les données, il faut créer une vue sous CouchDB

Pour récupérer tous les achievements



{
"_id": "_design/get",
"_rev": "1-c3aadc7c4354a3a71d3c87177830f18b",
"views": {
"all": {
"map": "function(doc){ emit(null, doc) }"
}
}
}


Pour récupérer les attributions



{
"_id": "_design/get",
"_rev": "14-31c39a656b122e7a56490f278b77e4e7",
"views": {
"all": {
"map": "function(doc) { emit(doc.pseudo, doc) }"
}
}
}


Notez la clé doc.pseudo qui va être très importante

Le système de requête par vue est pas super intuitif, j'ai mis du temps à faire ce que je voulais faire, surtout par rapport à ce j'ai pu testé sur MongoDB par exemple. Après cet avis n'engage que moi...

Retour côté client



/*
* On récupère la vue contenant les docs de la bdd achievements dans CouchDB
*/

var achievements = JSON.parse(ajax('GET', '/couchDB/achievements/_design/get/_view/all'));

/*
* On boucle selon le nombre d'achievements
*/

for (var i = 0, l = achievements.rows.length; i < l; ++i) {

/*
* L'object est contenu dans la clé 'rows', puis clé 'value'
*/

var achievement = achievements.rows[i].value;

/*
* On supprime les clés reservées à CouchDB
*/

delete achievement._id;
delete achievement._rev;

/*
* On ajoute l'achievement à l'objet 'achievements'
*
* Note : la clé est i + 1 car l'ID commence à 1
*/

this.achievements[i + 1] = achievement;

}

/*
* On récupère la vue contenant les docs de la bdd attribution dans CouchDB en ne récupérant que les clés concernant le joueur (?key="joueur.pseudo")
*/

var attributions = JSON.parse(ajax('GET', '/couchDB/attribution/_design/get/_view/all?key="' + joueur.pseudo + '"'));

/*
* On boucle selon le nombre d'attributions
*/

for (var i = 0, l = attributions.rows.length; i < l; ++i) {

/*
* L'object est contenu dans la clé 'rows', puis clé 'value'
*/

var attribution = attributions.rows[i].value;

/*
* Si l'id de l'achievement est déjà attribué, on le supprime de l'objet 'achievements'
*/

if (attribution.idAchievement in this.achievements) {

delete this.achievements[attribution.idAchievement];

}
}


Voilà pour le feedback.

En conclusion, à mon avis, CouchDB c'est vraiment top quand on veut faire une appli côté client, avec une vue et un model uniquement.
Pour un scénario d'écriture, les requêtes HTTP ça va vite surcharger.

Ca me permet aussi de sortir un peu de mes habitudes, et de découvrir le NoSQL. Ca m'a aussi permis de me rendre compte que j'aurais finalement mieux fait d'adopter MongoDB dès le début. Car pour ce que j'en ai lu, ça serait plus rapide que MySQL. De plus je pourrais sortir du JSON, plutôt que de sortir des lignes MySQL et de remplir un objet avec ensuite... D'autant plus que je n'ai jamais besoin de jointures de dingue, et que j'ai rarement plus de 2 clés étrangères. Javascript, Node.JS, MongoDB, Socket.IO, c'est vraiment un éco-système qui se complète avec un point commun : le JSON.


RE: Reflexions sur un système d'achievements - srm - 24-05-2012

(22-05-2012, 05:58 PM)Maks a écrit : Ils filent un wrapper pour une implémentation Javascript mais pas un seul exemple d'utilisation.
Je m'en suis sorti avec mes fonctions ajax à moi.

Le non support des requêtes CORS c'est un vrai problème aussi, et là pas de doc officielle sur le sujet.

Les vues c'est pas super intuitif, ni le système de requêtes. Le système de MongoDB est bien meilleur mais ça marche pas de la même manière non plus.

C'est quoi CORS ? :o
Et vu que ce sont des banales requêtes HTTP à faire, à toi de coder en Javascript avec des appels Ajax comme tu veux, ils font des clients pour plein de langages, mais je m'en sers quasiment jamais tant c'est inutile.

Les requêtes HTTP si tu as beaucoup d'update peut sembler un frein, en pratique pas du tout, à part si tu as des besoins de performances accrus (mais on est pas Facebook ou je ne sais qui ^^)
Tu peux faire des bulk opérations :
http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API

A la fois récupérer plusieurs documents avec une requête, ou en modifier/insérer plusieurs avec une requête.
Ok pour CORS, il te suffit de mettre un Apache en Proxy qui ajoute l'header et rulez Smile
De toute façon ils recommandent toujours de délivrer CouchDB via une autre couche, par exemple un Apache, un Varnish ou autre.


RE: Reflexions sur un système d'achievements - Maks - 24-05-2012

Oui dans cas cas précis, à moins d'avoir 10 000 joueurs, ma requête POST ne devrait pas peser très lourd ^^

Les requêtes CORS c'est une sécurité quand tu veux accéder à des données situées sur un autre serveur, ou un autre port via AJAX. Ici donc le port 5984. Mais pas moyen d'ajouter un header à CouchDB donc oui via Apache j'ai rajouté comme je l'ai dis dans le feedback :

Code :
ProxyPass /couchDB http://127.0.0.1:5984/

D'autant plus que Opera ne supporte pas les CORS (alors que même IE8 peut le faire !!), la solution du proxy est vraiment sympa.