Aller au contenu

Échanges

tilttek/exchange

Quand l'application le permet, les membres échangent des objets de leurs collections (des cartes virtuelles, les dés d'un jeu) et peuvent ajouter des points d'un côté ou de l'autre.

L'inventaire

Les objets vivent dans les collections de l'application. Trades les atteint par un Inventory que l'application implémente : à qui appartient un objet, s'il peut changer de mains (un objet gagné par une quête peut rester lié à son membre), et les déplacements d'un échange accepté, tous ou aucun. InMemoryInventory garde des objets en mémoire, pour les tests et pour essayer les échanges.

Une offre

Trades::offer($key, $proposer, $recipient, $offered, $requested, $offeredPoints, $requestedPoints, $currency) fait une offre : les objets de l'auteur contre ceux du destinataire, au moins un objet en tout, au plus 50 de chaque côté, aucun objet deux fois. Les gardes sont vérifiées (Gardes). Chaque objet doit appartenir à celui qui le donne et être échangeable, et chaque objet offert doit être libre. Dès l'offre, le stockage retient les objets offerts : aucune autre offre ne peut les offrir avant que celle-ci se ferme. Les objets demandés ne sont pas retenus ; plusieurs offres peuvent demander le même objet.

Une offre attend sa réponse pendant sa durée de vie, sept jours à moins que Trades ne reçoive une autre durée ISO 8601.

Les réponses

  • Accepter, par le destinataire : la propriété, le caractère échangeable et les retenues sont vérifiés de nouveau, ainsi que les deux soldes selon les points qu'ils ajoutent. Ensuite, dans une seule transaction, l'offre se ferme, les points se déplacent par des transferts du grand livre sous exchange:offer:<key>:offered et :requested, et l'inventaire déplace les objets. Quand une étape échoue, rien ne bouge.
  • Refuser, par le destinataire, et retirer, par l'auteur : l'offre se ferme et ses objets redeviennent libres.
  • Contrer, par le destinataire : une offre à lui, adressée à l'auteur, qui nomme l'offre à laquelle elle répond. Dans une seule transaction, la première offre se ferme comme contrée et la contre-offre s'ouvre. Elle est vérifiée comme une offre avant que la première se ferme.

Un membre qui n'est pas le bon côté est refusé avec Refusal::NotParty. Une réponse à une offre qui n'est plus ouverte est refusée avec ExchangeClosed, et une réponse après la durée de vie fait d'abord expirer l'offre. La même réponse donnée deux fois retourne l'offre telle quelle ; la même clé pour une autre offre est un ExchangeConflict.

$trades->offer('trade:812', $ada, $grace, offered: [$redDragon], requested: [$greenElf], offeredPoints: 20);
$trades->counter('trade:812', $grace, 'trade:813', offered: [$greenElf], requested: [$redDragon, $blueKnight]);
$trades->accept('trade:813', $ada);

L'expiration et le journal

expireDue() fait expirer les offres qui ont dépassé leur durée de vie et libère leurs objets : c'est la tâche qu'exécute un planificateur. ofMember($member) donne les offres qu'un membre a faites ou reçues, dans l'ordre où elles ont été faites : le journal de ses échanges. made($member) compte les offres qu'un membre a faites sur la période de l'allocation.

Une seule transaction

Les étapes d'une acceptation et d'une contre-offre passent par une Transaction. ImmediateTransaction les exécute à mesure : avec les stockages en mémoire, Trades vérifie ce qu'il peut avant sa première écriture. Un stockage sur une base de données les exécute dans une de ses transactions, comme le fait le DbalTransaction du bundle ; le grand livre de points et l'inventaire y prennent part quand ils sont gardés sur la même connexion.