11package com .skyblockexp .ezrtp .platform ;
22
3+ import org .bukkit .Bukkit ;
34import org .bukkit .World ;
45import org .bukkit .plugin .Plugin ;
56
7+ import java .lang .reflect .Method ;
8+
69public final class BukkitPlatformScheduler implements PlatformScheduler {
710
811 private final Plugin plugin ;
12+ private final PlatformRuntimeCapabilities capabilities ;
913
10- public BukkitPlatformScheduler (Plugin plugin ) {
14+ /**
15+ * Creates a scheduler with explicit capability information.
16+ *
17+ * <p>When {@code capabilities.regionizedRuntime()} is {@code true} (Folia), sync Bukkit
18+ * scheduler calls are replaced with reflection-based Folia scheduler calls. This prevents
19+ * the {@link UnsupportedOperationException} that Folia's {@code CraftScheduler} throws for
20+ * all synchronous tasks.
21+ */
22+ public BukkitPlatformScheduler (Plugin plugin , PlatformRuntimeCapabilities capabilities ) {
1123 this .plugin = plugin ;
24+ this .capabilities = capabilities ;
1225 }
1326
1427 @ Override
@@ -18,31 +31,153 @@ public void executeAsync(Runnable task) {
1831
1932 @ Override
2033 public void executeRegion (World world , int chunkX , int chunkZ , Runnable task ) {
34+ if (capabilities .regionizedRuntime ()) {
35+ if (world != null ) {
36+ invokeRegionTask (world , chunkX , chunkZ , task );
37+ } else {
38+ invokeGlobalRun (task );
39+ }
40+ return ;
41+ }
2142 plugin .getServer ().getScheduler ().runTask (plugin , task );
2243 }
2344
2445 @ Override
2546 public PlatformTask scheduleRepeating (Runnable task , long delayTicks , long periodTicks ) {
47+ if (capabilities .regionizedRuntime ()) {
48+ PlatformTask foliaTask = scheduleGlobalRepeating (task , delayTicks , periodTicks );
49+ if (foliaTask != null ) {
50+ return foliaTask ;
51+ }
52+ // Folia detected but reflection unavailable — return a no-op rather than calling the
53+ // Bukkit scheduler which Folia forbids.
54+ return () -> {};
55+ }
2656 org .bukkit .scheduler .BukkitTask bukkitTask = plugin .getServer ().getScheduler ()
2757 .runTaskTimer (plugin , task , delayTicks , periodTicks );
2858 return bukkitTask ::cancel ;
2959 }
3060
3161 @ Override
3262 public void executeGlobal (Runnable task ) {
63+ if (capabilities .regionizedRuntime ()) {
64+ invokeGlobalRun (task );
65+ return ;
66+ }
3367 plugin .getServer ().getScheduler ().runTask (plugin , task );
3468 }
3569
3670 @ Override
3771 public PlatformTask executeGlobalDelayed (Runnable task , long delayTicks ) {
38- org .bukkit .scheduler .BukkitTask bukkitTask =
72+ if (capabilities .regionizedRuntime ()) {
73+ PlatformTask foliaTask = invokeGlobalRunDelayed (task , delayTicks );
74+ if (foliaTask != null ) {
75+ return foliaTask ;
76+ }
77+ return () -> {};
78+ }
79+ org .bukkit .scheduler .BukkitTask bukkit =
3980 plugin .getServer ().getScheduler ().runTaskLater (plugin , task , delayTicks );
40- return bukkitTask ::cancel ;
81+ return bukkit ::cancel ;
4182 }
4283
4384 @ Override
4485 public void executeRegionDelayed (
4586 World world , int chunkX , int chunkZ , Runnable task , long delayTicks ) {
87+ if (capabilities .regionizedRuntime ()) {
88+ if (world != null ) {
89+ invokeRegionDelayed (world , chunkX , chunkZ , task , delayTicks );
90+ } else {
91+ invokeGlobalRunDelayed (task , delayTicks );
92+ }
93+ return ;
94+ }
4695 plugin .getServer ().getScheduler ().runTaskLater (plugin , task , delayTicks );
4796 }
97+
98+ private boolean invokeGlobalRun (Runnable task ) {
99+ try {
100+ Object globalScheduler = Bukkit .class .getMethod ("getGlobalRegionScheduler" ).invoke (null );
101+ Method run = globalScheduler .getClass ().getMethod (
102+ "run" , Plugin .class , java .util .function .Consumer .class );
103+ run .invoke (globalScheduler , plugin ,
104+ (java .util .function .Consumer <Object >) ignored -> task .run ());
105+ return true ;
106+ } catch (ReflectiveOperationException ignored ) {
107+ return false ;
108+ }
109+ }
110+
111+ private PlatformTask invokeGlobalRunDelayed (Runnable task , long delayTicks ) {
112+ try {
113+ Object globalScheduler = Bukkit .class .getMethod ("getGlobalRegionScheduler" ).invoke (null );
114+ Method runDelayed = globalScheduler .getClass ().getMethod (
115+ "runDelayed" , Plugin .class , java .util .function .Consumer .class , long .class );
116+ Object scheduledTask = runDelayed .invoke (globalScheduler , plugin ,
117+ (java .util .function .Consumer <Object >) ignored -> task .run (), delayTicks );
118+ Method cancel = scheduledTask .getClass ().getMethod ("cancel" );
119+ return () -> {
120+ try {
121+ cancel .invoke (scheduledTask );
122+ } catch (ReflectiveOperationException ignored ) {
123+ }
124+ };
125+ } catch (ReflectiveOperationException ignored ) {
126+ return null ;
127+ }
128+ }
129+
130+ private boolean invokeRegionTask (World world , int chunkX , int chunkZ , Runnable task ) {
131+ try {
132+ Object regionScheduler = Bukkit .class .getMethod ("getRegionScheduler" ).invoke (null );
133+ Method run = regionScheduler .getClass ().getMethod (
134+ "run" , Plugin .class , World .class , int .class , int .class ,
135+ java .util .function .Consumer .class );
136+ run .invoke (regionScheduler , plugin , world , chunkX , chunkZ ,
137+ (java .util .function .Consumer <Object >) ignored -> task .run ());
138+ return true ;
139+ } catch (ReflectiveOperationException ignored ) {
140+ return false ;
141+ }
142+ }
143+
144+ private boolean invokeRegionDelayed (
145+ World world , int chunkX , int chunkZ , Runnable task , long delayTicks ) {
146+ try {
147+ Object regionScheduler = Bukkit .class .getMethod ("getRegionScheduler" ).invoke (null );
148+ Method runDelayed = regionScheduler .getClass ().getMethod (
149+ "runDelayed" , Plugin .class , World .class , int .class , int .class ,
150+ java .util .function .Consumer .class , long .class );
151+ runDelayed .invoke (regionScheduler , plugin , world , chunkX , chunkZ ,
152+ (java .util .function .Consumer <Object >) ignored -> task .run (), delayTicks );
153+ return true ;
154+ } catch (ReflectiveOperationException ignored ) {
155+ return false ;
156+ }
157+ }
158+
159+ private PlatformTask scheduleGlobalRepeating (Runnable task , long delayTicks , long periodTicks ) {
160+ try {
161+ Object globalRegionScheduler = Bukkit .class .getMethod ("getGlobalRegionScheduler" ).invoke (null );
162+ Method runAtFixedRate = globalRegionScheduler .getClass ().getMethod (
163+ "runAtFixedRate" ,
164+ Plugin .class ,
165+ java .util .function .Consumer .class ,
166+ long .class ,
167+ long .class );
168+ Object scheduledTask = runAtFixedRate .invoke (globalRegionScheduler , plugin ,
169+ (java .util .function .Consumer <Object >) ignored -> task .run (),
170+ delayTicks ,
171+ periodTicks );
172+ Method cancel = scheduledTask .getClass ().getMethod ("cancel" );
173+ return () -> {
174+ try {
175+ cancel .invoke (scheduledTask );
176+ } catch (ReflectiveOperationException ignored ) {
177+ }
178+ };
179+ } catch (ReflectiveOperationException ignored ) {
180+ return null ;
181+ }
182+ }
48183}
0 commit comments