diff --git a/build.gradle.kts b/build.gradle.kts index 2879f61072..17c4d72fa7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,9 @@ import org.ajoberstar.grgit.Grgit import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED +import xyz.jpenilla.runpaper.task.RunServer import java.net.URI import java.time.format.DateTimeFormatter -import xyz.jpenilla.runpaper.task.RunServer plugins { id("io.github.gradle-nexus.publish-plugin") version "1.3.0" @@ -102,6 +102,15 @@ tasks { .toTypedArray()) } + register("runFolia") { + downloadsApiService.set(xyz.jpenilla.runtask.service.DownloadsAPIService.folia(project)) + minecraftVersion("1.19.4") + group = "run paper" + runDirectory.set(file("run-folia")) + jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true") + pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } + .toTypedArray()) + } } nexusPublishing { diff --git a/settings.gradle.kts b/settings.gradle.kts index 9121dc6ac2..911fecf2f6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,7 +5,6 @@ include("worldedit-libs") listOf("legacy", "1_17_1", "1_18_2", "1_19", "1_19_3","1_19_4").forEach { include("worldedit-bukkit:adapters:adapter-$it") } - listOf("bukkit", "core", "cli").forEach { include("worldedit-libs:$it") include("worldedit-$it") @@ -21,5 +20,14 @@ dependencyResolutionManagement { } } } +pluginManagement { + repositories { + gradlePluginPortal() + maven { + name = "jmp repository" + url = uri("https://repo.jpenilla.xyz/snapshots") + } + } +} enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweWorldNativeAccess.java index 282a6b5058..9bb5249ffa 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweWorldNativeAccess.java @@ -95,7 +95,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta net.minecraft.world.level.block.state.BlockState blockState ) { int currentTick = MinecraftServer.currentTick; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) ); @@ -266,7 +266,7 @@ public void run(Object value) { } } }; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { runnableVal.run(); } else { TaskManager.taskManager().sync(runnableVal); diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java index 69ab5fe9b6..d22de89b96 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java @@ -205,7 +205,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c if (nmsChunk != null) { return nmsChunk; } - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } } else { @@ -220,7 +220,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c return nmsChunk; } // Avoid "async" methods from the main thread. - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweWorldNativeAccess.java index ef7fc98ac0..f15f48d70a 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweWorldNativeAccess.java @@ -95,7 +95,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta net.minecraft.world.level.block.state.BlockState blockState ) { int currentTick = MinecraftServer.currentTick; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) ); @@ -266,7 +266,7 @@ public void run(Object value) { } } }; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { runnableVal.run(); } else { TaskManager.taskManager().sync(runnableVal); diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java index 8e982a84e0..070dd0ae70 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightPlatformAdapter.java @@ -233,7 +233,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c if (nmsChunk != null) { return nmsChunk; } - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } } else { @@ -248,7 +248,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c return nmsChunk; } // Avoid "async" methods from the main thread. - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightFaweWorldNativeAccess.java index 0e7f6ea0cb..986e96c0aa 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightFaweWorldNativeAccess.java @@ -95,7 +95,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta net.minecraft.world.level.block.state.BlockState blockState ) { int currentTick = MinecraftServer.currentTick; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) ); @@ -266,7 +266,7 @@ public void run(Object value) { } } }; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { runnableVal.run(); } else { TaskManager.taskManager().sync(runnableVal); diff --git a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java index 2fcb722256..ffa2621e02 100644 --- a/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R1/PaperweightPlatformAdapter.java @@ -266,7 +266,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c if (nmsChunk != null) { return nmsChunk; } - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } } else { @@ -281,7 +281,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c return nmsChunk; } // Avoid "async" methods from the main thread. - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); 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 af0bcfa32e..da9b63b356 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..dc41e458dd 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() { @@ -95,7 +96,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta net.minecraft.world.level.block.state.BlockState blockState ) { int currentTick = MinecraftServer.currentTick; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) ); @@ -266,7 +267,7 @@ public void run(Object value) { } } }; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { runnableVal.run(); } else { TaskManager.taskManager().sync(runnableVal); 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 5f8e399404..363bf711ab 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; @@ -263,7 +267,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c if (nmsChunk != null) { return nmsChunk; } - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } } else { @@ -278,7 +282,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c return nmsChunk; } // Avoid "async" methods from the main thread. - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); @@ -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/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java index e106c7d569..935b0d53fa 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java @@ -172,6 +172,8 @@ public final class PaperweightAdapter implements BukkitImplAdapter>> allBlockProperties = null; + // Folia - START + private MethodHandle currentWorldData; + + private Class regionizedWorldData; + + private Field captureTreeGeneration; + private Field captureBlockStates; + private Field capturedBlockStates; + public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException { this.parent = new PaperweightAdapter(); + if (this.parent.isFolia()) { + Method getCurrentWorldData = ServerLevel.class.getSuperclass().getDeclaredMethod( + "getCurrentWorldData" + ); + getCurrentWorldData.setAccessible(true); + try { + currentWorldData = MethodHandles.lookup().unreflect(getCurrentWorldData); + } catch (IllegalAccessException e) { + } + + try { + regionizedWorldData = Class.forName("io.papermc.paper.threadedregions.RegionizedWorldData"); + } catch (ClassNotFoundException e) { + } + if (regionizedWorldData != null) { + final Field captureTreeGeneration = regionizedWorldData.getDeclaredField("captureTreeGeneration"); + captureTreeGeneration.setAccessible(true); + this.captureTreeGeneration = captureTreeGeneration; + + final Field captureBlockStates = regionizedWorldData.getDeclaredField("captureBlockStates"); + captureBlockStates.setAccessible(true); + this.captureBlockStates = captureBlockStates; + + final Field capturedBlockStates = regionizedWorldData.getDeclaredField("capturedBlockStates"); + capturedBlockStates.setAccessible(true); + this.capturedBlockStates = capturedBlockStates; + } + } } @Nullable @@ -527,17 +567,37 @@ public boolean generateTree( final BlockVector3 finalBlockVector = blockVector3; // Sync to main thread to ensure no clashes occur Map placed = TaskManager.taskManager().sync(() -> { - serverLevel.captureTreeGeneration = true; - serverLevel.captureBlockStates = true; - try { - if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) { - return null; + if (this.parent.isFolia()) { + try { + var data = currentWorldData.invoke(serverLevel); + captureBlockStates.setBoolean(data, true); + captureTreeGeneration.setBoolean(data, true); + try { + if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) { + return null; + } + return ImmutableMap.copyOf((Map) capturedBlockStates.get(data)); + } finally { + captureBlockStates.setBoolean(data, false); + captureTreeGeneration.setBoolean(data, false); + ((Map) capturedBlockStates.get(data)).clear(); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + } else { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + try { + if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) { + return null; + } + return ImmutableMap.copyOf(serverLevel.capturedBlockStates); + } finally { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); } - return ImmutableMap.copyOf(serverLevel.capturedBlockStates); - } finally { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); } }); if (placed == null || placed.isEmpty()) { diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweWorldNativeAccess.java index dbe0150a9c..08115df406 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweWorldNativeAccess.java @@ -5,6 +5,7 @@ import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.task.RunnableVal; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3.PaperweightAdapter; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.util.SideEffect; @@ -26,6 +27,8 @@ import org.bukkit.event.block.BlockPhysicsEvent; import javax.annotation.Nullable; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashSet; @@ -51,6 +54,10 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess cachedChanges = new HashSet<>(); private final Set cachedChunksToSend = new HashSet<>(); + private final MethodHandle globalTickData; + private final Class regionScheduleHandleClass; + private final Class regionizedServerClass; + private final MethodHandle globalCurrentTick; private SideEffectSet sideEffectSet; public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference level) { @@ -58,7 +65,30 @@ 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); + PaperweightAdapter adapter = (PaperweightAdapter) paperweightFaweAdapter.getParent(); + if (adapter.isFolia()) { + try { + regionizedServerClass = Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + regionScheduleHandleClass = Class.forName( + "io.papermc.paper.threadedregions.TickRegionScheduler$RegionScheduleHandle"); + globalTickData = MethodHandles.lookup().unreflect(regionizedServerClass.getDeclaredMethod("getGlobalTickData")); + var data = globalTickData.invoke(); + globalCurrentTick = MethodHandles.lookup().unreflect(regionScheduleHandleClass.getDeclaredMethod( + "getCurrentTick")); + final long tick = (long) globalCurrentTick.invoke(data); + this.lastTick = new AtomicInteger((int) tick); + } catch (Throwable e) { + throw new RuntimeException(e); + } + + } else { + this.globalTickData = null; + this.regionScheduleHandleClass = null; + this.regionizedServerClass = null; + this.globalCurrentTick = null; + this.lastTick = new AtomicInteger(MinecraftServer.currentTick); + } + } private Level getLevel() { @@ -94,13 +124,24 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta LevelChunk levelChunk, BlockPos blockPos, net.minecraft.world.level.block.state.BlockState blockState ) { - int currentTick = MinecraftServer.currentTick; - if (Fawe.isMainThread()) { + int currentTick; + PaperweightAdapter adapter = (PaperweightAdapter) paperweightFaweAdapter.getParent(); + if (adapter.isFolia()) { + try { + var data = globalTickData.invoke(); + currentTick = (int) globalCurrentTick.invoke(data); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } else { + currentTick = MinecraftServer.currentTick; + } + + if (Fawe.isTickThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) ); } - // Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( ) cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState)); cachedChunksToSend.add(new IntPair(levelChunk.locX, levelChunk.locZ)); boolean nextTick = lastTick.get() > currentTick; @@ -110,6 +151,7 @@ public synchronized net.minecraft.world.level.block.state.BlockState setBlockSta } flushAsync(nextTick); } + return blockState; } @@ -266,7 +308,7 @@ public void run(Object value) { } } }; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { runnableVal.run(); } else { TaskManager.taskManager().sync(runnableVal); diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java index c12f57002c..26317c460d 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java @@ -12,6 +12,7 @@ import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks; import com.fastasyncworldedit.core.util.MathMan; +import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.google.common.base.Suppliers; import com.sk89q.jnbt.CompoundTag; @@ -19,11 +20,15 @@ import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; @@ -80,6 +85,7 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import static net.minecraft.core.registries.Registries.BIOME; @@ -237,13 +243,32 @@ public void removeSectionLighting(int layer, boolean sky) { @Override public CompoundTag getTile(int x, int y, int z) { - BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + ( - chunkX << 4), y, (z & 15) + ( - chunkZ << 4))); - if (blockEntity == null) { - return null; + PaperweightAdapter adapter = (PaperweightAdapter) this.adapter.getParent(); + if (adapter.isFolia()) { + Supplier entity = () -> { + var pos = new BlockPos((x & 15) + ( + chunkX << 4), y, (z & 15) + ( + chunkZ << 4)); + return getChunk().getBlockEntity(pos); + }; + + if (entity == null) { + return null; + } + return new PaperweightLazyCompoundTag(Suppliers.memoize(TaskManager.taskManager().syncAt( + entity, + new Location(new BukkitWorld(serverLevel.getWorld()), Vector3.at(x, y, z)) + )::saveWithId)); + } else { + BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + ( + chunkX << 4), y, (z & 15) + ( + chunkZ << 4))); + if (blockEntity == null) { + return null; + } + return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)); } - return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)); + } @Override @@ -758,8 +783,8 @@ public synchronized > T call(IChunkSet set, Runnable finaliz final int y = blockHash.getY(); final int z = blockHash.getZ() + bz; final BlockPos pos = new BlockPos(x, y, z); - - synchronized (nmsWorld) { + PaperweightAdapter adapter = (PaperweightAdapter) this.adapter.getParent(); + if (adapter.isFolia()) { BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); if (tileEntity == null || tileEntity.isRemoved()) { nmsWorld.removeBlockEntity(pos); @@ -773,7 +798,24 @@ public synchronized > T call(IChunkSet set, Runnable finaliz tag.put("z", IntTag.valueOf(z)); tileEntity.load(tag); } + } else { + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( + nativeTag); + tag.put("x", IntTag.valueOf(x)); + tag.put("y", IntTag.valueOf(y)); + tag.put("z", IntTag.valueOf(z)); + tileEntity.load(tag); + } + } } + } }; } diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java index 11dadaf1e4..a1b6a5d8b5 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java @@ -122,9 +122,20 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static Field LEVEL_CHUNK_ENTITIES; private static Field SERVER_LEVEL_ENTITY_MANAGER; + private static boolean FOLIA_SUPPORT; + static { final MethodHandles.Lookup lookup = MethodHandles.lookup(); try { + boolean isFolia = false; + try { + // Assume API is present + Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler"); + isFolia = true; + } catch (Exception unused) { + + } + FOLIA_SUPPORT = isFolia; fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d")); fieldData.setAccessible(true); @@ -222,13 +233,18 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { throw new RuntimeException(rethrow); } MethodHandle craftChunkGetHandle; - final MethodType type = methodType(ChunkAccess.class); + final MethodType type = methodType(ChunkAccess.class, ChunkStatus.class); try { - craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", type); + if (FOLIA_SUPPORT) { + craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", methodType(LevelChunk.class)); + } else { + craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", type); + } + } catch (NoSuchMethodException | IllegalAccessException e) { try { final MethodType newType = methodType(ChunkAccess.class, ChunkStatus.class); - craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", newType); + craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", newType); // Todo: Check folia craftChunkGetHandle = MethodHandles.insertArguments(craftChunkGetHandle, 1, ChunkStatus.FULL); } catch (NoSuchMethodException | IllegalAccessException ex) { throw new RuntimeException(ex); @@ -288,28 +304,34 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c if (nmsChunk != null) { return nmsChunk; } - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } } else { LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); if (nmsChunk != null) { - addTicket(serverLevel, chunkX, chunkZ); + if(!FOLIA_SUPPORT) {// TODO: Dirty folia workaround - Needs be discussed with FAWE members + addTicket(serverLevel, chunkX, chunkZ); + } return nmsChunk; } nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); if (nmsChunk != null) { - addTicket(serverLevel, chunkX, chunkZ); + if(!FOLIA_SUPPORT) {// TODO: Dirty folia workaround - Needs be discussed with FAWE members + addTicket(serverLevel, chunkX, chunkZ); + } return nmsChunk; } // Avoid "async" methods from the main thread. - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return serverLevel.getChunk(chunkX, chunkZ); } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { CraftChunk chunk = (CraftChunk) future.get(); - addTicket(serverLevel, chunkX, chunkZ); + if(!FOLIA_SUPPORT) {// TODO: Dirty folia workaround - Needs be discussed with FAWE members + addTicket(serverLevel, chunkX, chunkZ); + } return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); } catch (Throwable e) { e.printStackTrace(); diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java index 4f22e87346..6d1b7192d4 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java @@ -274,9 +274,6 @@ protected boolean initNewWorld() throws Exception { WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) ) : null; - @Override - public void tick(BooleanSupplier shouldKeepTicking) { //no ticking - } @Override public Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index a6c5147ad6..ae59095be7 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -50,7 +50,6 @@ val adapters = configurations.create("adapters") { attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, objects.named(Obfuscation.OBFUSCATED)) } } - dependencies { // Modules api(projects.worldeditCore) @@ -64,6 +63,11 @@ dependencies { implementation(libs.fastutil) // Platform expectations + compileOnly("dev.folia:folia-api:1.19.4-R0.1-SNAPSHOT") { + exclude("junit", "junit") + exclude(group = "org.slf4j", module = "slf4j-api") + exclude(group = "net.kyori") + } compileOnly("io.papermc.paper:paper-api") { exclude("junit", "junit") exclude(group = "org.slf4j", module = "slf4j-api") 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 5d2e35335f..973f6a27a6 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; diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java index 83de13f667..46a096e53d 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateSchematicHandler.java @@ -159,7 +159,7 @@ public void paste( } } }; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { com.fastasyncworldedit.core.util.TaskManager.taskManager().async(r); } else { r.run(); 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..f1d96ca1db --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java @@ -0,0 +1,221 @@ +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) { + Bukkit.getGlobalRegionScheduler().runAtFixedRate( + WorldEditPlugin.getInstance(), + scheduledTask -> runnable.run(), + 1, + interval + ); + return 0; + } + + @Override + public int repeatAsync(@NotNull final Runnable runnable, final int interval) { + Bukkit.getAsyncScheduler().runAtFixedRate( + WorldEditPlugin.getInstance(), + scheduledTask -> runnable.run(), + 0, + ticksToMs(interval), + TimeUnit.MILLISECONDS + ); + return idCounter.getAndIncrement(); + } + + @Override + public void async(@NotNull final Runnable runnable) { + Bukkit.getAsyncScheduler().runNow(WorldEditPlugin.getInstance(), (s) -> runnable.run()); + } + + @Override + public void task(@NotNull final Runnable runnable) { + Bukkit.getGlobalRegionScheduler().execute(WorldEditPlugin.getInstance(), runnable); + } + + @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) { + Bukkit.getGlobalRegionScheduler().runDelayed(WorldEditPlugin.getInstance(), scheduledTask -> runnable.run(), delay); + } + + @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.FoliaRegionScheduler"); + 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..10e07892eb 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 @@ 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; @@ -70,6 +71,7 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; public class BukkitPlayer extends AbstractPlayerActor { @@ -161,7 +163,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 +185,7 @@ public void giveItem(BaseItemStack itemStack) { } player.updateInventory(); return null; - }); + }, this); } //FAWE end @@ -240,14 +242,30 @@ public boolean trySetPosition(Vector3 pos, float pitch, float yaw) { } org.bukkit.World finalWorld = world; //FAWE end - return TaskManager.taskManager().sync(() -> player.teleport(new Location( + if (FoliaSupport.isFolia()) { + return TaskManager.taskManager().syncWith(() -> { + try { + return player.teleportAsync(new Location( + finalWorld, + pos.getX(), + pos.getY(), + pos.getZ(), + yaw, + pitch + )).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + }, this); + } + return TaskManager.taskManager().syncWith(() -> player.teleport(new Location( finalWorld, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch - ))); + )), this); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 769bcbd039..a37d010784 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -353,7 +353,7 @@ public void checkLoadedChunk(BlockVector3 pt) { //FAWE start int X = pt.getBlockX() >> 4; int Z = pt.getBlockZ() >> 4; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { world.getChunkAt(X, Z); } else if (PaperLib.isPaper()) { PaperLib.getChunkAtAsync(world, X, Z, true); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index 876517dd69..18a15e3dd7 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -21,6 +21,7 @@ import com.fastasyncworldedit.bukkit.BukkitPermissionAttachmentManager; import com.fastasyncworldedit.bukkit.FaweBukkit; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.UpdateNotification; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.util.WEManager; @@ -396,7 +397,9 @@ public void onDisable() { if (config != null) { config.unload(); } - this.getServer().getScheduler().cancelTasks(this); + if (!FoliaSupport.isFolia()) { + this.getServer().getScheduler().cancelTasks(this); + } } /** diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java index 5cf783350c..d18958b1eb 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplLoader.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.bukkit.adapter; import com.fastasyncworldedit.bukkit.util.MinecraftVersion; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.util.io.Closer; import org.apache.logging.log4j.Logger; @@ -44,9 +45,9 @@ public class BukkitImplLoader { private int zeroth = 0; private String customCandidate; - private static final String SEARCH_PACKAGE = "com.sk89q.worldedit.bukkit.adapter.impl.fawe"; - private static final String SEARCH_PACKAGE_DOT = SEARCH_PACKAGE + "."; - private static final String SEARCH_PATH = SEARCH_PACKAGE.replace(".", "/"); + private static String SEARCH_PACKAGE = "com.sk89q.worldedit.bukkit.adapter.impl.fawe"; + private static String SEARCH_PACKAGE_DOT = SEARCH_PACKAGE + "."; + private static String SEARCH_PATH = SEARCH_PACKAGE.replace(".", "/"); private static final String CLASS_SUFFIX = ".class"; private static final String LOAD_ERROR_MESSAGE = diff --git a/worldedit-bukkit/src/main/resources/plugin.yml b/worldedit-bukkit/src/main/resources/plugin.yml index b4f8452652..33070b1d38 100644 --- a/worldedit-bukkit/src/main/resources/plugin.yml +++ b/worldedit-bukkit/src/main/resources/plugin.yml @@ -10,6 +10,7 @@ description: Blazingly fast world manipulation for builders, large networks and authors: [ Empire92, MattBDev, IronApollo, dordsor21, NotMyFault ] loadbefore: [ WorldGuard, PlotSquared ] database: false +folia-supported: true permissions: fawe.plotsquared: default: true 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 edde8ed490..fe4ea65a92 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.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; @@ -123,14 +124,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); + // } } /** @@ -191,10 +195,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/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index a6d035155d..4952c2f3f6 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -155,7 +155,7 @@ public V load(@Nonnull T key) { */ public LongFunction createMainThreadSafeCache(Supplier withInitial) { return new LongFunction<>() { - private final LoadingCache loadingCache = Fawe.isMainThread() ? null : FaweCache.INSTANCE.createCache( + private final LoadingCache loadingCache = Fawe.isTickThread() ? null : FaweCache.INSTANCE.createCache( withInitial); @Override 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/entity/LazyBaseEntity.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/entity/LazyBaseEntity.java index f591cf8265..cd8325cd27 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/entity/LazyBaseEntity.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/entity/LazyBaseEntity.java @@ -24,7 +24,7 @@ public CompoundBinaryTag getNbt() { Supplier tmp = saveTag; if (tmp != null) { saveTag = null; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { setNbt(tmp.get()); } else { setNbt(TaskManager.taskManager().sync(tmp)); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SlowExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SlowExtent.java index 6aa07b1169..78f299f077 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SlowExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SlowExtent.java @@ -27,7 +27,7 @@ public void delay() { increment += nanos; if (increment >= THRESHOLD) { long wait = increment / 1000000; - if (!Fawe.isMainThread()) { + if (!Fawe.isTickThread()) { try { Thread.sleep(wait); } catch (InterruptedException e) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index e52ef0cba5..58469f941f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -82,7 +82,7 @@ public void closeAsync() { @Override public void flush() { try { - if (!Fawe.isMainThread()) { + if (!Fawe.isTickThread()) { while (waitingAsync.get() > 0) { synchronized (waitingAsync) { waitingAsync.wait(1000); @@ -358,7 +358,7 @@ public void add(int x, int y, int z, int combinedFrom, BaseBlock to) { } public Future addWriteTask(Runnable writeTask) { - return addWriteTask(writeTask, Fawe.isMainThread()); + return addWriteTask(writeTask, Fawe.isTickThread()); } public Future addWriteTask(final Runnable writeTask, final boolean completeNow) { 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 49ebd84ca6..c50ee077f0 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 @@ -16,6 +16,7 @@ import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal; 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; @@ -61,8 +62,8 @@ public QueueHandler() { @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(); @@ -182,7 +183,7 @@ public Future syncWhenFree(Supplier call) { } private Future sync(Runnable run, T value, Queue queue) { - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { run.run(); return Futures.immediateFuture(value); } @@ -193,7 +194,7 @@ private Future sync(Runnable run, T value, Queue queue) { } private Future sync(Runnable run, Queue queue) { - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { run.run(); return Futures.immediateCancelledFuture(); } @@ -204,7 +205,7 @@ private Future sync(Runnable run, Queue queue) { } private Future sync(Callable call, Queue queue) throws Exception { - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return Futures.immediateFuture(call.call()); } final FutureTask result = new FutureTask<>(call); @@ -214,7 +215,7 @@ private Future sync(Callable call, Queue queue) throws Exc } private Future sync(Supplier call, Queue queue) { - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { return Futures.immediateFuture(call.get()); } final FutureTask result = new FutureTask<>(call::get); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index 8ad257c764..77e87cd928 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java @@ -238,7 +238,7 @@ private > V submitUnchecked(IQueueChunk chunk) { return (V) result; } - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { V result = (V) chunk.call(); if (result == null) { return (V) (Future) Futures.immediateFuture(null); 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 ad69c87d6b..fda56d8036 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.startSet(true); @@ -187,8 +199,9 @@ 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()) { + if (Fawe.isTickThread()) { runnable.run(); } else { task(runnable); @@ -199,10 +212,10 @@ public void taskNowMain(@Nonnull final Runnable runnable) { * Run a task as soon as possible not on the main thread. * * @param runnable the task to run - * @see Fawe#isMainThread() + * @see Fawe#isTickThread() */ public void taskNowAsync(@Nonnull final Runnable runnable) { - taskNow(runnable, Fawe.isMainThread()); + taskNow(runnable, Fawe.isTickThread()); } /** @@ -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,8 +312,9 @@ public void notify(AtomicBoolean running) { } } + @Deprecated public void taskWhenFree(@Nonnull Runnable run) { - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { run.run(); } else { Fawe.instance().getQueueHandler().sync(run); @@ -308,8 +326,9 @@ 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()) { + if (Fawe.isTickThread()) { function.run(); return function.value; } @@ -325,8 +344,9 @@ 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()) { + if (Fawe.isTickThread()) { return supplier.get(); } try { @@ -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,8 +371,9 @@ 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()) { + if (Fawe.isTickThread()) { return function.get(); } try { @@ -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 diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java index c235ca7845..8b586baab1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java @@ -467,7 +467,7 @@ public EditSessionBuilder compile() { wnaMode = false; if (unwrapped instanceof IQueueExtent) { extent = queue = (IQueueExtent) unwrapped; - } else if (Settings.settings().QUEUE.PARALLEL_THREADS > 1 && !Fawe.isMainThread()) { + } else if (Settings.settings().QUEUE.PARALLEL_THREADS > 1 && !Fawe.isTickThread()) { ParallelQueueExtent parallel = new ParallelQueueExtent(Fawe.instance().getQueueHandler(), world, fastMode); queue = parallel.getExtent(); extent = parallel; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 7b493cf985..9dcce4b13b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -687,7 +687,7 @@ public void handleCommand(CommandEvent event) { Command cmd = optional.get(); PermissionCondition queued = cmd.getCondition().as(PermissionCondition.class).orElse(null); if (queued != null && !queued.isQueued()) { - TaskManager.taskManager().taskNow(() -> handleCommandOnCurrentThread(event), Fawe.isMainThread()); + TaskManager.taskManager().taskNow(() -> handleCommandOnCurrentThread(event), Fawe.isTickThread()); return; } else { actor.decline();