From fbe69bdc81c5c1131b01ac107430ed7d5e0dc726 Mon Sep 17 00:00:00 2001 From: SirYwell Date: Mon, 11 Dec 2023 21:27:45 +0100 Subject: [PATCH] rebase fixes --- build.gradle.kts | 2 +- .../fawe/v1_20_R1/PaperweightFaweAdapter.java | 1 + .../PaperweightStarlightRelighter.java | 87 +++------------ .../PaperweightFaweWorldNativeAccess.java | 7 +- .../v1_20_R2/PaperweightPlatformAdapter.java | 6 +- .../PaperweightFaweWorldNativeAccess.java | 16 ++- .../v1_20_R3/PaperweightPlatformAdapter.java | 12 +- .../fawe/v1_20_R3/regen/PaperweightRegen.java | 6 +- .../bukkit/adapter/FaweAdapter.java | 104 +++++++++++++++++- .../bukkit/adapter/StarlightRelighter.java | 5 +- .../bukkit/util/BukkitTaskManager.java | 9 +- .../bukkit/util/FoliaTaskManager.java | 36 +++++- .../sk89q/worldedit/bukkit/BukkitPlayer.java | 27 +++-- .../core/util/FoliaSupport.java | 31 ++++++ .../core/util/TaskManager.java | 2 + .../core/wrappers/AsyncPlayer.java | 4 +- 16 files changed, 244 insertions(+), 111 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index df1d9e0b9..da44e48a7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -85,7 +85,7 @@ allprojects { applyCommonConfiguration() val supportedVersions = listOf("1.18.2", "1.19.4", "1.20", "1.20.4") -val foliaSupportedVersions = listOf("1.20.1") +val foliaSupportedVersions = listOf("1.20.2") tasks { fun registerVersion(version: String, software: String, task: RunServer.() -> Unit = {}) { 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 ffe067ec9..a3eef00a8 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 @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.NbtUtils; +import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightStarlightRelighter.java index 0b7b2c718..dd3f47c1f 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightStarlightRelighter.java @@ -35,82 +35,21 @@ public class PaperweightStarlightRelighter extends StarlightRelighter fixLightingSafe(true)); - } finally { - this.areaLock.unlock(); - } + protected CompletableFuture chunkLoadFuture(final ChunkPos chunkPos) { + return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z) + .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel( + FAWE_TICKET, + chunkPos, + LIGHT_LEVEL, + Unit.INSTANCE + )); } - /* - * Processes a set of chunks and runs an action afterwards. - * The action is run async, the chunks are partly processed on the main thread - * (as required by the server). - */ - private void fixLighting(LongSet chunks, Runnable andThen) { - // convert from long keys to ChunkPos - Set coords = new HashSet<>(); - LongIterator iterator = chunks.iterator(); - 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 - 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, - IntConsumer processCallback + @Override + protected void invokeRelight( + final Set coords, + final Consumer chunkCallback, + final IntConsumer processCallback ) { try { serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback); diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java index 75ab999c1..4ba80a73c 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java @@ -2,6 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.task.RunnableVal; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -21,6 +22,7 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -58,7 +60,10 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess serverLevel + PaperweightPlatformAdapter.task(() -> serverLevel .getChunkSource() - .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE), + serverLevel, chunkX, chunkZ + ); } public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java index ad04e7b03..451fb3740 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java @@ -2,7 +2,7 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.math.IntPair; -import com.fastasyncworldedit.core.util.TaskManager; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.task.RunnableVal; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; @@ -15,12 +15,13 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; @@ -58,7 +59,10 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess TaskManager.taskManager().sync(runnableVal)); + // TODO + // TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal)); } @Override @@ -269,7 +274,8 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess Bukkit.getWorld(world) != null); + boolean loaded = TaskManager.taskManager().syncGlobal(() -> Bukkit.getWorld(world) != null); if (loaded) { LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); // Retry chunk load @@ -313,7 +313,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { e.printStackTrace(); } } - return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); + return TaskManager.taskManager().syncGlobal(() -> serverLevel.getChunk(chunkX, chunkZ)); } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { @@ -374,7 +374,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { ); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); - }); + }, BukkitAdapter.adapt(nmsWorld.getWorld()), chunkX, chunkZ); } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -674,6 +674,10 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { return List.of(); } + public static void task(Runnable task, ServerLevel level, int chunkX, int chunkZ) { + TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + record FakeIdMapBlock(int size) implements IdMap { @Override diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java index edf9e9f90..a2cc22a95 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java @@ -4,13 +4,13 @@ import com.fastasyncworldedit.bukkit.adapter.Regenerator; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightGetBlocks; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightPlatformAdapter; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.regions.Region; @@ -438,11 +438,11 @@ public class PaperweightRegen extends Regenerator { + PaperweightPlatformAdapter.task(() -> { final CraftWorld world = freshWorld.getWorld(); final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); blockPopulator.populate(world, random, chunk); - }); + }, freshWorld, levelChunk.getPos().x, levelChunk.getPos().z); } @Override diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java index e116daf17..135ae595e 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.bukkit.adapter; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -11,7 +12,16 @@ import org.bukkit.TreeType; import org.bukkit.World; import org.bukkit.block.BlockState; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; import java.util.List; +import java.util.Map; + +import static com.fastasyncworldedit.core.util.FoliaSupport.getRethrowing; +import static com.fastasyncworldedit.core.util.FoliaSupport.runRethrowing; +import static java.lang.invoke.MethodType.methodType; /** * A base class for version-specific implementations of the BukkitImplAdapter @@ -21,6 +31,39 @@ import java.util.List; */ public abstract class FaweAdapter extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { + private static final VarHandle CAPTURE_TREE_GENERATION; + private static final VarHandle CAPTURE_BLOCK_STATES; + private static final MethodHandle CAPTURED_BLOCK_STATES; + private static final MethodHandle GET_CURRENT_WORLD_DATA; + + static { + VarHandle captureTreeGeneration = null; + VarHandle captureBlockStates = null; + MethodHandle capturedBlockStates = null; + MethodHandle getCurrentWorldData = null; + if (FoliaSupport.isFolia()) { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + Class regionizedWorldDataClass = Class.forName("io.papermc.paper.threadedregions.RegionizedWorldData"); + Class serverLevelClass = regionizedWorldDataClass.getDeclaredField("world").getType(); + captureTreeGeneration = lookup.findVarHandle(regionizedWorldDataClass, "captureTreeGeneration", boolean.class); + captureBlockStates = lookup.findVarHandle(regionizedWorldDataClass, "captureBlockStates", boolean.class); + capturedBlockStates = lookup.findGetter(regionizedWorldDataClass, "capturedBlockStates", Map.class); + getCurrentWorldData = lookup.findVirtual( + serverLevelClass, + "getCurrentWorldData", + methodType(regionizedWorldDataClass) + ); + } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError("Incompatible Folia version", e); + } + } + CAPTURE_TREE_GENERATION = captureTreeGeneration; + CAPTURE_BLOCK_STATES = captureBlockStates; + CAPTURED_BLOCK_STATES = capturedBlockStates; + GET_CURRENT_WORLD_DATA = getCurrentWorldData; + } + @Override public boolean generateTree( final TreeGenerator.TreeType treeType, @@ -35,17 +78,17 @@ public abstract class FaweAdapter extends CachedBukkitAdapter } BlockVector3 target = blockVector3; SERVER_LEVEL serverLevel = getServerLevel(world); - List placed = TaskManager.taskManager().sync(() -> { - preCaptureStates(serverLevel); + List placed = TaskManager.taskManager().syncAt(() -> { + preCaptureStatesCommon(serverLevel); try { if (!world.generateTree(BukkitAdapter.adapt(world, target), bukkitType)) { return null; } - return getCapturedBlockStatesCopy(serverLevel); + return getCapturedBlockStatesCopyCommon(serverLevel); } finally { - postCaptureBlockStates(serverLevel); + postCaptureBlockStatesCommon(serverLevel); } - }); + }, BukkitAdapter.adapt(world), blockVector3.getBlockX() >> 4, blockVector3.getBlockZ() >> 4); if (placed == null || placed.isEmpty()) { return false; @@ -61,6 +104,57 @@ public abstract class FaweAdapter extends CachedBukkitAdapter return true; } + private void preCaptureStatesCommon(SERVER_LEVEL serverLevel) { + if (FoliaSupport.isFolia()) { + preCaptureStatesFolia(serverLevel); + } else { + preCaptureStates(serverLevel); + } + } + + private List getCapturedBlockStatesCopyCommon(SERVER_LEVEL serverLevel) { + if (FoliaSupport.isFolia()) { + return getCapturedBlockStatesCopyFolia(serverLevel); + } else { + return getCapturedBlockStatesCopy(serverLevel); + } + } + + private void postCaptureBlockStatesCommon(SERVER_LEVEL serverLevel) { + if (FoliaSupport.isFolia()) { + postCaptureBlockStatesFolia(serverLevel); + } else { + postCaptureBlockStates(serverLevel); + } + } + + private void preCaptureStatesFolia(SERVER_LEVEL serverLevel) { + runRethrowing(() -> { + Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel); + CAPTURE_TREE_GENERATION.set(currentWorldData, true); + CAPTURE_BLOCK_STATES.set(currentWorldData, true); + }); + } + + private List getCapturedBlockStatesCopyFolia(SERVER_LEVEL serverLevel) { + return getRethrowing(() -> { + Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel); + @SuppressWarnings("unchecked") + var capturedBlockStates = (Map) CAPTURED_BLOCK_STATES.invoke(currentWorldData); + return new ArrayList<>(capturedBlockStates.values()); + }); + } + + private void postCaptureBlockStatesFolia(SERVER_LEVEL serverLevel) { + runRethrowing(() -> { + Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel); + CAPTURE_TREE_GENERATION.set(currentWorldData, false); + CAPTURE_BLOCK_STATES.set(currentWorldData, false); + var capturedBlockStates = (Map) CAPTURED_BLOCK_STATES.invoke(currentWorldData); + capturedBlockStates.clear(); + }); + } + protected abstract void preCaptureStates(SERVER_LEVEL serverLevel); protected abstract List getCapturedBlockStatesCopy(SERVER_LEVEL serverLevel); diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/StarlightRelighter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/StarlightRelighter.java index 02729cfa9..a9deefe2a 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/StarlightRelighter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/StarlightRelighter.java @@ -78,7 +78,8 @@ public abstract class StarlightRelighter implements Rel LOGGER.warn("Processed {} chunks instead of {}", i, coords.size()); } // post process chunks on main thread - TaskManager.taskManager().task(() -> postProcessChunks(coords)); + // safe to run globally on Folia as ticket modification is behind a lock + TaskManager.taskManager().taskGlobal(() -> postProcessChunks(coords)); // call callback on our own threads TaskManager.taskManager().async(andThen); }; @@ -99,7 +100,7 @@ public abstract class StarlightRelighter implements Rel */ protected void fixLighting(LongSet chunks, Runnable andThen) { Set coords = convertChunkKeysToChunkPos(chunks); - TaskManager.taskManager().task(() -> { + TaskManager.taskManager().taskGlobal(() -> { // trigger chunk load and apply ticket on main thread List> futures = chunkLoadFutures(coords); // collect futures and trigger relight once all chunks are loaded 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 9f500f155..7c25e996d 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 @@ -33,17 +33,22 @@ public class BukkitTaskManager extends TaskManager { @Override public void task(@NotNull final Runnable runnable, @NotNull final Location location) { - + this.plugin.getServer().getScheduler().runTask(this.plugin, runnable); } @Override public void task(@NotNull final Runnable runnable, @NotNull final World world, final int chunkX, final int chunkZ) { + this.plugin.getServer().getScheduler().runTask(this.plugin, runnable); + } + @Override + public void taskGlobal(final Runnable runnable) { + this.plugin.getServer().getGlobalRegionScheduler().run(this.plugin, task -> runnable.run()); } @Override public void later(@NotNull final Runnable runnable, final Location location, final int delay) { - + this.plugin.getServer().getScheduler().runTaskLater(this.plugin, runnable, delay); } @Override 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 index f6ab9e1c7..c67720bb7 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.bukkit.util; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; @@ -9,6 +10,7 @@ import com.sk89q.worldedit.world.World; import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; +import java.lang.invoke.MethodHandle; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; @@ -16,8 +18,21 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Supplier; +import static java.lang.invoke.MethodHandles.lookup; +import static java.lang.invoke.MethodType.methodType; + public class FoliaTaskManager extends TaskManager { + private final static MethodHandle IS_GLOBAL_TICK_THREAD; + + static { + try { + IS_GLOBAL_TICK_THREAD = lookup().findStatic(Bukkit.class, "isGlobalTickThread", methodType(boolean.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError("Incompatile Folia version", e); + } + } + private final AtomicInteger idCounter = new AtomicInteger(); @Override @@ -49,6 +64,11 @@ public class FoliaTaskManager extends TaskManager { ); } + @Override + public void taskGlobal(final Runnable runnable) { + Bukkit.getGlobalRegionScheduler().run(WorldEditPlugin.getInstance(), asConsumer(runnable)); + } + @Override public void later(@NotNull final Runnable runnable, final Location location, final int delay) { Bukkit.getRegionScheduler().runDelayed( @@ -80,6 +100,7 @@ public class FoliaTaskManager extends TaskManager { if (Bukkit.isOwnedByCurrentRegion(adapt, chunkX, chunkZ)) { return supplier.get(); } + ensureOffTickThread(); FutureTask task = new FutureTask<>(supplier::get); Bukkit.getRegionScheduler().run( WorldEditPlugin.getInstance(), @@ -105,6 +126,7 @@ public class FoliaTaskManager extends TaskManager { if (Bukkit.isOwnedByCurrentRegion(adapt)) { return supplier.get(); } + ensureOffTickThread(); FutureTask task = new FutureTask<>(supplier::get); adapt.getScheduler().execute(WorldEditPlugin.getInstance(), task, null, 0); try { @@ -117,6 +139,9 @@ public class FoliaTaskManager extends TaskManager { @Override public T syncGlobal(final Supplier supplier) { // TODO avoid deadlocks (Bukkit.isGlobalTickThread not available at time of writing) + if (isGlobalTickThread()) { + return supplier.get(); + } FutureTask task = new FutureTask<>(supplier::get); Bukkit.getGlobalRegionScheduler().run(WorldEditPlugin.getInstance(), asConsumer(task)); try { @@ -126,12 +151,21 @@ public class FoliaTaskManager extends TaskManager { } } + private boolean isGlobalTickThread() { + return FoliaSupport.getRethrowing(() -> (boolean) IS_GLOBAL_TICK_THREAD.invokeExact()); + } + + private void ensureOffTickThread() { + if (FoliaSupport.isTickThread()) { + throw new IllegalStateException("Expected to be off tick thread"); + } + } + private int ticksToMs(int ticks) { // 1 tick = 50ms return ticks * 50; } - private T fail(String message) { throw new UnsupportedOperationException(message); } 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 286420aa0..33f5cbfa8 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 @@ -21,6 +21,7 @@ package com.sk89q.worldedit.bukkit; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.util.StringUtil; import com.sk89q.wepif.VaultResolver; @@ -52,6 +53,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; +import io.papermc.lib.PaperLib; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -69,7 +71,9 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; public class BukkitPlayer extends AbstractPlayerActor { @@ -239,16 +243,21 @@ public class BukkitPlayer extends AbstractPlayerActor { } } org.bukkit.World finalWorld = world; + final Location target = new Location(finalWorld, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch); + Supplier> teleport = () -> PaperLib.teleportAsync(player, target); + if (FoliaSupport.isTickThread()) { + teleport.get().whenComplete((b, thr) -> { + if (thr != null) { + thr.printStackTrace(); + } + if (!b) { + player.sendMessage("Teleportation failed"); + } + }); + return true; // TODO this might not be correct + } + return TaskManager.taskManager().syncWith(() -> teleport.get().join(), this); //FAWE end - // 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/util/FoliaSupport.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/FoliaSupport.java index f8458dc19..acb432497 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/FoliaSupport.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/FoliaSupport.java @@ -35,4 +35,35 @@ public final class FoliaSupport { public static boolean isTickThread() { return TICK_THREAD_CLASS.isInstance(Thread.currentThread()); } + + + @FunctionalInterface + public interface ThrowingSupplier { + + T get() throws Throwable; + + } + @FunctionalInterface + public interface ThrowingRunnable { + + void run() throws Throwable; + + } + + public static void runRethrowing(ThrowingRunnable runnable) { + getRethrowing(() -> { + runnable.run(); + return null; + }); + } + + public static T getRethrowing(ThrowingSupplier supplier) { + try { + return supplier.get(); + } catch (RuntimeException | Error e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } } 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 ec2d59993..56ab75381 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 @@ -73,6 +73,8 @@ public abstract class TaskManager { public abstract void task(@Nonnull final Runnable runnable, @Nonnull World world, int chunkX, int chunkZ); + public abstract void taskGlobal(Runnable runnable); + /** * Get the public ForkJoinPool. * - ONLY SUBMIT SHORT LIVED TASKS
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 cfd77b57e..3316e7d6b 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 @@ -40,7 +40,7 @@ public class AsyncPlayer extends PlayerProxy { @Override public void findFreePosition(Location searchPos) { - TaskManager.taskManager().syncAt(new RunnableVal() { + TaskManager.taskManager().task(new RunnableVal() { @Override public void run(Boolean value) { getBasePlayer().findFreePosition(searchPos); @@ -50,7 +50,7 @@ public class AsyncPlayer extends PlayerProxy { @Override public void setOnGround(Location searchPos) { - TaskManager.taskManager().syncAt(new RunnableVal() { + TaskManager.taskManager().task(new RunnableVal() { @Override public void run(Boolean value) { getBasePlayer().setOnGround(searchPos);