diff --git a/build.gradle.kts b/build.gradle.kts index da44e48a7..bdbe68dc8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -85,7 +85,7 @@ allprojects { applyCommonConfiguration() val supportedVersions = listOf("1.18.2", "1.19.4", "1.20", "1.20.4") -val foliaSupportedVersions = listOf("1.20.2") +val foliaSupportedVersions = listOf("1.20.4") tasks { fun registerVersion(version: String, software: String, task: RunServer.() -> Unit = {}) { diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/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 cc6a07d22..2ae3c68fe 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 @@ -338,11 +338,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { // Ensure chunk is definitely loaded before applying a ticket - task(() -> serverLevel - .getChunkSource() - .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE), - serverLevel, chunkX, chunkZ - ); + final Runnable addChunkTicket = () -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE); + if (FoliaSupport.isFolia()) { + // run from any thread on Folia + addChunkTicket.run(); + return; + } + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(addChunkTicket); } public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { 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 ad17d07b8..66bb92897 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 @@ -323,11 +323,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { // Ensure chunk is definitely loaded before applying a ticket - PaperweightPlatformAdapter.task(() -> serverLevel - .getChunkSource() - .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE), - serverLevel, chunkX, chunkZ - ); + final Runnable addChunkTicket = () -> serverLevel + .getChunkSource() + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE); + if (FoliaSupport.isFolia()) { + // run from any thread on Folia + addChunkTicket.run(); + return; + } + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(addChunkTicket); } public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java index 6a7b9e5f2..63ef199cd 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java @@ -321,9 +321,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { 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 + final Runnable addChunkTicket = () -> 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 (FoliaSupport.isFolia()) { + // run from any thread on Folia + addChunkTicket.run(); + return; + } + io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(addChunkTicket); } public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitFoliaAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitFoliaAdapter.java new file mode 100644 index 000000000..0623d314e --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitFoliaAdapter.java @@ -0,0 +1,67 @@ +package com.fastasyncworldedit.bukkit.adapter; + +import com.fastasyncworldedit.bukkit.util.BukkitReflectionUtils; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.regions.Region; +import org.bukkit.World; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.invoke.MethodHandles.collectArguments; +import static java.lang.invoke.MethodHandles.dropArguments; +import static java.lang.invoke.MethodHandles.dropReturn; +import static java.lang.invoke.MethodHandles.filterArguments; +import static java.lang.invoke.MethodHandles.filterReturnValue; +import static java.lang.invoke.MethodHandles.guardWithTest; +import static java.lang.invoke.MethodHandles.iteratedLoop; +import static java.lang.invoke.MethodHandles.permuteArguments; +import static java.lang.invoke.MethodType.methodType; + +public class BukkitFoliaAdapter { + + // @formatter:off + public static List getEntities(World world, Region region) { + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class craftWorldClass = world.getClass(); // assume it's CraftWorld + Class serverLevel = Class.forName(Refraction.pickName("net.minecraft.server.level.ServerLevel", "net.minecraft.server.level.WorldServer")); + Class craftEntity = BukkitReflectionUtils.getCbClass("entity.CraftEntity"); + Class nmsEntityClass = Class.forName("net.minecraft.world.entity.Entity"); + Class nmsBlockPosClass = Class.forName(Refraction.pickName("net.minecraft.core.BlockPos", "net.minecraft.core.BlockPosition")); + Class entityLookup = Class.forName("io.papermc.paper.chunk.system.entity.EntityLookup"); + MethodHandle getHandle = lookup.findVirtual(craftWorldClass, "getHandle", methodType(serverLevel)); + MethodHandle getEntityLookup = lookup.findVirtual(serverLevel, "getEntityLookup", methodType(entityLookup)); + MethodHandle getAll = lookup.findVirtual(entityLookup, Refraction.pickName("getAll", "a"), methodType(Iterable.class)); + MethodHandle getEntities = filterReturnValue(filterReturnValue(getHandle, getEntityLookup), getAll); + MethodHandle regionContainsXYZ = lookup.findVirtual(Region.class, "contains", methodType(boolean.class, int.class, int.class, int.class)); + MethodHandle blockPos = lookup.findVirtual(nmsEntityClass, Refraction.pickName("blockPosition", "dm"), methodType(nmsBlockPosClass)); + MethodHandle getX = lookup.findVirtual(nmsBlockPosClass, Refraction.pickName("getX", "u"), methodType(int.class)); + MethodHandle getY = lookup.findVirtual(nmsBlockPosClass, Refraction.pickName("getY", "v"), methodType(int.class)); + MethodHandle getZ = lookup.findVirtual(nmsBlockPosClass, Refraction.pickName("getZ", "w"), methodType(int.class)); + MethodHandle regionContainsBPBPBP = filterArguments(regionContainsXYZ, 1, getX, getY, getZ); + MethodHandle regionContainsBlockPos = permuteArguments(regionContainsBPBPBP, methodType(boolean.class, nmsBlockPosClass, Region.class), 1, 0, 0, 0); + MethodHandle isInRegion = dropArguments(filterArguments(regionContainsBlockPos, 0, blockPos), 0, ArrayList.class); + MethodHandle getBukkitEntity = lookup.findVirtual(nmsEntityClass, "getBukkitEntity", methodType(craftEntity)); + MethodHandle adapt = lookup.findStatic(BukkitAdapter.class, "adapt", methodType(Entity.class, org.bukkit.entity.Entity.class)); + MethodHandle weEntity = filterReturnValue(getBukkitEntity.asType(getBukkitEntity.type().changeReturnType(org.bukkit.entity.Entity.class)), adapt); + MethodHandle add = lookup.findVirtual(ArrayList.class, "add", methodType(boolean.class, Object.class)); + MethodHandle addConverted = filterArguments(add, 1, weEntity.asType(weEntity.type().changeReturnType(Object.class))); + MethodHandle arrayListIdentity = MethodHandles.identity(ArrayList.class); + MethodHandle addConvertedReturn = collectArguments(dropArguments(arrayListIdentity, 1, nmsEntityClass), 0, dropReturn(addConverted)); + MethodHandle addConvertedReturnCollapsed = permuteArguments(addConvertedReturn, methodType(ArrayList.class, ArrayList.class, nmsEntityClass), 0, 1, 0, 1); + MethodHandle newArrayListHandle = lookup.findConstructor(ArrayList.class, methodType(void.class)); + MethodHandle ifInRegion = guardWithTest(isInRegion, dropArguments(addConvertedReturnCollapsed, 2, Region.class), dropArguments(arrayListIdentity, 1, nmsEntityClass, Region.class)); + MethodHandle iterate = iteratedLoop(null, newArrayListHandle, dropArguments(ifInRegion, 2, Iterable.class)); + MethodHandle preIter = filterArguments(iterate, 0, getEntities); + return (List) preIter.invoke(world, region); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + // @formatter:on +} 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 f29a35b28..f0055aafe 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 @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit; +import com.fastasyncworldedit.bukkit.adapter.BukkitFoliaAdapter; import com.fastasyncworldedit.bukkit.util.WorldUnloadedException; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; @@ -26,6 +27,7 @@ import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -43,7 +45,9 @@ import com.sk89q.worldedit.internal.wna.WorldNativeAccess; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.regions.AbstractRegion; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.SideEffect; @@ -147,6 +151,9 @@ public class BukkitWorld extends AbstractWorld { public List getEntities(Region region) { World world = getWorld(); + if (FoliaSupport.isFolia()) { + return BukkitFoliaAdapter.getEntities(world, region); + } List ents = TaskManager.taskManager().syncGlobal(world::getEntities); List entities = new ArrayList<>(); for (Entity ent : ents) { @@ -159,6 +166,40 @@ public class BukkitWorld extends AbstractWorld { @Override public List getEntities() { + if (FoliaSupport.isFolia()) { + return BukkitFoliaAdapter.getEntities(getWorld(), new AbstractRegion(null) { + + @Override + public BlockVector3 getMinimumPoint() { + return null; + } + + @Override + public BlockVector3 getMaximumPoint() { + return null; + } + + @Override + public void expand(final BlockVector3... changes) throws RegionOperationException { + + } + + @Override + public void contract(final BlockVector3... changes) throws RegionOperationException { + + } + + @Override + public boolean contains(final BlockVector3 position) { + return false; + } + + @Override + public boolean contains(final int x, final int y, final int z) { + return true; + } + }); + } List list = new ArrayList<>(); List ents = TaskManager.taskManager().syncGlobal(getWorld()::getEntities);