1+ package dev ._2lstudios .hamsterapi .utils ;
2+
3+ import java .lang .reflect .Method ;
4+ import java .util .HashMap ;
5+ import java .util .Map ;
6+ import java .util .concurrent .CompletableFuture ;
7+ import java .util .concurrent .Executors ;
8+ import java .util .function .Consumer ;
9+
10+ import org .bukkit .Bukkit ;
11+ import org .bukkit .Location ;
12+ import org .bukkit .Server ;
13+ import org .bukkit .World ;
14+ import org .bukkit .entity .Entity ;
15+ import org .bukkit .entity .Player ;
16+ import org .bukkit .plugin .Plugin ;
17+ import org .bukkit .scheduler .BukkitScheduler ;
18+
19+ import dev ._2lstudios .hamsterapi .HamsterAPI ;
20+
21+ public class FoliaAPI {
22+ private static Map <String , Method > cachedMethods = new HashMap <>();
23+
24+ private static BukkitScheduler bS = Bukkit .getScheduler ();
25+ private static Object globalRegionScheduler = getGlobalRegionScheduler ();
26+ private static Object regionScheduler = getRegionScheduler ();
27+ private static Object asyncScheduler = getAsyncScheduler ();
28+
29+ // Cache methods as early as possible
30+ static {
31+ cacheMethods ();
32+ }
33+
34+ private static Method getMethod (Class <?> clazz , String methodName , Class <?>... parameterTypes ) {
35+ if (clazz == null ) {
36+ return null ;
37+ }
38+ try {
39+ return clazz .getMethod (methodName , parameterTypes );
40+ } catch (NoSuchMethodException e ) {
41+ // Gracefully handle the case where the method does not exist
42+ return null ;
43+ }
44+ }
45+
46+ private static void cacheMethods () {
47+ // Cache methods for globalRegionScheduler
48+ if (globalRegionScheduler != null ) {
49+ Method runAtFixedRateMethod = getMethod (globalRegionScheduler .getClass (), "runAtFixedRate" , Plugin .class , Consumer .class , long .class , long .class );
50+ if (runAtFixedRateMethod != null ) {
51+ cachedMethods .put ("globalRegionScheduler.runAtFixedRate" , runAtFixedRateMethod );
52+ }
53+
54+ Method runMethod = getMethod (globalRegionScheduler .getClass (), "run" , Plugin .class , Consumer .class );
55+ if (runMethod != null ) {
56+ cachedMethods .put ("globalRegionScheduler.run" , runMethod );
57+ }
58+
59+ Method runDelayedMethod = getMethod (globalRegionScheduler .getClass (), "runDelayed" , Plugin .class , Consumer .class , long .class );
60+ if (runDelayedMethod != null ) {
61+ cachedMethods .put ("globalRegionScheduler.runDelayed" , runDelayedMethod );
62+ }
63+
64+ Method cancelTasksMethod = getMethod (globalRegionScheduler .getClass (), "cancelTasks" , Plugin .class );
65+ if (cancelTasksMethod != null ) {
66+ cachedMethods .put ("globalRegionScheduler.cancelTasks" , cancelTasksMethod );
67+ }
68+ }
69+
70+ // Cache methods for regionScheduler
71+ if (regionScheduler != null ) {
72+ Method executeMethod = getMethod (regionScheduler .getClass (), "execute" , Plugin .class , World .class , int .class , int .class , Runnable .class );
73+ if (executeMethod != null ) {
74+ cachedMethods .put ("regionScheduler.execute" , executeMethod );
75+ }
76+
77+ Method executeLocationMethod = getMethod (regionScheduler .getClass (), "execute" , Plugin .class , Location .class , Runnable .class );
78+ if (executeLocationMethod != null ) {
79+ cachedMethods .put ("regionScheduler.executeLocation" , executeLocationMethod );
80+ }
81+
82+ Method runAtFixedRateMethod = getMethod (regionScheduler .getClass (), "runAtFixedRate" , Plugin .class , Location .class , Consumer .class , long .class , long .class );
83+ if (runAtFixedRateMethod != null ) {
84+ cachedMethods .put ("regionScheduler.runAtFixedRate" , runAtFixedRateMethod );
85+ }
86+
87+ Method runDelayedMethod = getMethod (regionScheduler .getClass (), "runDelayed" , Plugin .class , Location .class , Consumer .class , long .class );
88+ if (runDelayedMethod != null ) {
89+ cachedMethods .put ("regionScheduler.runDelayed" , runDelayedMethod );
90+ }
91+ }
92+
93+ // Cache methods for entity scheduler
94+ Method getSchedulerMethod = getMethod (Entity .class , "getScheduler" );
95+ if (getSchedulerMethod != null ) {
96+ cachedMethods .put ("entity.getScheduler" , getSchedulerMethod );
97+ }
98+
99+ Method executeEntityMethod = getMethod (Entity .class , "execute" , Plugin .class , Runnable .class , Runnable .class , long .class );
100+ if (executeEntityMethod != null ) {
101+ cachedMethods .put ("entityScheduler.execute" , executeEntityMethod );
102+ }
103+
104+ Method runAtFixedRateEntityMethod = getMethod (Entity .class , "runAtFixedRate" , Plugin .class , Consumer .class , Runnable .class , long .class , long .class );
105+ if (runAtFixedRateEntityMethod != null ) {
106+ cachedMethods .put ("entityScheduler.runAtFixedRate" , runAtFixedRateEntityMethod );
107+ }
108+
109+ // Cache method for Player teleportAsync
110+ Method teleportAsyncMethod = getMethod (Player .class , "teleportAsync" , Location .class );
111+ if (teleportAsyncMethod != null ) {
112+ cachedMethods .put ("player.teleportAsync" , teleportAsyncMethod );
113+ }
114+
115+ // Cache methods for asyncScheduler
116+ if (asyncScheduler != null ) {
117+ Method cancelTasksMethod = getMethod (asyncScheduler .getClass (), "cancelTasks" , Plugin .class );
118+ if (cancelTasksMethod != null ) {
119+ cachedMethods .put ("asyncScheduler.cancelTasks" , cancelTasksMethod );
120+ }
121+ }
122+ }
123+
124+ private static Object invokeMethod (Method method , Object object , Object ... args ) {
125+ try {
126+ if (method != null && object != null ) {
127+ method .setAccessible (true );
128+ return method .invoke (object , args );
129+ }
130+ } catch (Exception e ) {
131+ e .printStackTrace ();
132+ }
133+ return null ;
134+ }
135+
136+ private static Object getGlobalRegionScheduler () {
137+ Method method = getMethod (Server .class , "getGlobalRegionScheduler" );
138+ return invokeMethod (method , Bukkit .getServer ());
139+ }
140+
141+ private static Object getRegionScheduler () {
142+ Method method = getMethod (Server .class , "getRegionScheduler" );
143+ return invokeMethod (method , Bukkit .getServer ());
144+ }
145+
146+ private static Object getAsyncScheduler () {
147+ Method method = getMethod (Server .class , "getAsyncScheduler" );
148+ return invokeMethod (method , Bukkit .getServer ());
149+ }
150+
151+ public static boolean isFolia () {
152+ try {
153+ Class .forName ("io.papermc.paper.threadedregions.RegionizedServer" );
154+ return globalRegionScheduler != null && regionScheduler != null ;
155+ } catch (Exception ig ) {
156+ return false ;
157+ }
158+ }
159+
160+ public static void runTaskAsync (Runnable run , long delay ) {
161+ if (!isFolia ()) {
162+ bS .runTaskLaterAsynchronously (HamsterAPI .getInstance (), run , delay );
163+ return ;
164+ }
165+ Executors .defaultThreadFactory ().newThread (run ).start ();
166+ }
167+
168+ public static void runTaskAsync (Runnable run ) {
169+ runTaskAsync (run , 1L );
170+ }
171+
172+ public static void runTaskTimerAsync (Consumer <Object > run , long delay , long period ) {
173+ if (!isFolia ()) {
174+ bS .runTaskTimerAsynchronously (HamsterAPI .getInstance (), () -> run .accept (null ), delay , period );
175+ return ;
176+ }
177+ Method method = cachedMethods .get ("globalRegionScheduler.runAtFixedRate" );
178+ invokeMethod (method , globalRegionScheduler , HamsterAPI .getInstance (), run , delay , period );
179+ }
180+
181+ public static void runTaskTimerAsync (Runnable runnable , long delay , long period ) {
182+ runTaskTimerAsync (obj -> runnable .run (), delay , period );
183+ }
184+
185+ public static void runTaskTimer (Consumer <Object > run , long delay , long period ) {
186+ if (!isFolia ()) {
187+ bS .runTaskTimer (HamsterAPI .getInstance (), () -> run .accept (null ), delay , period );
188+ return ;
189+ }
190+ Method method = cachedMethods .get ("globalRegionScheduler.runAtFixedRate" );
191+ invokeMethod (method , globalRegionScheduler , HamsterAPI .getInstance (), run , delay , period );
192+ }
193+
194+ public static void runTask (Runnable run ) {
195+ if (!isFolia ()) {
196+ bS .runTask (HamsterAPI .getInstance (), run );
197+ return ;
198+ }
199+ Method method = cachedMethods .get ("globalRegionScheduler.run" );
200+ invokeMethod (method , globalRegionScheduler , HamsterAPI .getInstance (), (Consumer <Object >) ignored -> run .run ());
201+ }
202+
203+ public static void runTask (Consumer <Object > run ) {
204+ if (!isFolia ()) {
205+ bS .runTask (HamsterAPI .getInstance (), () -> run .accept (null ));
206+ return ;
207+ }
208+ Method method = cachedMethods .get ("globalRegionScheduler.run" );
209+ invokeMethod (method , globalRegionScheduler , HamsterAPI .getInstance (), run );
210+ }
211+
212+ public static void runTaskLater (Runnable run , long delay ) {
213+ if (!isFolia ()) {
214+ bS .runTaskLater (HamsterAPI .getInstance (), run , delay );
215+ return ;
216+ }
217+ // Use Folia's global region scheduler for a delayed task
218+ Method method = cachedMethods .get ("globalRegionScheduler.runDelayed" );
219+ invokeMethod (method , globalRegionScheduler , HamsterAPI .getInstance (), (Consumer <Object >) ignored -> run .run (), delay );
220+ }
221+
222+ public static void runTaskLater (Consumer <Object > run , long delay ) {
223+ if (!isFolia ()) {
224+ bS .runTaskLater (HamsterAPI .getInstance (), () -> run .accept (null ), delay );
225+ return ;
226+ }
227+ // Use Folia's global region scheduler for a delayed task
228+ Method method = cachedMethods .get ("globalRegionScheduler.runDelayed" );
229+ invokeMethod (method , globalRegionScheduler , HamsterAPI .getInstance (), run , delay );
230+ }
231+
232+ public static void runTaskForEntity (Entity entity , Runnable run , Runnable retired , long delay ) {
233+ if (!isFolia ()) {
234+ bS .runTaskLater (HamsterAPI .getInstance (), run , delay );
235+ return ;
236+ }
237+ if (entity == null ) return ;
238+ Method getSchedulerMethod = cachedMethods .get ("entity.getScheduler" );
239+ Object entityScheduler = invokeMethod (getSchedulerMethod , entity );
240+ Method executeMethod = cachedMethods .get ("entityScheduler.execute" );
241+ invokeMethod (executeMethod , entityScheduler , HamsterAPI .getInstance (), run , retired , delay );
242+ }
243+
244+ public static void runTaskForEntityRepeating (Entity entity , Consumer <Object > task , Runnable retired ,
245+ long initialDelay , long period ) {
246+ if (!isFolia ()) {
247+ bS .runTaskTimer (HamsterAPI .getInstance (), () -> task .accept (null ), initialDelay , period );
248+ return ;
249+ }
250+ if (entity == null ) return ;
251+ Method getSchedulerMethod = cachedMethods .get ("entity.getScheduler" );
252+ Object entityScheduler = invokeMethod (getSchedulerMethod , entity );
253+ Method runAtFixedRateMethod = cachedMethods .get ("entityScheduler.runAtFixedRate" );
254+ invokeMethod (runAtFixedRateMethod , entityScheduler , HamsterAPI .getInstance (), task , retired , initialDelay , period );
255+ }
256+
257+ public static void runTaskForRegion (World world , int chunkX , int chunkZ , Runnable run ) {
258+ if (!isFolia ()) {
259+ bS .runTask (HamsterAPI .getInstance (), run );
260+ return ;
261+ }
262+ if (world == null ) return ;
263+ Method executeMethod = cachedMethods .get ("regionScheduler.execute" );
264+ invokeMethod (executeMethod , regionScheduler , HamsterAPI .getInstance (), world , chunkX , chunkZ , run );
265+ }
266+
267+ public static void runTaskForRegion (Location location , Runnable run ) {
268+ if (!isFolia ()) {
269+ bS .runTask (HamsterAPI .getInstance (), run );
270+ return ;
271+ }
272+ if (location == null ) return ;
273+ Method executeMethod = cachedMethods .get ("regionScheduler.executeLocation" );
274+ invokeMethod (executeMethod , regionScheduler , HamsterAPI .getInstance (), location , run );
275+ }
276+
277+ public static void runTaskForRegionRepeating (Location location , Consumer <Object > task , long initialDelay ,
278+ long period ) {
279+ if (!isFolia ()) {
280+ bS .runTaskTimer (HamsterAPI .getInstance (), () -> task .accept (null ), initialDelay , period );
281+ return ;
282+ }
283+ if (location == null ) return ;
284+ Method runAtFixedRateMethod = cachedMethods .get ("regionScheduler.runAtFixedRate" );
285+ invokeMethod (runAtFixedRateMethod , regionScheduler , HamsterAPI .getInstance (), location , task , initialDelay , period );
286+ }
287+
288+ public static void runTaskForRegionDelayed (Location location , Consumer <Object > task , long delay ) {
289+ if (!isFolia ()) {
290+ bS .runTaskLater (HamsterAPI .getInstance (), () -> task .accept (null ), delay );
291+ return ;
292+ }
293+ if (location == null ) return ;
294+ Method runDelayedMethod = cachedMethods .get ("regionScheduler.runDelayed" );
295+ invokeMethod (runDelayedMethod , regionScheduler , HamsterAPI .getInstance (), location , task , delay );
296+ }
297+
298+ public static CompletableFuture <Boolean > teleportPlayer (Player e , Location location , Boolean async ) {
299+ if (!isFolia ()) {
300+ e .teleport (location );
301+ return CompletableFuture .completedFuture (true );
302+ } else if (async ) {
303+ Method teleportMethod = cachedMethods .get ("player.teleportAsync" );
304+ return CompletableFuture .completedFuture (invokeMethod (teleportMethod , e , location ) != null );
305+ } else {
306+ e .teleport (location );
307+ return CompletableFuture .completedFuture (true );
308+ }
309+ }
310+
311+ public static void cancelAllTasks () {
312+ Plugin plugin = HamsterAPI .getInstance ();
313+ if (!isFolia ()) {
314+ // Standard Bukkit/Spigot/Paper: cancel all tasks for the plugin
315+ bS .cancelTasks (plugin );
316+ return ;
317+ }
318+
319+ // 1. Cancel tasks on the GlobalRegionScheduler
320+ Method cancelGlobalMethod = cachedMethods .get ("globalRegionScheduler.cancelTasks" );
321+ invokeMethod (cancelGlobalMethod , globalRegionScheduler , plugin );
322+
323+ // 2. Cancel tasks on the modern AsyncScheduler
324+ Method cancelAsyncMethod = cachedMethods .get ("asyncScheduler.cancelTasks" );
325+ invokeMethod (cancelAsyncMethod , asyncScheduler , plugin );
326+ }
327+ }
0 commit comments