Skip to content

Commit 4026510

Browse files
authored
Merge pull request #106 from ez-plugins/fix/folia-error
fix: BukkitPlatformScheduler teleportAsync on Folia (v3.2.3)
2 parents 7d6385f + 6c88e59 commit 4026510

11 files changed

Lines changed: 96 additions & 219 deletions

File tree

.github/workflows/nightly.yml

Lines changed: 0 additions & 202 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ Release tags use the `v` prefix (e.g. `v3.0.2`).
2020

2121
---
2222

23+
## [3.2.3] - 2026-05-16
24+
25+
### Fixed
26+
27+
- **Folia teleport crash** (`UnsupportedOperationException: Must use teleportAsync while in
28+
region threading`): `BukkitPlatformScheduler` now overrides `teleportAsync` and, when running
29+
on Folia (`regionizedRuntime` capability), calls `player.teleportAsync(Location)` via
30+
reflection instead of the forbidden synchronous `player.teleport()`. The Bukkit module was
31+
the only scheduler that still used the synchronous fallback from the interface default;
32+
`PaperPlatformScheduler` already called `teleportAsync` directly.
33+
34+
---
35+
2336
## [3.2.2] - 2026-05-12
2437

2538
### Fixed

ezrtp-api/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
<parent>
66
<groupId>com.skyblockexp</groupId>
77
<artifactId>ezrtp-parent</artifactId>
8-
<version>3.2.2</version>
8+
<version>3.2.3</version>
99
<relativePath>../pom.xml</relativePath>
1010
</parent>
1111

1212
<groupId>com.skyblockexp</groupId>
1313
<artifactId>ezrtp-api</artifactId>
14-
<version>3.2.2</version>
14+
<version>3.2.3</version>
1515
<packaging>jar</packaging>
1616
<name>EzRTP API</name>
1717
<description>Lightweight public API for EzRTP intended for third-party plugins.</description>

ezrtp-bukkit/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
<parent>
66
<groupId>com.skyblockexp</groupId>
77
<artifactId>ezrtp-parent</artifactId>
8-
<version>3.2.2</version>
8+
<version>3.2.3</version>
99
<relativePath>../pom.xml</relativePath>
1010
</parent>
1111

1212
<groupId>com.skyblockexp</groupId>
1313
<artifactId>ezrtp-bukkit</artifactId>
14-
<version>3.2.2</version>
14+
<version>3.2.3</version>
1515
<packaging>jar</packaging>
1616
<name>EzRTP (Bukkit)</name>
1717
<description>EzRTP Bukkit-compatible plugin module.</description>
@@ -111,7 +111,7 @@
111111
<dependency>
112112
<groupId>com.skyblockexp</groupId>
113113
<artifactId>ezrtp-common</artifactId>
114-
<version>3.2.2</version>
114+
<version>3.2.3</version>
115115
</dependency>
116116
</dependencies>
117117

ezrtp-bukkit/src/main/java/com/skyblockexp/ezrtp/platform/BukkitPlatformScheduler.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.skyblockexp.ezrtp.platform;
22

33
import org.bukkit.Bukkit;
4+
import org.bukkit.Location;
45
import org.bukkit.World;
6+
import org.bukkit.entity.Player;
57
import org.bukkit.plugin.Plugin;
68

79
import java.lang.reflect.Method;
10+
import java.util.concurrent.CompletableFuture;
811

912
public final class BukkitPlatformScheduler implements PlatformScheduler {
1013

@@ -95,6 +98,28 @@ public void executeRegionDelayed(
9598
plugin.getServer().getScheduler().runTaskLater(plugin, task, delayTicks);
9699
}
97100

101+
@Override
102+
public CompletableFuture<Boolean> teleportAsync(Player player, Location destination) {
103+
if (capabilities.regionizedRuntime()) {
104+
// Folia forbids synchronous teleport from a region thread. Use teleportAsync
105+
// via reflection so that the bukkit module works on Folia without a hard
106+
// Paper/Folia compile-time dependency.
107+
return invokeTeleportAsync(player, destination);
108+
}
109+
return CompletableFuture.completedFuture(player.teleport(destination));
110+
}
111+
112+
@SuppressWarnings("unchecked")
113+
private CompletableFuture<Boolean> invokeTeleportAsync(Player player, Location destination) {
114+
try {
115+
Method method = player.getClass().getMethod("teleportAsync", Location.class);
116+
return (CompletableFuture<Boolean>) method.invoke(player, destination);
117+
} catch (ReflectiveOperationException ignored) {
118+
// teleportAsync not available (shouldn't happen on Folia); fall back to sync.
119+
return CompletableFuture.completedFuture(player.teleport(destination));
120+
}
121+
}
122+
98123
private boolean invokeGlobalRun(Runnable task) {
99124
try {
100125
Object globalScheduler = Bukkit.class.getMethod("getGlobalRegionScheduler").invoke(null);

ezrtp-bukkit/src/test/java/com/skyblockexp/ezrtp/platform/BukkitPlatformSchedulerFoliaTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.skyblockexp.ezrtp.platform;
22

3+
import org.bukkit.Location;
34
import org.bukkit.Server;
5+
import org.bukkit.entity.Player;
46
import org.bukkit.plugin.Plugin;
57
import org.bukkit.scheduler.BukkitScheduler;
68
import org.bukkit.scheduler.BukkitTask;
@@ -14,6 +16,7 @@
1416

1517
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
1618
import static org.junit.jupiter.api.Assertions.assertNotNull;
19+
import static org.junit.jupiter.api.Assertions.assertTrue;
1720
import static org.mockito.ArgumentMatchers.any;
1821
import static org.mockito.ArgumentMatchers.anyLong;
1922
import static org.mockito.ArgumentMatchers.eq;
@@ -175,4 +178,42 @@ void executeRegionDelayed_withFoliaCapabilities_doesNotThrow() {
175178

176179
assertDoesNotThrow(() -> scheduler.executeRegionDelayed(null, 0, 0, () -> {}, 20L));
177180
}
181+
182+
// --- teleportAsync ---
183+
184+
/**
185+
* On Folia (regionized), teleportAsync must not call the synchronous Player.teleport().
186+
* Since Player.teleportAsync() is not available in the test JVM, the reflection path
187+
* falls through to the sync fallback. This test verifies the method does not throw and
188+
* returns a completed future.
189+
*/
190+
@Test
191+
void teleportAsync_withFoliaCapabilities_doesNotThrow() {
192+
Player player = mock(Player.class);
193+
Location destination = mock(Location.class);
194+
when(player.teleport(destination)).thenReturn(true);
195+
196+
BukkitPlatformScheduler scheduler =
197+
new BukkitPlatformScheduler(plugin, PlatformRuntimeCapabilities.PAPER_FOLIA);
198+
199+
assertDoesNotThrow(() -> scheduler.teleportAsync(player, destination));
200+
}
201+
202+
/**
203+
* On standard Bukkit, teleportAsync delegates to the synchronous Player.teleport().
204+
*/
205+
@Test
206+
void teleportAsync_withBukkitCapabilities_callsSyncTeleport() throws Exception {
207+
Player player = mock(Player.class);
208+
Location destination = mock(Location.class);
209+
when(player.teleport(destination)).thenReturn(true);
210+
211+
BukkitPlatformScheduler scheduler =
212+
new BukkitPlatformScheduler(plugin, PlatformRuntimeCapabilities.BUKKIT);
213+
214+
Boolean result = scheduler.teleportAsync(player, destination).get();
215+
216+
assertTrue(result);
217+
verify(player).teleport(destination);
218+
}
178219
}

ezrtp-common/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
<parent>
66
<groupId>com.skyblockexp</groupId>
77
<artifactId>ezrtp-parent</artifactId>
8-
<version>3.2.2</version>
8+
<version>3.2.3</version>
99
<relativePath>../pom.xml</relativePath>
1010
</parent>
1111

1212
<groupId>com.skyblockexp</groupId>
1313
<artifactId>ezrtp-common</artifactId>
14-
<version>3.2.2</version>
14+
<version>3.2.3</version>
1515
<packaging>jar</packaging>
1616
<name>EzRTP Common</name>
1717
<description>Shared utilities for EzRTP (platform-independent)</description>

0 commit comments

Comments
 (0)