Toutes les notes

, depuis Lilies

L'événement Paper qui arrive déjà annulé

Pourquoi sept de mes gestionnaires de plugin Minecraft ignoraient chaque clic dans le vide, et pourquoi 5 tests unitaires au vert ne l'ont pas remarqué.

Dans la réécriture en Java de Lilies, un clic droit avec une tulipe dans le vide était censé la mettre en banque. Il ne se passait rien. Le module avait cinq tests unitaires et tous passaient.

À quoi ressemblait le gestionnaire

@EventHandler(ignoreCancelled = true)
public void onUse(PlayerInteractEvent e) {
    if (e.getAction() != Action.RIGHT_CLICK_AIR) return;
    // bank the tulip ...
}

ignoreCancelled = true est une habitude dans les plugins Bukkit : si un autre plugin a annulé l'événement, on l'ignore. Ça semble inoffensif. Ici, ça a rendu tout le gestionnaire inactif.

Pourquoi il ne s'exécutait jamais

Pour PlayerInteractEvent, isCancelled() n'est pas un drapeau séparé. Il renvoie vrai quand useInteractedBlock() vaut DENY. Quand on clique dans le vide, il n'y a pas de bloc, et Paper construit l'événement avec DENY déjà défini. Donc chaque clic dans le vide arrive déjà annulé, et un gestionnaire qui ignore les événements annulés n'en voit jamais un.

Les tests unitaires ne pouvaient pas l'attraper. Ils construisaient l'événement à la main et appelaient la méthode du gestionnaire directement, donc l'événement n'était jamais annulé et la vérification ignoreCancelled ne s'exécutait jamais du tout.

Comment ça a été trouvé

Un test de démarrage. Il lance un vrai serveur avec tous les jars de plugins chargés et déclenche de vrais événements via le gestionnaire de plugins, le même chemin que le clic d'un joueur. Il a échoué dès la première exécution. Chercher dans le reste de la suite a trouvé le même drapeau sur sept gestionnaires, y compris celui qui ramasse les gains d'argent.

La correction

@EventHandler
public void onUse(PlayerInteractEvent e) {
    if (e.getAction() == Action.RIGHT_CLICK_BLOCK && e.isCancelled()) return;
    // ...
}

Retirer le drapeau et dire ce que l'on veut vraiment. Un clic sur bloc refusé ne fait toujours rien, et les clics dans le vide passent.

La leçon

Un test qui appelle un gestionnaire directement saute tout ce que le framework fait avant que le gestionnaire ne s'exécute. Gardez au moins un test qui passe par la vraie porte d'entrée.