From 0faaaa55ee7e6148c179ff138338ec8202ab0a2e Mon Sep 17 00:00:00 2001 From: SirYwell Date: Tue, 7 Mar 2023 14:36:58 +0100 Subject: [PATCH] Initial experimental work on Folia support --- .../fawe/v1_20_R1/PaperweightFaweAdapter.java | 8 +- .../PaperweightFaweWorldNativeAccess.java | 3 +- .../v1_20_R1/PaperweightPlatformAdapter.java | 12 +- .../fastasyncworldedit/bukkit/FaweBukkit.java | 26 ++- .../bukkit/listener/ChunkListener.java | 4 + .../bukkit/util/BukkitTaskManager.java | 26 +++ .../bukkit/util/FoliaTaskManager.java | 206 ++++++++++++++++++ .../sk89q/worldedit/bukkit/BukkitPlayer.java | 9 +- .../com/fastasyncworldedit/core/Fawe.java | 15 +- .../com/fastasyncworldedit/core/IFawe.java | 2 + .../queue/implementation/QueueHandler.java | 8 +- .../core/util/FoliaSupport.java | 38 ++++ .../core/util/TaskManager.java | 26 +++ .../core/wrappers/AsyncPlayer.java | 4 +- 14 files changed, 367 insertions(+), 20 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/util/FoliaSupport.java diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweAdapter.java index e13ff700f..04d116d97 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweAdapter.java @@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1; import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; +import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; @@ -240,7 +241,12 @@ public final class PaperweightFaweAdapter extends FaweAdapter> 4, z >> 4); + LevelChunk chunk; + if (Fawe.isTickThread()) { + chunk = handle.getChunk(x >> 4, z >> 4); + } else { + chunk = TaskManager.taskManager().syncAt(() -> handle.getChunk(x >> 4, z >> 4), BukkitAdapter.adapt(location)); + } final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); BlockState state = adapt(blockData); diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweWorldNativeAccess.java index e1230b20e..186415f9f 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweWorldNativeAccess.java @@ -59,7 +59,8 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess p.connection.send(packet)); - }); + }, toLocation(nmsWorld, coordIntPair)); } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false); } + public static Location toLocation(ServerLevel serverLevel, ChunkPos chunkPos) { + final World adapt = BukkitAdapter.adapt(serverLevel.getWorld()); + final Vector3 pos = Vector3.at(chunkPos.getMinBlockX(), 0, chunkPos.getMinBlockZ()); + return new Location(adapt, pos); + } + /* NMS conversion */ diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java index 5e60a704c..7a8604fcb 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java @@ -11,6 +11,8 @@ import com.fastasyncworldedit.bukkit.regions.ResidenceFeature; import com.fastasyncworldedit.bukkit.regions.TownyFeature; import com.fastasyncworldedit.bukkit.regions.WorldGuardFeature; import com.fastasyncworldedit.bukkit.util.BukkitTaskManager; +import com.fastasyncworldedit.bukkit.util.FoliaTaskManager; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.bukkit.util.ItemUtil; import com.fastasyncworldedit.bukkit.util.MinecraftVersion; import com.fastasyncworldedit.bukkit.util.image.BukkitImageViewer; @@ -63,6 +65,7 @@ public class FaweBukkit implements IFawe, Listener { private ItemUtil itemUtil; private Preloader preloader; private volatile boolean keepUnloaded; + private static final Thread startingThread = Thread.currentThread(); public FaweBukkit(Plugin plugin) { this.plugin = plugin; @@ -74,7 +77,7 @@ public class FaweBukkit implements IFawe, Listener { } catch (Throwable e) { LOGGER.error("Brush Listener Failed", e); } - if (PaperLib.isPaper() && Settings.settings().EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING > 1) { + if (!FoliaSupport.isFolia() && PaperLib.isPaper() && Settings.settings().EXPERIMENTAL.DYNAMIC_CHUNK_RENDERING > 1) { new RenderListener(plugin); } } catch (final Throwable e) { @@ -89,20 +92,22 @@ public class FaweBukkit implements IFawe, Listener { platformAdapter = new NMSAdapter(); //PlotSquared support is limited to Spigot/Paper as of 02/20/2020 - TaskManager.taskManager().later(this::setupPlotSquared, 0); + // TODO plotsquared support + // TaskManager.taskManager().later(this::setupPlotSquared, 0); + // TODO moved out of task below?? + Bukkit.getPluginManager().registerEvents(FaweBukkit.this, FaweBukkit.this.plugin); // Registered delayed Event Listeners - TaskManager.taskManager().task(() -> { + /*TaskManager.taskManager().task(() -> { // Fix for ProtocolSupport Settings.settings().PROTOCOL_SUPPORT_FIX = Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport"); // This class - Bukkit.getPluginManager().registerEvents(FaweBukkit.this, FaweBukkit.this.plugin); // The tick limiter new ChunkListener9(); - }); + });*/ // Warn if small-edits are enabled with extended world heights if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) { @@ -192,6 +197,9 @@ public class FaweBukkit implements IFawe, Listener { */ @Override public TaskManager getTaskManager() { + if (FoliaSupport.isFolia()) { + return new FoliaTaskManager(); + } return new BukkitTaskManager(plugin); } @@ -312,6 +320,14 @@ public class FaweBukkit implements IFawe, Listener { return platformAdapter; } + @Override + public boolean isTickThread() { + if (FoliaSupport.isFolia()) { + return FoliaSupport.isTickThread(); + } + return Thread.currentThread() == startingThread; + } + private void setupPlotSquared() { Plugin plotSquared = this.plugin.getServer().getPluginManager().getPlugin("PlotSquared"); if (plotSquared == null) { diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java index 7b9599084..43d935c33 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.bukkit.listener; import com.fastasyncworldedit.bukkit.FaweBukkit; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.util.FaweTimer; @@ -59,6 +60,9 @@ public abstract class ChunkListener implements Listener { Settings.settings().TICK_LIMITER.FALLING, Settings.settings().TICK_LIMITER.ITEMS}; public ChunkListener() { + if (FoliaSupport.isFolia()) { + return; + } if (Settings.settings().TICK_LIMITER.ENABLED) { PluginManager plm = Bukkit.getPluginManager(); Plugin plugin = Fawe.platform().getPlugin(); diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/BukkitTaskManager.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/BukkitTaskManager.java index 11b8565a6..a79cd5d2e 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/BukkitTaskManager.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/BukkitTaskManager.java @@ -1,10 +1,14 @@ package com.fastasyncworldedit.bukkit.util; import com.fastasyncworldedit.core.util.TaskManager; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.util.Location; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nonnull; +import java.util.function.Supplier; public class BukkitTaskManager extends TaskManager { @@ -34,11 +38,21 @@ public class BukkitTaskManager extends TaskManager { this.plugin.getServer().getScheduler().runTask(this.plugin, runnable).getTaskId(); } + @Override + public void task(@NotNull final Runnable runnable, @NotNull final Location context) { + + } + @Override public void later(@Nonnull final Runnable runnable, final int delay) { this.plugin.getServer().getScheduler().runTaskLater(this.plugin, runnable, delay).getTaskId(); } + @Override + public void later(@NotNull final Runnable runnable, final Location location, final int delay) { + + } + @Override public void laterAsync(@Nonnull final Runnable runnable, final int delay) { this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, runnable, delay); @@ -51,4 +65,16 @@ public class BukkitTaskManager extends TaskManager { } } + // TODO + + @Override + public T syncAt(final Supplier supplier, final Location context) { + return sync(supplier); + } + + @Override + public T syncWith(final Supplier supplier, final Player context) { + return sync(supplier); + } + } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java new file mode 100644 index 000000000..f35a51bf5 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java @@ -0,0 +1,206 @@ +package com.fastasyncworldedit.bukkit.util; + +import com.fastasyncworldedit.core.util.TaskManager; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.util.Location; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import static java.lang.invoke.MethodHandles.dropReturn; +import static java.lang.invoke.MethodHandles.explicitCastArguments; +import static java.lang.invoke.MethodHandles.filterArguments; +import static java.lang.invoke.MethodHandles.insertArguments; +import static java.lang.invoke.MethodType.methodType; + +public class FoliaTaskManager extends TaskManager { + + private final ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor(); + private final AtomicInteger idCounter = new AtomicInteger(); + + @Override + public int repeat(@NotNull final Runnable runnable, final int interval) { + return fail(); + } + + @Override + public int repeatAsync(@NotNull final Runnable runnable, final int interval) { + backgroundExecutor.scheduleAtFixedRate(runnable, 0, ticksToMs(interval), TimeUnit.MILLISECONDS); + return idCounter.getAndIncrement(); + } + + @Override + public void async(@NotNull final Runnable runnable) { + backgroundExecutor.submit(runnable); + } + + @Override + public void task(@NotNull final Runnable runnable) { + fail(); + } + + @Override + public void task(@NotNull final Runnable runnable, @NotNull final Location context) { + SchedulerAdapter.executeForLocation(context, runnable); + } + + @Override + public void later(@NotNull final Runnable runnable, final int delay) { + fail(); + } + + @Override + public void later(@NotNull final Runnable runnable, final Location location, final int delay) { + fail("Not implemented"); + } + + @Override + public void laterAsync(@NotNull final Runnable runnable, final int delay) { + backgroundExecutor.schedule(runnable, ticksToMs(delay), TimeUnit.MILLISECONDS); + } + + @Override + public void cancel(final int task) { + fail("Not implemented"); + } + + @Override + public T syncAt(final Supplier supplier, final Location context) { + FutureTask task = new FutureTask<>(supplier::get); + SchedulerAdapter.executeForLocation(context, task); + try { + return task.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Override + public T syncWith(final Supplier supplier, final Player context) { + FutureTask task = new FutureTask<>(supplier::get); + SchedulerAdapter.executeForEntity(context, task); + try { + return task.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + private int ticksToMs(int ticks) { + // 1 tick = 50ms + return ticks * 50; + } + + private T fail() { + return fail("No main thread present"); + } + + private T fail(String message) { + throw new UnsupportedOperationException(message); + } + + private static class SchedulerAdapter { + + private static final MethodHandle EXECUTE_FOR_LOCATION; + private static final MethodHandle EXECUTE_FOR_PLAYER; + + private static final MethodType LOCATION_EXECUTE_TYPE = methodType( + void.class, + Plugin.class, + org.bukkit.Location.class, + Runnable.class + ); + + private static final MethodType ENTITY_EXECUTE_TYPE = methodType( + boolean.class, + Plugin.class, + Runnable.class, + Runnable.class, + long.class + ); + + static { + final Plugin pluginInstance = WorldEditPlugin.getInstance(); + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + + MethodHandle executeForLocation; + + MethodHandle executeForPlayer; + try { + Class regionisedSchedulerClass = Class.forName("io.papermc.paper.threadedregions.scheduler.RegionisedScheduler"); + final Method method = Bukkit.class.getDeclaredMethod("getRegionScheduler"); + executeForLocation = lookup.findVirtual( + regionisedSchedulerClass, + "execute", + LOCATION_EXECUTE_TYPE + ); + executeForLocation = executeForLocation.bindTo(method.invoke(null)); + executeForLocation = executeForLocation.bindTo(pluginInstance); + + Class entitySchedulerClass = Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler"); + executeForPlayer = lookup.findVirtual( + entitySchedulerClass, + "execute", + ENTITY_EXECUTE_TYPE + ); + // (ES, P, R, R, L)Z (ES, R, R, L)Z + executeForPlayer = insertArguments(executeForPlayer, 1, pluginInstance); + // (ES, R1, R2, L)Z -> (ES, R1)Z + executeForPlayer = insertArguments(executeForPlayer, 2, null, 0); + // (ES, R1)Z -> (ES, R1)V + executeForPlayer = dropReturn(executeForPlayer); + MethodHandle getScheduler = lookup.findVirtual( + org.bukkit.entity.Entity.class, + "getScheduler", + methodType(entitySchedulerClass) + ); + // (ES, R1)V -> (E, R1)V + executeForPlayer = filterArguments(executeForPlayer, 0, getScheduler); + MethodType finalType = methodType(void.class, org.bukkit.entity.Player.class, Runnable.class); + // (ES, R1)V -> (P, R1)V + executeForPlayer = explicitCastArguments(executeForPlayer, finalType); + } catch (Throwable throwable) { + throw new AssertionError(throwable); + } + EXECUTE_FOR_LOCATION = executeForLocation; + EXECUTE_FOR_PLAYER = executeForPlayer; + } + + static void executeForLocation(Location location, Runnable task) { + try { + EXECUTE_FOR_LOCATION.invokeExact(BukkitAdapter.adapt(location), task); + } catch (Error | RuntimeException e) { + throw e; + } catch (Throwable other) { + throw new RuntimeException(other); + } + } + static void executeForEntity(Player player, Runnable task) { + // TODO task might not be run if player retires + try { + EXECUTE_FOR_PLAYER.invokeExact(BukkitAdapter.adapt(player), task); + } catch (Error | RuntimeException e) { + throw e; + } catch (Throwable other) { + throw new RuntimeException(other); + } + } + + } + +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 9c5196e91..286420aa0 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -161,7 +161,7 @@ public class BukkitPlayer extends AbstractPlayerActor { public void giveItem(BaseItemStack itemStack) { final PlayerInventory inv = player.getInventory(); ItemStack newItem = BukkitAdapter.adapt(itemStack); - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncWith(() -> { if (itemStack.getType().getId().equalsIgnoreCase(WorldEdit.getInstance().getConfiguration().wandItem)) { inv.remove(newItem); } @@ -183,7 +183,7 @@ public class BukkitPlayer extends AbstractPlayerActor { } player.updateInventory(); return null; - }); + }, this); } //FAWE end @@ -240,14 +240,15 @@ public class BukkitPlayer extends AbstractPlayerActor { } org.bukkit.World finalWorld = world; //FAWE end - return TaskManager.taskManager().sync(() -> player.teleport(new Location( + // TODO async teleport? + return TaskManager.taskManager().syncWith(() -> player.teleport(new Location( finalWorld, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch - ))); + )), this); } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java index ff2f69486..230e12013 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java @@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.util.CachedTextureUtil; import com.fastasyncworldedit.core.util.CleanTextureUtil; import com.fastasyncworldedit.core.util.FaweTimer; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.MemUtil; import com.fastasyncworldedit.core.util.RandomTextureUtil; @@ -129,14 +130,17 @@ public class Fawe { this.timer = new FaweTimer(); // Delayed worldedit setup - TaskManager.taskManager().later(() -> { + // TODO support again + /*TaskManager.taskManager().later(() -> { try { WEManager.weManager().addManagers(Fawe.this.implementation.getMaskManagers()); } catch (Throwable ignored) { } - }, 0); + }, 0);*/ - TaskManager.taskManager().repeat(timer, 1); + if (!FoliaSupport.isFolia()) { + TaskManager.taskManager().repeat(timer, 1); + } clipboardExecutor = new KeyQueuedExecutorService<>(new ThreadPoolExecutor( 1, @@ -206,10 +210,15 @@ public class Fawe { } } + @Deprecated public static boolean isMainThread() { return instance == null || instance.thread == Thread.currentThread(); } + public static boolean isTickThread() { + return instance == null || instance.implementation.isTickThread(); + } + /** * Non-api. Handles an input FAWE exception if not already handled, given the input boolean array. * Looks at the {@link FaweException.Type} and decides what to do (rethrows if we want to attempt to show the error to the diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/IFawe.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/IFawe.java index 5c8a80c02..e467ba6e2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/IFawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/IFawe.java @@ -49,4 +49,6 @@ public interface IFawe { FAWEPlatformAdapterImpl getPlatformAdapter(); + boolean isTickThread(); + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java index 7bdbf4645..0074cecdf 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -17,6 +17,7 @@ import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal; import com.fastasyncworldedit.core.util.task.FaweForkJoinWorkerThreadFactory; import com.fastasyncworldedit.core.wrappers.WorldWrapper; import com.google.common.util.concurrent.Futures; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; import java.lang.ref.WeakReference; @@ -90,13 +91,14 @@ public abstract class QueueHandler implements Trimable, Runnable { private long allocate = 50; protected QueueHandler() { - TaskManager.taskManager().repeat(this, 1); + // TODO make main thread independent + // TaskManager.taskManager().repeat(this, 1); } @Override public void run() { - if (!Fawe.isMainThread()) { - throw new IllegalStateException("Not main thread"); + if (!Fawe.isTickThread()) { + throw new IllegalStateException("Not ticking thread"); } if (!syncTasks.isEmpty()) { long currentAllocate = getAllocate(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/FoliaSupport.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/FoliaSupport.java new file mode 100644 index 000000000..f8458dc19 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/FoliaSupport.java @@ -0,0 +1,38 @@ +package com.fastasyncworldedit.core.util; + +public final class FoliaSupport { + private FoliaSupport() { + + } + + private static final boolean IS_FOLIA; + private static final Class TICK_THREAD_CLASS; + static { + boolean isFolia = false; + try { + // Assume API is present + Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler"); + isFolia = true; + } catch (Exception unused) { + + } + IS_FOLIA = isFolia; + Class tickThreadClass = String.class; // thread will never be instance of String + if (IS_FOLIA) { + try { + tickThreadClass = Class.forName("io.papermc.paper.util.TickThread"); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } + } + TICK_THREAD_CLASS = tickThreadClass; + } + + public static boolean isFolia() { + return IS_FOLIA; + } + + public static boolean isTickThread() { + return TICK_THREAD_CLASS.isInstance(Thread.currentThread()); + } +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java index 176e02673..69a16b437 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java @@ -4,7 +4,10 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.util.task.RunnableVal; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.util.Location; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; @@ -52,6 +55,7 @@ public abstract class TaskManager { * @param runnable the task to run * @param interval in ticks */ + @Deprecated public abstract int repeat(@Nonnull final Runnable runnable, final int interval); /** @@ -75,7 +79,14 @@ public abstract class TaskManager { * * @param runnable the task to run */ + @Deprecated public abstract void task(@Nonnull final Runnable runnable); + /** + * Run a task on the main thread. + * + * @param runnable the task to run + */ + public abstract void task(@Nonnull final Runnable runnable, @Nonnull Location contextLocation); /** * Get the public ForkJoinPool. @@ -159,6 +170,7 @@ public abstract class TaskManager { /** * Disable async catching for a specific task. */ + @Deprecated public void runUnsafe(Runnable run) { QueueHandler queue = Fawe.instance().getQueueHandler(); queue.startUnsafe(Fawe.isMainThread()); @@ -191,6 +203,7 @@ public abstract class TaskManager { * * @param runnable the task to run */ + @Deprecated public void taskNowMain(@Nonnull final Runnable runnable) { if (Fawe.isMainThread()) { runnable.run(); @@ -215,6 +228,7 @@ public abstract class TaskManager { * @param runnable the task to run. * @param async whether the task should run on the main thread */ + @Deprecated public void taskSoonMain(@Nonnull final Runnable runnable, boolean async) { if (async) { async(runnable); @@ -230,7 +244,9 @@ public abstract class TaskManager { * @param runnable the task to run * @param delay in ticks */ + @Deprecated public abstract void later(@Nonnull final Runnable runnable, final int delay); + public abstract void later(@Nonnull final Runnable runnable, Location location, final int delay); /** * Run a task later asynchronously. @@ -255,6 +271,7 @@ public abstract class TaskManager { * @param task the task to run on each object * @param whenDone when the object task completes */ + @Deprecated public void objectTask(Collection objects, final RunnableVal task, final Runnable whenDone) { final Iterator iterator = objects.iterator(); task(new Runnable() { @@ -307,6 +324,7 @@ public abstract class TaskManager { } } + @Deprecated public void taskWhenFree(@Nonnull Runnable run) { if (Fawe.isMainThread()) { run.run(); @@ -320,6 +338,7 @@ public abstract class TaskManager { * - Useful if you need to access something from the Bukkit API from another thread
* - Usually wait time is around 25ms
*/ + @Deprecated public T syncWhenFree(@Nonnull final RunnableVal function) { if (Fawe.isMainThread()) { function.run(); @@ -337,6 +356,7 @@ public abstract class TaskManager { * - Useful if you need to access something from the Bukkit API from another thread
* - Usually wait time is around 25ms
*/ + @Deprecated public T syncWhenFree(@Nonnull final Supplier supplier) { if (Fawe.isMainThread()) { return supplier.get(); @@ -353,6 +373,7 @@ public abstract class TaskManager { * - Useful if you need to access something from the Bukkit API from another thread
* - Usually wait time is around 25ms */ + @Deprecated public T sync(@Nonnull final RunnableVal function) { return sync((Supplier) function); } @@ -362,6 +383,7 @@ public abstract class TaskManager { * - Useful if you need to access something from the Bukkit API from another thread
* - Usually wait time is around 25ms
*/ + @Deprecated public T sync(final Supplier function) { if (Fawe.isMainThread()) { return function.get(); @@ -373,4 +395,8 @@ public abstract class TaskManager { } } + public abstract T syncAt(Supplier supplier, Location context); + + public abstract T syncWith(Supplier supplier, Player context); + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/AsyncPlayer.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/AsyncPlayer.java index 700070611..5baff8236 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/AsyncPlayer.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/AsyncPlayer.java @@ -58,12 +58,12 @@ public class AsyncPlayer extends PlayerProxy { @Override public void findFreePosition() { - TaskManager.taskManager().sync(new RunnableVal() { + TaskManager.taskManager().syncWith(new RunnableVal() { @Override public void run(Boolean value) { getBasePlayer().findFreePosition(); } - }); + }, this); } @Override