En la reescritura en Java de Lilies, hacer clic derecho con un tulipán al aire debía depositarlo. No hacía nada. El módulo tenía cinco pruebas unitarias y todas pasaban.
Cómo era el manejador
@EventHandler(ignoreCancelled = true)
public void onUse(PlayerInteractEvent e) {
if (e.getAction() != Action.RIGHT_CLICK_AIR) return;
// bank the tulip ...
}
ignoreCancelled = true es una costumbre en los plugins de Bukkit: si otro plugin canceló el
evento, omitirlo. Parece inofensivo. Aquí convirtió todo el manejador en código muerto.
Por qué nunca se ejecutaba
Para PlayerInteractEvent, isCancelled() no es una bandera separada. Devuelve true
cuando useInteractedBlock() es DENY. Cuando haces clic al aire no hay bloque, y
Paper construye el evento con DENY ya establecido. Así que cada clic al aire llega ya
cancelado, y un manejador que ignora eventos cancelados nunca ve ninguno.
Las pruebas unitarias no podían detectarlo. Construían el evento a mano y llamaban directamente al método
del manejador, así que el evento nunca estaba cancelado y la comprobación de ignoreCancelled
nunca se ejecutaba.
Cómo se encontró
Una prueba de arranque. Inicia un servidor real con todos los jars de plugins cargados y dispara eventos reales a través del gestor de plugins, la misma ruta que sigue el clic de un jugador. Falló en la primera ejecución. Buscar en el resto de la suite encontró la misma bandera en siete manejadores, incluido el que recoge las gotas de dinero.
La solución
@EventHandler
public void onUse(PlayerInteractEvent e) {
if (e.getAction() == Action.RIGHT_CLICK_BLOCK && e.isCancelled()) return;
// ...
}
Elimina la bandera y di lo que quieres decir. Un clic de bloque denegado sigue sin hacer nada, y los clics al aire pasan.
La lección
Una prueba que llama a un manejador directamente se salta todo lo que hace el framework antes de que el manejador se ejecute. Mantén al menos una prueba que entre por la puerta principal real.