diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java index 7cfce755ed..686c80af69 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweAdapter.java @@ -3,6 +3,7 @@ import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter; 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; @@ -259,7 +260,13 @@ public BlockState getBlock(Location location) { int y = location.getBlockY(); int z = location.getBlockZ(); final ServerLevel handle = craftWorld.getHandle(); - LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + LevelChunk chunk; + if (Fawe.isTickThread()) { + // TODO check if is owned by this thread, else synchronize + 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_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweWorldNativeAccess.java index e5c26aba5e..ce5ad770e1 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightFaweWorldNativeAccess.java @@ -58,7 +58,8 @@ public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAd this.level = level; // Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging. // - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway. - this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + // TODO + this.lastTick = new AtomicInteger(0); } private Level getLevel() { diff --git a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightPlatformAdapter.java index 479df3b1ae..ed81c2bd8d 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightPlatformAdapter.java @@ -11,9 +11,13 @@ import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; @@ -352,13 +356,19 @@ public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boole ); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> 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/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightStarlightRelighter.java index 642250aa0c..ecaa894dcc 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R2/PaperweightStarlightRelighter.java @@ -5,9 +5,11 @@ import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.util.Location; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.LongArraySet; import it.unimi.dsi.fastutil.longs.LongIterator; @@ -101,39 +103,53 @@ private void fixLighting(LongSet chunks, Runnable andThen) { while (iterator.hasNext()) { coords.add(new ChunkPos(iterator.nextLong())); } + if (FoliaSupport.isFolia()) { + relightRegion(andThen, coords); + return; + } TaskManager.taskManager().task(() -> { // trigger chunk load and apply ticket on main thread - List> futures = new ArrayList<>(); - for (ChunkPos pos : coords) { - futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z) - .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel( - FAWE_TICKET, - pos, - LIGHT_LEVEL, - Unit.INSTANCE - )) - ); - } - // collect futures and trigger relight once all chunks are loaded - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v -> - invokeRelight( - coords, - c -> { - }, // no callback for single chunks required - i -> { - if (i != coords.size()) { - LOGGER.warn("Processed {} chunks instead of {}", i, coords.size()); - } - // post process chunks on main thread - TaskManager.taskManager().task(() -> postProcessChunks(coords)); - // call callback on our own threads - TaskManager.taskManager().async(andThen); - } - ) - ); + relightRegion(andThen, coords); }); } + private void relightRegion(Runnable andThen, Set coords) { + List> futures = new ArrayList<>(); + for (ChunkPos pos : coords) { + futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z) + .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel( + FAWE_TICKET, + pos, + LIGHT_LEVEL, + Unit.INSTANCE + )) + ); + } + Location location = toLocation(coords.iterator().next()); + // collect futures and trigger relight once all chunks are loaded + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAcceptAsync(v -> + invokeRelight( + coords, + c -> { + }, // no callback for single chunks required + i -> { + if (i != coords.size()) { + LOGGER.warn("Processed {} chunks instead of {}", i, coords.size()); + } + // post process chunks on main thread + TaskManager.taskManager().task(() -> postProcessChunks(coords), location); + // call callback on our own threads + TaskManager.taskManager().async(andThen); + } + ), + task -> TaskManager.taskManager().task(task, location) + ); + } + + private Location toLocation(ChunkPos chunkPos) { + return PaperweightPlatformAdapter.toLocation(this.serverLevel, chunkPos); + } + private void invokeRelight( Set coords, Consumer chunkCallback, 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 e4f66dea36..ca6b1fcfd6 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.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 FaweBukkit(Plugin plugin) { } 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 FaweBukkit(Plugin plugin) { 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 String getDebugInfo() { */ @Override public TaskManager getTaskManager() { + if (FoliaSupport.isFolia()) { + return new FoliaTaskManager(); + } return new BukkitTaskManager(plugin); } @@ -312,6 +320,14 @@ public FAWEPlatformAdapterImpl getPlatformAdapter() { 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 7b95990843..43d935c33e 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 11b8565a67..a79cd5d2e6 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 void task(@Nonnull final Runnable runnable) { 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 void cancel(final int task) { } } + // 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 0000000000..f928593494 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java @@ -0,0 +1,209 @@ +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.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 Runnable THROW_IF_RETIRED = () -> throwRetired(); + + 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, THROW_IF_RETIRED, 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) { + try { + EXECUTE_FOR_PLAYER.invokeExact(BukkitAdapter.adapt(player), task); + } catch (Error | RuntimeException e) { + throw e; + } catch (Throwable other) { + throw new RuntimeException(other); + } + } + + private static void throwRetired() { + throw new RuntimeException("Player retired"); + } + + } + +} 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 9c5196e917..286420aa04 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 String getDisplayName() { 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 void giveItem(BaseItemStack itemStack) { } player.updateInventory(); return null; - }); + }, this); } //FAWE end @@ -240,14 +240,15 @@ public boolean trySetPosition(Vector3 pos, float pitch, float yaw) { } 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 8f4818f2f0..c5401c8e6d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/Fawe.java @@ -6,12 +6,12 @@ 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; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TextureUtil; -import com.fastasyncworldedit.core.util.WEManager; import com.fastasyncworldedit.core.util.task.KeyQueuedExecutorService; import com.github.luben.zstd.Zstd; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -129,14 +129,17 @@ private Fawe(final IFawe implementation) { 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 +209,15 @@ public static void setupInjector() { } } + @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 5c8a80c02f..e467ba6e24 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 @@ default boolean isChunksStretched() { 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 7f604a2774..9fdeecd32c 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 @@ -12,7 +12,6 @@ import com.fastasyncworldedit.core.queue.Trimable; import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache; import com.fastasyncworldedit.core.util.MemUtil; -import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal; import com.fastasyncworldedit.core.util.task.FaweForkJoinWorkerThreadFactory; import com.fastasyncworldedit.core.wrappers.WorldWrapper; @@ -87,13 +86,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 0000000000..f8458dc197 --- /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 f3abf0ac30..8ea8bda95c 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.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 static TaskManager 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 static TaskManager 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. @@ -155,6 +166,7 @@ public void parallel(Collection runnables, @Nullable Integer numThread /** * Disable async catching for a specific task. */ + @Deprecated public void runUnsafe(Runnable run) { QueueHandler queue = Fawe.instance().getQueueHandler(); queue.startUnsafe(Fawe.isMainThread()); @@ -187,6 +199,7 @@ public void taskNow(@Nonnull final Runnable runnable, boolean async) { * * @param runnable the task to run */ + @Deprecated public void taskNowMain(@Nonnull final Runnable runnable) { if (Fawe.isMainThread()) { runnable.run(); @@ -211,6 +224,7 @@ public void taskNowAsync(@Nonnull final Runnable runnable) { * @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); @@ -226,7 +240,9 @@ public void taskSoonMain(@Nonnull final Runnable runnable, boolean async) { * @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. @@ -251,6 +267,7 @@ public void taskSoonMain(@Nonnull final Runnable runnable, boolean async) { * @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() { @@ -295,6 +312,7 @@ public void notify(AtomicBoolean running) { } } + @Deprecated public void taskWhenFree(@Nonnull Runnable run) { if (Fawe.isMainThread()) { run.run(); @@ -308,6 +326,7 @@ public void taskWhenFree(@Nonnull Runnable run) { * - 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(); @@ -325,6 +344,7 @@ public T syncWhenFree(@Nonnull final RunnableVal function) { * - 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(); @@ -341,6 +361,7 @@ public T syncWhenFree(@Nonnull final Supplier supplier) { * - 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); } @@ -350,6 +371,7 @@ public T sync(@Nonnull final RunnableVal function) { * - 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(); @@ -361,4 +383,8 @@ public T sync(final Supplier function) { } } + 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 7000706113..be5b4b5447 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 @@ -10,6 +10,7 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.PlayerProxy; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.util.Direction; @@ -17,6 +18,7 @@ import com.sk89q.worldedit.util.TargetBlock; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockTypes; +import org.jetbrains.annotations.Nullable; public class AsyncPlayer extends PlayerProxy { @@ -58,12 +60,12 @@ public void run(Boolean value) { @Override public void findFreePosition() { - TaskManager.taskManager().sync(new RunnableVal() { + TaskManager.taskManager().syncWith(new RunnableVal() { @Override public void run(Boolean value) { getBasePlayer().findFreePosition(); } - }); + }, this); } @Override @@ -177,19 +179,13 @@ public void setPosition(Vector3 pos, float pitch, float yaw) { } @Override - public Location getBlockTrace(int range, boolean useLastBlock) { - return TaskManager.taskManager().sync(() -> { - TargetBlock tb = new TargetBlock(AsyncPlayer.this, range, 0.2D); - return useLastBlock ? tb.getAnyTargetBlock() : tb.getTargetBlock(); - }); + public Location getBlockTrace(final int range, final boolean useLastBlock, @Nullable final Mask stopMask) { + return TaskManager.taskManager().syncAt(() -> super.getBlockTrace(range, useLastBlock, stopMask), getLocation()); } @Override - public Location getBlockTraceFace(int range, boolean useLastBlock) { - return TaskManager.taskManager().sync(() -> { - TargetBlock tb = new TargetBlock(AsyncPlayer.this, range, 0.2D); - return useLastBlock ? tb.getAnyTargetBlockFace() : tb.getTargetBlockFace(); - }); + public Location getBlockTraceFace(final int range, final boolean useLastBlock, @Nullable final Mask stopMask) { + return TaskManager.taskManager().syncAt(() -> super.getBlockTraceFace(range, useLastBlock, stopMask), getLocation()); } @Override