Todas las notas

, de Lilies

El evento de Paper que llega precancelado

Por qué siete de mis manejadores de plugins de Minecraft ignoraban cada clic al aire, y por qué 5 pruebas unitarias en verde no lo notaron.

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.