diff --git a/build.gradle.kts b/build.gradle.kts index 02df265c09..40c492f1c4 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" @@ -84,7 +84,7 @@ allprojects { applyCommonConfiguration() val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.2") - +val foliaSupportedVersions = listOf("1.19.4", "1.20.1", "1.20.2") tasks { supportedVersions.forEach { register("runServer-$it") { @@ -102,6 +102,17 @@ tasks { .toTypedArray()) } + foliaSupportedVersions.forEach { + register("runFolia-$it") { + downloadsApiService.set(xyz.jpenilla.runtask.service.DownloadsAPIService.folia(project)) + minecraftVersion(it) + group = "run paper" + runDirectory.set(file("run-folia-$it")) + 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 d8b8012cf2..78e4d4f1f0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,7 +5,6 @@ include("worldedit-libs") listOf("1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2").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/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java index ad6e6ae804..c6c17642e3 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java @@ -88,6 +88,7 @@ import org.bukkit.entity.Player; import javax.annotation.Nullable; +import java.lang.invoke.MethodHandle; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; @@ -555,6 +556,11 @@ protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + @Override + protected MethodHandle getCurrentWorldData() { + return null; + } + @Override public List getEntities(org.bukkit.World world) { // Quickly add each entity to a list copy. 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 16dfd7bd8c..8132d0f4ce 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 @@ -204,7 +204,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 { @@ -219,7 +219,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/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java index 8b96e2ea6b..c7f4004bf3 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java @@ -89,6 +89,7 @@ import org.bukkit.entity.Player; import javax.annotation.Nullable; +import java.lang.invoke.MethodHandle; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; @@ -547,6 +548,11 @@ protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + @Override + protected MethodHandle getCurrentWorldData() { + return null; + } + @Override public List getEntities(org.bukkit.World world) { // Quickly add each entity to a list copy. 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 06281908d0..d4ec3e17d3 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 @@ -220,7 +220,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 { @@ -235,7 +235,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_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 22c5a07b20..13169c6065 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 @@ -173,6 +173,8 @@ public final class PaperweightAdapter implements BukkitImplAdapter getCapturedBlockStatesCopy(final ServerLevel serverLevel) { - return new ArrayList<>(serverLevel.capturedBlockStates.values()); - } - - @Override - protected void postCaptureBlockStates(final ServerLevel serverLevel) { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); - } - - @Override - protected ServerLevel getServerLevel(final World world) { - return ((CraftWorld) world).getHandle(); - } - @Override public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); @@ -611,4 +592,76 @@ private boolean wasAccessibleSinceLastSave(ChunkHolder holder) { return true; } + @Override + protected void preCaptureStates(final ServerLevel serverLevel) { + if (this.isFolia()) { + try { + var data = currentWorldData.invoke(serverLevel); + captureBlockStates.setBoolean(data, true); + captureTreeGeneration.setBoolean(data, true); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } else { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + } + + } + + @Override + protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { + return new ArrayList<>(serverLevel.capturedBlockStates.values()); + } + + // Folia - Start + @Override + protected void postCaptureBlockStates(final ServerLevel serverLevel) { + if (this.isFolia()) { + try { + var data = currentWorldData.invoke(serverLevel); + captureBlockStates.setBoolean(data, false); + captureTreeGeneration.setBoolean(data, false); + ((Map) capturedBlockStates.get(data)).clear(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } else { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + } + + // Folia - End + + @Override + protected ServerLevel getServerLevel(final World world) { + return ((CraftWorld) world).getHandle(); + } + + // Folia - Start + @Override + protected MethodHandle getCurrentWorldData() { + if (this.isFolia()) { + Method getCurrentWorldData; + try { + getCurrentWorldData = ServerLevel.class.getSuperclass().getDeclaredMethod( + "getCurrentWorldData" + ); + } catch (NoSuchMethodException e) { + return null; + } + getCurrentWorldData.setAccessible(true); + + try { + currentWorldData = MethodHandles.lookup().unreflect(getCurrentWorldData); + } catch (IllegalAccessException e) { + } + } + return null; + } + + // Folia - End + } 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 60695c1e86..cbcf09aafb 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; @@ -82,6 +87,7 @@ import java.util.concurrent.locks.ReentrantLock; 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; @@ -256,13 +262,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 @@ -789,8 +814,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); @@ -804,7 +829,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 d0ce1c5551..2d301976f3 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 @@ -119,9 +119,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); @@ -178,7 +189,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q")); fieldRemove.setAccessible(true); - boolean chunkRewrite; try { ServerLevel.class.getDeclaredMethod("getEntityLookup"); @@ -257,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 { @@ -272,11 +282,17 @@ 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); try { + /* + CraftChunk chunk = (CraftChunk) future.get(); + if(!FOLIA_SUPPORT) {// TODO: Dirty folia workaround - Needs be discussed with FAWE members + addTicket(serverLevel, chunkX, chunkZ); + } + */ CraftChunk chunk; try { chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); @@ -302,10 +318,17 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { - // Ensure chunk is definitely loaded before applying a ticket - io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel - .getChunkSource() - .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + if (FOLIA_SUPPORT) { + TaskManager.taskManager().taskNowMain(() -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + } else { + // Ensure chunk is definitely loaded before applying a ticket + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + } + } public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { @@ -445,12 +468,16 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot + @SuppressWarnings("deprecation") + // constructor is deprecated on paper, but needed to keep compatibility with spigot final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, PalettedContainer.Strategy.SECTION_STATES, - PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), + PalettedContainer.Strategy.SECTION_STATES.getConfiguration( + Block.BLOCK_STATE_REGISTRY, + bitsPerEntry + ), nmsBits, palette ); @@ -641,7 +668,9 @@ static List getEntities(LevelChunk chunk) { if (POST_CHUNK_REWRITE) { try { //noinspection unchecked - return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ)); + return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level + .getEntityLookup() + .getChunk(chunk.locX, chunk.locZ)); } catch (IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e); } 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 17b3545352..8545fecd58 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 @@ -271,9 +271,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/adapters/adapter-1_20/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts index 20bff73592..863e21831f 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts @@ -12,6 +12,7 @@ repositories { dependencies { // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ + // // the().foliaDevBundle("1.20.1-R0.1-SNAPSHOT") the().paperDevBundle("1.20.1-R0.1-20230916.212543-167") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R1/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R1/PaperweightAdapter.java index b40c12b1ae..940d81be83 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R1/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R1/PaperweightAdapter.java @@ -173,6 +173,8 @@ public final class PaperweightAdapter implements BukkitImplAdapter>> allBlockProperties = null; + public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException { this.parent = new PaperweightAdapter(); } @@ -487,29 +491,79 @@ public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { return CraftItemStack.asCraftMirror(stack); } + // Folia - Start @Override protected void preCaptureStates(final ServerLevel serverLevel) { - serverLevel.captureTreeGeneration = true; - serverLevel.captureBlockStates = true; + if (this.isFolia()) { + try { + var data = currentWorldData.invoke(serverLevel); + captureBlockStates.setBoolean(data, true); + captureTreeGeneration.setBoolean(data, true); + ((Map) capturedBlockStates.get(data)).clear(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } else { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + } } + // Folia - End @Override protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { return new ArrayList<>(serverLevel.capturedBlockStates.values()); } + // Folia - Start @Override protected void postCaptureBlockStates(final ServerLevel serverLevel) { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); + if (this.isFolia()) { + try { + var data = currentWorldData.invoke(serverLevel); + captureBlockStates.setBoolean(data, false); + captureTreeGeneration.setBoolean(data, false); + ((Map) capturedBlockStates.get(data)).clear(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } else { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } } + // Folia - End @Override protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + // Folia - Start + @Override + protected MethodHandle getCurrentWorldData() { + if (this.isFolia()) { + Method getCurrentWorldData; + try { + getCurrentWorldData = ServerLevel.class.getSuperclass().getDeclaredMethod( + "getCurrentWorldData" + ); + } catch (NoSuchMethodException e) { + return null; + } + getCurrentWorldData.setAccessible(true); + + try { + currentWorldData = MethodHandles.lookup().unreflect(getCurrentWorldData); + } catch (IllegalAccessException e) { + } + } + return null; + } + + // Folia - End + @Override public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweWorldNativeAccess.java index e1230b20e4..8cd120531b 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweWorldNativeAccess.java @@ -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_20_R1.PaperweightAdapter; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.util.SideEffect; @@ -27,6 +28,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; @@ -52,6 +55,12 @@ 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) { @@ -59,7 +68,29 @@ 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() { @@ -95,7 +126,18 @@ 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; + 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.isMainThread()) { return levelChunk.setBlockState(blockPos, blockState, this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE) diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java index 67bcd6902c..76ee53c14e 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/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_20_R1.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.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; @@ -82,6 +87,7 @@ import java.util.concurrent.locks.ReentrantLock; 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; @@ -256,13 +262,31 @@ 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 @@ -788,7 +812,8 @@ public synchronized > T call(IChunkSet set, Runnable finaliz 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); @@ -802,6 +827,22 @@ 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_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java index 8083e3fdeb..5ba67e56a7 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightPlatformAdapter.java @@ -4,6 +4,7 @@ import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; +import com.fastasyncworldedit.bukkit.util.FoliaTaskManager; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; @@ -15,6 +16,7 @@ import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.internal.util.LogManagerCompat; +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.BlockState; @@ -86,6 +88,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Function; +import java.util.function.Supplier; import static java.lang.invoke.MethodType.methodType; import static net.minecraft.core.registries.Registries.BIOME; @@ -129,9 +132,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); @@ -299,25 +313,28 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c if (Fawe.isMainThread()) { return serverLevel.getChunk(chunkX, chunkZ); } + if (FOLIA_SUPPORT) { + Supplier foliaSupplier = () -> { + CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); + try { + CraftChunk chunk = (CraftChunk) future.get(); + + addTicket(serverLevel, chunkX, chunkZ); + return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + + } catch (Throwable e) { + e.printStackTrace(); + } + throw new RuntimeException(); + }; + FoliaTaskManager taskManager = (FoliaTaskManager) TaskManager.taskManager(); + return taskManager.syncAt(foliaSupplier, serverLevel.getWorld(), chunkX, chunkZ); + } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk; - try { - chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); - } catch (TimeoutException e) { - String world = serverLevel.getWorld().getName(); - // We've already taken 10 seconds we can afford to wait a little here. - boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); - if (loaded) { - LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); - // Retry chunk load - chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); - } else { - throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); - } - } + CraftChunk chunk = (CraftChunk) future.get(); addTicket(serverLevel, chunkX, chunkZ); - return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + return chunk.getCraftWorld().getHandle().getChunk(chunkX, chunkZ); } catch (Throwable e) { e.printStackTrace(); } @@ -326,10 +343,15 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { - // Ensure chunk is definitely loaded before applying a ticket - io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel + Runnable runnable = () -> serverLevel .getChunkSource() - .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE); + if (FOLIA_SUPPORT) { + TaskManager.taskManager().taskNowMain(runnable); + } else { + // Ensure chunk is definitely loaded before applying a ticket + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(runnable); + } } public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { @@ -467,7 +489,8 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot + @SuppressWarnings("deprecation") + // constructor is deprecated on paper, but needed to keep compatibility with spigot final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts index 70c495b7b5..e0747f7651 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - the().paperDevBundle("1.20.2-R0.1-20231125.095734-103") + the().FoliaDevBundle("1.20.2-R0.1-SNAPSHOT") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java index b73e6d06ea..6daa09ae65 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R2/PaperweightAdapter.java @@ -171,6 +171,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter getCapturedBlockStatesCopy(final Ser @Override protected void postCaptureBlockStates(final ServerLevel serverLevel) { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); + // Folia - Start + if (this.isFolia()) { + try { + var data = currentWorldData.invoke(serverLevel); + captureBlockStates.setBoolean(data, false); + captureTreeGeneration.setBoolean(data, false); + ((Map) capturedBlockStates.get(data)).clear(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } else { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + // Folia - End } @Override @@ -513,6 +541,29 @@ protected ServerLevel getServerLevel(final World world) { return ((CraftWorld) world).getHandle(); } + // Folia - Start + @Override + protected MethodHandle getCurrentWorldData() { + if (this.isFolia()) { + Method getCurrentWorldData; + try { + getCurrentWorldData = ServerLevel.class.getSuperclass().getDeclaredMethod( + "getCurrentWorldData" + ); + } catch (NoSuchMethodException e) { + return null; + } + getCurrentWorldData.setAccessible(true); + + try { + currentWorldData = MethodHandles.lookup().unreflect(getCurrentWorldData); + } catch (IllegalAccessException e) { + } + } + return null; + } + // Folia - End + @Override public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java index 9ab6b06021..09f6c88512 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightPlatformAdapter.java @@ -4,6 +4,7 @@ import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; +import com.fastasyncworldedit.bukkit.util.FoliaTaskManager; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; @@ -15,6 +16,7 @@ import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.internal.util.LogManagerCompat; +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.BlockState; @@ -83,6 +85,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Function; +import java.util.function.Supplier; import static java.lang.invoke.MethodType.methodType; import static net.minecraft.core.registries.Registries.BIOME; @@ -122,10 +125,20 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static Method PAPER_CHUNK_GEN_ALL_ENTITIES; 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); @@ -290,25 +303,28 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c if (Fawe.isMainThread()) { return serverLevel.getChunk(chunkX, chunkZ); } + if (FOLIA_SUPPORT) { + Supplier foliaSupplier = () -> { + CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); + try { + CraftChunk chunk = (CraftChunk) future.get(); + + addTicket(serverLevel, chunkX, chunkZ); + return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + + } catch (Throwable e) { + e.printStackTrace(); + } + throw new RuntimeException(); + }; + FoliaTaskManager taskManager = (FoliaTaskManager) TaskManager.taskManager(); + return taskManager.syncAt(foliaSupplier, serverLevel.getWorld(), chunkX, chunkZ); + } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk; - try { - chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); - } catch (TimeoutException e) { - String world = serverLevel.getWorld().getName(); - // We've already taken 10 seconds we can afford to wait a little here. - boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); - if (loaded) { - LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); - // Retry chunk load - chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); - } else { - throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); - } - } + CraftChunk chunk = (CraftChunk) future.get(); addTicket(serverLevel, chunkX, chunkZ); - return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + return chunk.getCraftWorld().getHandle().getChunk(chunkX, chunkZ); } catch (Throwable e) { e.printStackTrace(); } @@ -317,10 +333,15 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { - // Ensure chunk is definitely loaded before applying a ticket - io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel + Runnable runnable = () -> serverLevel .getChunkSource() - .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE); + if (FOLIA_SUPPORT) { + TaskManager.taskManager().taskNowMain(runnable); + } else { + // Ensure chunk is definitely loaded before applying a ticket + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(runnable); + } } public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { @@ -458,7 +479,8 @@ public static LevelChunkSection newChunkSection( } // Create palette with data - @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot + @SuppressWarnings("deprecation") + // constructor is deprecated on paper, but needed to keep compatibility with spigot final PalettedContainer blockStatePalettedContainer = new PalettedContainer<>( Block.BLOCK_STATE_REGISTRY, diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index dfcae5783b..5e2d39e03b 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -60,7 +60,6 @@ val adapters = configurations.create("adapters") { ) } } - dependencies { // Modules api(projects.worldeditCore) 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 5e60a704ce..7a8604fcbb 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/adapter/FaweAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java index e116daf17a..dc402cf4bb 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 @@ -11,6 +11,8 @@ import org.bukkit.World; import org.bukkit.block.BlockState; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; import java.util.List; /** @@ -21,6 +23,32 @@ */ public abstract class FaweAdapter extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { + + // Folia - Start + protected MethodHandle currentWorldData = getCurrentWorldData(); + + protected Class regionizedWorldData = getRegionitedWorldData(); + + protected Field captureTreeGeneration = getCaptureTreeGeneration(); + + protected Field captureBlockStates = getCaptureBlockStates(); + + protected Field capturedBlockStates = getCapturedBlockStates(); + + private final boolean folia; + + protected FaweAdapter() { + boolean isFolia = false; + try { + // Assume API is present + Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler"); + isFolia = true; + } catch (Exception unused) { + + } + this.folia = isFolia; + } + @Override public boolean generateTree( final TreeGenerator.TreeType treeType, @@ -69,4 +97,55 @@ public boolean generateTree( protected abstract SERVER_LEVEL getServerLevel(World world); + // Folia Support + protected abstract MethodHandle getCurrentWorldData(); + + private Class getRegionitedWorldData() { + try { + regionizedWorldData = Class.forName("io.papermc.paper.threadedregions.RegionizedWorldData"); + } catch (ClassNotFoundException e) { + } + return null; + } + + private Field getCaptureTreeGeneration() { + Field captureTreeGeneration = null; + if (regionizedWorldData != null) { + try { + captureTreeGeneration = regionizedWorldData.getDeclaredField("captureTreeGeneration"); + captureTreeGeneration.setAccessible(true); + } catch (NoSuchFieldException e) { + } + } + return captureTreeGeneration; + } + + private Field getCaptureBlockStates() { + Field captureBlockStates = null; + if (regionizedWorldData != null) { + try { + captureBlockStates = regionizedWorldData.getDeclaredField("captureBlockStates"); + captureBlockStates.setAccessible(true); + } catch (NoSuchFieldException e) { + } + } + return captureBlockStates; + } + + private Field getCapturedBlockStates() { + Field capturedBlockStates = null; + if (regionizedWorldData != null) { + try { + capturedBlockStates = regionizedWorldData.getDeclaredField("capturedBlockStates"); + capturedBlockStates.setAccessible(true); + } catch (NoSuchFieldException e) { + } + } + return capturedBlockStates; + } + + protected boolean isFolia() { + return this.folia; + } + } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java index 7b95990843..78f60f133c 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 6cf09da28d..8a65bfd685 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 @@ -160,7 +160,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..4edcbaa699 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java @@ -0,0 +1,262 @@ +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.World; +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); + } + } + + public T syncAt(final Supplier supplier, final World context, int chunkX, int chunkZ) { + FutureTask task = new FutureTask<>(supplier::get); + SchedulerAdapter.executeForChunk(context,chunkX, chunkZ, 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_CHUNK; + 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 CHUNK_EXECUTE_TYPE = methodType( + void.class, + Plugin.class, + org.bukkit.World.class, + int.class, + int.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 executeForChunk; + + 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); + + executeForChunk = lookup.findVirtual( + regionisedSchedulerClass, + "execute", + CHUNK_EXECUTE_TYPE + ); + executeForChunk = executeForChunk.bindTo(method.invoke(null)); + executeForChunk = executeForChunk.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_CHUNK = executeForChunk; + EXECUTE_FOR_PLAYER = executeForPlayer; + } + + static void executeForChunk(World world,int chunkX, int chunkZ, Runnable task) { + try { + EXECUTE_FOR_CHUNK.invokeExact(world,chunkX, chunkZ, task); + } catch (Error | RuntimeException e) { + throw e; + } catch (Throwable other) { + throw new RuntimeException(other); + } + } + + 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 a9ba45afed..710b32d5bf 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 @@ -365,7 +365,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 e881416c54..c97cdc32d8 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; @@ -400,7 +401,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 ff2f69486a..16240381f6 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; @@ -129,15 +130,18 @@ 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); + clipboardExecutor = new KeyQueuedExecutorService<>(new ThreadPoolExecutor( 1, Settings.settings().QUEUE.PARALLEL_THREADS, @@ -206,10 +210,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 0655e42148..6cf1b19a09 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -156,7 +156,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 4bbe1a109a..018a95dd47 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 @@ -353,7 +353,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 014b94fce7..cef80cbb63 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/QueueHandler.java @@ -17,6 +17,7 @@ import com.fastasyncworldedit.core.util.task.FaweForkJoinWorkerThreadFactory; import com.fastasyncworldedit.core.wrappers.WorldWrapper; import com.google.common.util.concurrent.Futures; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; import java.lang.ref.WeakReference; @@ -88,14 +89,14 @@ public abstract class QueueHandler implements Trimable, Runnable { private long last; private long allocate = 50; - protected QueueHandler() { + public QueueHandler() { TaskManager.taskManager().repeat(this, 1); } @Override public void run() { - if (!Fawe.isMainThread()) { - throw new IllegalStateException("Not main thread"); + if (!Fawe.isTickThread()) { + throw new IllegalStateException("Not ticking thread"); } if (!syncTasks.isEmpty()) { long currentAllocate = getAllocate(); @@ -316,7 +317,7 @@ public Future syncWhenFree(Supplier supplier) { } private Future sync(Runnable run, T value, Queue queue) { - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { run.run(); return Futures.immediateFuture(value); } @@ -327,7 +328,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(); } @@ -338,7 +339,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); @@ -348,7 +349,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 34dd5191e0..e2e34c4f02 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 @@ -239,7 +239,7 @@ private > V submitUnchecked(IQueueChunk chunk) { } } - 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 176e02673b..25923b2071 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. @@ -159,6 +170,7 @@ public void parallel(Collection runnables, @Nullable Integer numThread /** * Disable async catching for a specific task. */ + @Deprecated public void runUnsafe(Runnable run) { QueueHandler queue = Fawe.instance().getQueueHandler(); queue.startUnsafe(Fawe.isMainThread()); @@ -191,8 +203,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); @@ -203,10 +216,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()); } /** @@ -215,6 +228,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); @@ -230,7 +244,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. @@ -255,6 +271,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() { @@ -307,8 +324,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); @@ -320,8 +338,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; } @@ -337,8 +356,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 { @@ -353,6 +373,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); } @@ -362,8 +383,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 { @@ -373,4 +395,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 ca6f3b6a56..4dfd17638a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSessionBuilder.java @@ -469,7 +469,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;