EssentialsX

EssentialsX

2M Downloads

IAsyncTeleport#teleport not properly implemented to handle passed CompletableFuture instances for players without the bypass permission.

qruet opened this issue ยท 1 comments

commented

Type of bug

Other unexpected behaviour

/ess version output

2.18.2.0

Server startup log

https://paste.gg/p/anonymous/6f60d0e22fb7422597adc58b4c56fbc7

EssentialsX config files

https://paste.gg/p/anonymous/fed6c8e0005d42209343b677e73d4a6f

Error log (if applicable)

No response

Bug description

I am a plugin developer who has hooked into the Essential's API to call teleportation functions so that my plugin may integrate with the already existing teleportation cooldown system. The following code snippet:

CompletableFuture<Boolean> future = new CompletableFuture<>();
future.exceptionally((e) -> {
      LanguageIO.processMessage("Generics.Error.Critical Server Error", player, msg -> msg = msg.replaceAll("%error%", "VisitCmd Essentials Teleport"));
       e.printStackTrace();
       return false;
});

future.thenAccept((e) -> {
      island.addVisitor(player);
});
((IAsyncTeleport) teleport).teleport(to, null, PlayerTeleportEvent.TeleportCause.COMMAND, future);

If a player does not have the permission essentials.teleport.timer.bypass then the passed CompletableFuture#thenAccept is never called. Digging into the teleport function, there is no implementation of a loop, therefore unless the teleportation is instantaneous the passed instance is lost.

Steps to reproduce

  1. Debug:
CompletableFuture<Boolean> future = new CompletableFuture<>();
future.thenAccept((e) -> {
      Bukkit.broadcastMessage("#thenAccept");
});
((IAsyncTeleport) teleport).teleport(to, null, PlayerTeleportEvent.TeleportCause.COMMAND, future);
  1. Remove the essentials.teleport.timer.bypass permission from a player
  2. Have them teleport somewhere with a cooldown
  3. The debug statement will never been called.
  4. Add the essentials.teleport.timer.bypass permission to a player
  5. They will bypass the cooldown and the debug statement is called on teleport.

Expected behaviour

CompletableFuture#thenAccept should be called after a player has teleported regardless of the cooldown.

Actual behaviour

CompletableFuture#thenAccept does not work if the teleportation is not instantaneous.

A quick and dirty fix was to simply implement a BukkitRunnable loop that iterated once every tick and called on the AsyncTeleport#cooldown function until it returned true. Once the function returns true, CompletableFuture#complete is called.

commented

Confirmed on:

[00:13:43 INFO]: Server version: 1.16.5-R0.1-SNAPSHOT git-Paper-542 (MC: 1.16.5)
[00:13:43 INFO]: EssentialsX version: 2.19.0-dev+96-1cf2b11

Minimal reproduction steps:

  1. Set teleport-delay in the config to something larger than 0.
  2. Make sure player doesn't have the essentials.teleport.timer.bypass permission.
  3. Call AsyncTeleport#teleport for player and observe that the passed future is never completed.