Skip to content

Commit

Permalink
Cj/async teleport (#34)
Browse files Browse the repository at this point in the history
* implement teleportAsync compatibility

* try to use Paper's built in teleportAsync even if we use bukkit compatibility

* grammar fix

* increment version to 0.6.0
  • Loading branch information
CJCrafter authored Oct 8, 2024
1 parent 2ce76de commit 8e96143
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 5 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Spigot's scheduler, `BukkitRunnable`. This adds support for practically any serv
<dependency>
<groupId>com.cjcrafter</groupId>
<artifactId>foliascheduler</artifactId>
<version>0.5.0</version>
<version>0.6.0</version>
</dependency>
```

Expand All @@ -37,7 +37,7 @@ Spigot's scheduler, `BukkitRunnable`. This adds support for practically any serv
<dependency>
<groupId>com.cjcrafter</groupId>
<artifactId>foliascheduler</artifactId>
<version>0.5.0</version>
<version>0.6.0</version>
</dependency>
</dependencies>

Expand Down Expand Up @@ -76,7 +76,7 @@ repositories {
}

dependencies {
implementation("com.cjcrafter:foliascheduler:0.5.0")
implementation("com.cjcrafter:foliascheduler:0.6.0")
}
```

Expand All @@ -97,7 +97,7 @@ repositories {

dependencies {
// TODO add your version of Spigot/Paper here
implementation("com.cjcrafter:foliascheduler:0.5.0")
implementation("com.cjcrafter:foliascheduler:0.6.0")
}

// See https://github.com/Minecrell/plugin-yml
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group = "com.cjcrafter"
version = "0.5.0"
version = "0.6.0"

val githubOwner = "CJCrafter"
val githubRepo = "FoliaScheduler"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.CompletableFuture;

@ApiStatus.Internal
public class FoliaServer implements ServerImplementation {

Expand Down Expand Up @@ -88,4 +91,9 @@ public void cancelTasks() {
globalScheduler.cancelTasks();
asyncScheduler.cancelTasks();
}

@Override
public @NotNull CompletableFuture<Boolean> teleportAsync(@NotNull Entity entity, @NotNull Location location, @NotNull PlayerTeleportEvent.TeleportCause cause) {
return entity.teleportAsync(location, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@
import com.cjcrafter.foliascheduler.GlobalSchedulerImplementation;
import com.cjcrafter.foliascheduler.RegionSchedulerImplementation;
import com.cjcrafter.foliascheduler.ServerImplementation;
import com.cjcrafter.foliascheduler.util.MethodInvoker;
import com.cjcrafter.foliascheduler.util.ReflectionUtil;
import com.cjcrafter.foliascheduler.util.WrappedReflectiveOperationException;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;

@ApiStatus.Internal
public class BukkitServer implements ServerImplementation {
Expand All @@ -21,12 +31,21 @@ public class BukkitServer implements ServerImplementation {
private final @NotNull BukkitRegionScheduler region;
private final @NotNull BukkitAsyncScheduler async;

// On Paper servers, the teleportAsync method is supported back to 1.13
private @Nullable MethodInvoker teleportAsyncMethod;

@ApiStatus.Internal
public BukkitServer(@NotNull Plugin owningPlugin) {
this.owningPlugin = owningPlugin;
this.sync = new BukkitSyncScheduler(owningPlugin);
this.region = new BukkitRegionScheduler(owningPlugin);
this.async = new BukkitAsyncScheduler(owningPlugin);

try {
teleportAsyncMethod = ReflectionUtil.getMethod(Entity.class, "teleportAsync", Location.class, PlayerTeleportEvent.TeleportCause.class);
} catch (WrappedReflectiveOperationException ignore) {
// This happens on older paper servers, or plain Spigot servers
}
}

@Override
Expand Down Expand Up @@ -88,4 +107,28 @@ public boolean isOwnedByCurrentRegion(@NotNull Entity entity) {
public void cancelTasks() {
owningPlugin.getServer().getScheduler().cancelTasks(owningPlugin);
}

@Override
public @NotNull CompletableFuture<Boolean> teleportAsync(@NotNull Entity entity, @NotNull Location location, PlayerTeleportEvent.@NotNull TeleportCause cause) {
// Check if the teleportAsync method is supported (Paper 1.13+)
if (teleportAsyncMethod != null) {
Object result = teleportAsyncMethod.invoke(entity, location, cause);
return (CompletableFuture<Boolean>) Objects.requireNonNull(result);
}

// Fallback to the synchronous teleport method, executed 1 tick later on the main thread
// to allow this method to be called asynchronously.
CompletableFuture<Boolean> future = new CompletableFuture<>();
entity(entity).run(task -> {
try {
entity.teleport(location);
future.complete(true);
} catch (Throwable ex) {
owningPlugin.getLogger().log(Level.SEVERE, "Failed to teleport entity", ex);
future.complete(false);
}
});

return future;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.CompletableFuture;

public interface ServerImplementation {

/**
Expand Down Expand Up @@ -139,4 +142,35 @@ public interface ServerImplementation {
* @see <a href="https://github.com/CJCrafter/FoliaScheduler/issues/28">Issue #28</a>
*/
void cancelTasks();

/**
* Teleports an entity to a location. If chunks need to be loaded, those chunks will be loaded
* asynchronously. Shortcut for {@link #teleportAsync(Entity, Location, PlayerTeleportEvent.TeleportCause)}.
*
* @param entity The entity to teleport
* @param location The location to teleport the entity to
* @return A future that completes when the entity is teleported, or the teleport fails
*/
default @NotNull CompletableFuture<Boolean> teleportAsync(@NotNull Entity entity, @NotNull Location location) {
return teleportAsync(entity, location, PlayerTeleportEvent.TeleportCause.PLUGIN);
}

/**
* Teleports an entity to a location. If chunks need to be loaded, those chunks will be loaded
* asynchronously.
*
* <p>The returned {@link CompletableFuture future} will complete when the entity is teleported,
* or the teleport fails. If the teleport fails, the future will complete with a value of
* {@code false}.
*
* <p>Note that this method is only asynchronous on Paper servers, versions 1.13 and later. On
* other servers, this method will be delegated to a synchronous task that executes
* {@link Entity#teleport(Location)} in the main thread (which may lead to lag spikes).
*
* @param entity The entity to teleport
* @param location The location to teleport the entity to
* @param cause The cause of the teleport
* @return A future that completes when the entity is teleported, or the teleport fails
*/
@NotNull CompletableFuture<Boolean> teleportAsync(@NotNull Entity entity, @NotNull Location location, @NotNull PlayerTeleportEvent.TeleportCause cause);
}

0 comments on commit 8e96143

Please sign in to comment.