diff --git a/build.gradle.kts b/build.gradle.kts index 15845439ad..bdbe68dc81 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,10 @@ import org.ajoberstar.grgit.Grgit import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED +import org.gradle.configurationcache.extensions.capitalized +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,24 +85,33 @@ allprojects { applyCommonConfiguration() val supportedVersions = listOf("1.18.2", "1.19.4", "1.20", "1.20.4") +val foliaSupportedVersions = listOf("1.20.4") tasks { - supportedVersions.forEach { - register("runServer-$it") { - minecraftVersion(it) - pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } + fun registerVersion(version: String, software: String, task: RunServer.() -> Unit = {}) { + register("run${software.capitalized()}-$version") { + minecraftVersion(version) + pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false) + .map { (it as Jar).archiveFile } .toTypedArray()) jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true") - group = "run paper" - runDirectory.set(file("run-$it")) + group = "run $software" + runDirectory.set(file("run-$software-$version")) + task(this) } } runServer { - minecraftVersion("1.20.4") - pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } - .toTypedArray()) - + registerVersion("1.20.4", "paper") } + supportedVersions.forEach { + registerVersion(it, "paper") + } + foliaSupportedVersions.forEach { + registerVersion(it, "folia") { + downloadsApiService.set(xyz.jpenilla.runtask.service.DownloadsAPIService.folia(project)) + } + } + } nexusPublishing { diff --git a/settings.gradle.kts b/settings.gradle.kts index 74cba1396b..dd74eaa319 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,5 +21,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_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..74717ed95c 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) ); @@ -250,7 +250,8 @@ public void run(Object value) { } } }; - TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal)); + // we don't support Folia on that version, we can run this globally + TaskManager.taskManager().async(() -> TaskManager.taskManager().syncGlobal(runnableVal)); } @Override @@ -266,10 +267,11 @@ public void run(Object value) { } } }; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { runnableVal.run(); } else { - TaskManager.taskManager().sync(runnableVal); + // we don't support Folia on that version, we can run this globally + TaskManager.taskManager().syncGlobal(runnableVal); } cachedChanges.clear(); cachedChunksToSend.clear(); 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..9407098e93 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 @@ -10,6 +10,7 @@ import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; @@ -246,7 +247,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c } 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); + boolean loaded = TaskManager.taskManager().syncGlobal(() -> Bukkit.getWorld(world) != null); if (loaded) { LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); // Retry chunk load @@ -260,7 +261,8 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c e.printStackTrace(); } } - return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); + return TaskManager.taskManager().syncAt(() -> serverLevel.getChunk(chunkX, chunkZ), + BukkitAdapter.adapt(serverLevel.getWorld()), chunkX, chunkZ); } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { @@ -300,7 +302,7 @@ public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boole return; } LevelChunk levelChunk = optional.get(); - TaskManager.taskManager().task(() -> { + PaperweightPlatformAdapter.task(() -> { ClientboundLevelChunkWithLightPacket packet; if (PaperLib.isPaper()) { packet = new ClientboundLevelChunkWithLightPacket( @@ -322,7 +324,7 @@ public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boole ); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); - }); + }, nmsWorld, chunkX, chunkZ); } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -625,6 +627,10 @@ public static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTa } } + public static void task(Runnable task, ServerLevel level, int chunkX, int chunkZ) { + TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + record FakeIdMapBlock(int size) implements IdMap { @Override diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightStarlightRelighter.java index c832fc98ac..22fae53866 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightStarlightRelighter.java @@ -64,12 +64,20 @@ protected void invokeRelight( protected void postProcessChunks(Set coords) { boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; for (ChunkPos pos : coords) { - int x = pos.x; - int z = pos.z; - if (delay) { // we still need to send the block changes of that chunk - PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false); - } - serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); + PaperweightPlatformAdapter.task( + () -> { + int x = pos.x; + int z = pos.z; + if (delay) { // we still need to send the block changes of that chunk + PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false); + } + serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); + }, + serverLevel, + pos.x, + pos.z + ); + } } diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java index 8403d531d0..365022dac0 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/regen/PaperweightRegen.java @@ -9,6 +9,7 @@ import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.PaperweightGetBlocks; @@ -411,7 +412,12 @@ protected List getBlockPopulators() { @Override protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { // BlockPopulator#populate has to be called synchronously for TileEntity access - TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk())); + TaskManager.taskManager().task( + () -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()), + BukkitAdapter.adapt(originalBukkitWorld), + levelChunk.getPos().x, + levelChunk.getPos().z + ); } @Override 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..5c8a8bdf8a 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 @@ -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) ); @@ -250,7 +250,8 @@ public void run(Object value) { } } }; - TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal)); + // we don't support Folia on that version, we can run this globally + TaskManager.taskManager().async(() -> TaskManager.taskManager().syncGlobal(runnableVal)); } @Override @@ -266,10 +267,11 @@ public void run(Object value) { } } }; - if (Fawe.isMainThread()) { + if (Fawe.isTickThread()) { runnableVal.run(); } else { - TaskManager.taskManager().sync(runnableVal); + // we don't support Folia on that version, we can run this globally + TaskManager.taskManager().syncGlobal(runnableVal); } cachedChanges.clear(); cachedChunksToSend.clear(); 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..40d15dd036 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 @@ -11,6 +11,7 @@ import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; @@ -59,7 +60,6 @@ import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_19_R3.CraftChunk; -import sun.misc.Unsafe; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -283,7 +283,7 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c } 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); + boolean loaded = TaskManager.taskManager().syncGlobal(() -> Bukkit.getWorld(world) != null); if (loaded) { LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); // Retry chunk load @@ -298,7 +298,12 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c e.printStackTrace(); } } - return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); + return TaskManager.taskManager().syncAt( + () -> serverLevel.getChunk(chunkX, chunkZ), + BukkitAdapter.adapt(serverLevel.getWorld()), + chunkX, + chunkZ + ); } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { @@ -361,7 +366,7 @@ public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boole ); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); - }); + }, BukkitAdapter.adapt(nmsWorld.getWorld()), chunkX, chunkZ); } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -687,6 +692,10 @@ public static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTa } } + public static void task(Runnable task, ServerLevel level, int chunkX, int chunkZ) { + TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + record FakeIdMapBlock(int size) implements IdMap { @Override diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightStarlightRelighter.java index 580bbf5a6e..3b459b2b75 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightStarlightRelighter.java @@ -64,12 +64,20 @@ protected void invokeRelight( protected void postProcessChunks(Set coords) { boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; for (ChunkPos pos : coords) { - int x = pos.x; - int z = pos.z; - if (delay) { // we still need to send the block changes of that chunk - PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false); - } - serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); + PaperweightPlatformAdapter.task( + () -> { + int x = pos.x; + int z = pos.z; + if (delay) { // we still need to send the block changes of that chunk + PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false); + } + serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); + }, + serverLevel, + pos.x, + pos.z + ); + } } 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..047896d5ca 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 @@ -9,6 +9,7 @@ import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.PaperweightGetBlocks; @@ -442,7 +443,7 @@ protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blo final CraftWorld world = freshWorld.getWorld(); final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); blockPopulator.populate(world, random, chunk); - }); + }, BukkitAdapter.adapt(originalBukkitWorld), levelChunk.getPos().x, levelChunk.getPos().z); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweAdapter.java index e13ff700f6..7cc9b2f5f8 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightFaweAdapter.java @@ -2,6 +2,7 @@ import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; +import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; @@ -9,6 +10,7 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.NbtUtils; +import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/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..33ccf0a7e2 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 @@ -1,8 +1,9 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1; -import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; +import com.fastasyncworldedit.core.util.collection.FlushingPartitionedCache; import com.fastasyncworldedit.core.util.task.RunnableVal; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; @@ -14,21 +15,19 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; import javax.annotation.Nullable; import java.lang.ref.WeakReference; -import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -50,8 +49,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess level; private final AtomicInteger lastTick; - private final Set cachedChanges = new HashSet<>(); - private final Set cachedChunksToSend = new HashSet<>(); + private final FlushingPartitionedCache> cache; private SideEffectSet sideEffectSet; public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference level) { @@ -59,7 +57,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); + this.lastTick = new AtomicInteger(); + if (!FoliaSupport.isFolia()) { + this.lastTick.set(Bukkit.getCurrentTick()); + } + this.cache = new FlushingPartitionedCache<>( + cachedChange -> new IntPair(cachedChange.blockPos.getX() >> 4, cachedChange.blockPos.getZ() >> 4), + HashSet::new, + (chunk, changes) -> { + // TODO (folia) only send chunks based on ticks? Need to make sure everything actually flushed in the end + /*boolean nextTick = true; + if (!FoliaSupport.isFolia()) { + int currentTick = MinecraftServer.currentTick; + nextTick = lastTick.get() > currentTick; + if (nextTick) { + lastTick.set(currentTick); + } + }*/ + flushAsync(chunk, changes, true); + }, + 2048, + 16 + ); + } private Level getLevel() { @@ -95,22 +115,13 @@ 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()) { + if (PaperweightPlatformAdapter.isTickThreadFor(levelChunk)) { 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; - if (nextTick || cachedChanges.size() >= 1024) { - if (nextTick) { - lastTick.set(currentTick); - } - flushAsync(nextTick); - } + cache.insert(new CachedChange(levelChunk, blockPos, blockState)); return blockState; } @@ -227,16 +238,7 @@ public void onBlockStateChange( getLevel().onBlockStateChange(blockPos, oldState, newState); } - private synchronized void flushAsync(final boolean sendChunks) { - final Set changes = Set.copyOf(cachedChanges); - cachedChanges.clear(); - final Set toSend; - if (sendChunks) { - toSend = Set.copyOf(cachedChunksToSend); - cachedChunksToSend.clear(); - } else { - toSend = Collections.emptySet(); - } + private synchronized void flushAsync(IntPair chunk, Set changes, final boolean sendChunks) { RunnableVal runnableVal = new RunnableVal<>() { @Override public void run(Object value) { @@ -246,34 +248,18 @@ public void run(Object value) { if (!sendChunks) { return; } - for (IntPair chunk : toSend) { - PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); - } + PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); } }; - TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal)); + TaskManager.taskManager().async( + () -> PaperweightPlatformAdapter.task(runnableVal, getLevel().getWorld().getHandle(), chunk.x(), chunk.z()) + ); } + @Override public synchronized void flush() { - RunnableVal runnableVal = new RunnableVal<>() { - @Override - public void run(Object value) { - cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState, - sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE) - )); - for (IntPair chunk : cachedChunksToSend) { - PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); - } - } - }; - if (Fawe.isMainThread()) { - runnableVal.run(); - } else { - TaskManager.taskManager().sync(runnableVal); - } - cachedChanges.clear(); - cachedChunksToSend.clear(); + this.cache.flush(); } private record CachedChange( 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 5f74d1073f..a8c105ed91 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 @@ -257,9 +257,9 @@ 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))); + BlockEntity blockEntity = PaperweightPlatformAdapter.sync(() -> getChunk().getBlockEntity( + new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))), + serverLevel, chunkX, chunkZ); if (blockEntity == null) { return null; } @@ -268,7 +268,10 @@ public CompoundTag getTile(int x, int y, int z) { @Override public Map getTiles() { - Map nmsTiles = getChunk().getBlockEntities(); + Map nmsTiles = PaperweightPlatformAdapter.sync( + () -> getChunk().getBlockEntities(), + serverLevel, chunkX, chunkZ + ); if (nmsTiles.isEmpty()) { return Collections.emptyMap(); } 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..2ae3c68fe4 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 @@ -7,19 +7,25 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.TickThread; import io.papermc.paper.world.ChunkEntitySlices; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -61,7 +67,6 @@ import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R1.CraftChunk; -import sun.misc.Unsafe; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -86,6 +91,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; @@ -301,35 +307,46 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk; try { - chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + 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); + boolean loaded = TaskManager.taskManager().syncGlobal(() -> Bukkit.getWorld(world) != null); if (loaded) { LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); // Retry chunk load - chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); } else { throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); } } addTicket(serverLevel, chunkX, chunkZ); - return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + // chunk is loaded now, can access it directly + return serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); } catch (Throwable e) { e.printStackTrace(); } } - return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); + return TaskManager.taskManager().syncAt( + () -> serverLevel.getChunk(chunkX, chunkZ), + BukkitAdapter.adapt(serverLevel.getWorld()), + chunkX, + chunkZ + ); } 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) { @@ -383,13 +400,19 @@ public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boole ); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); - }); + }, toLocation(nmsWorld, coordIntPair)); } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false); } + public static Location toLocation(ServerLevel serverLevel, ChunkPos chunkPos) { + final World adapt = BukkitAdapter.adapt(serverLevel.getWorld()); + final Vector3 pos = Vector3.at(chunkPos.getMinBlockX(), 0, chunkPos.getMinBlockZ()); + return new Location(adapt, pos); + } + /* NMS conversion */ @@ -701,7 +724,22 @@ public static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTa } } - record FakeIdMapBlock(int size) implements IdMap { + public static void task(Runnable task, ServerLevel level, int chunkX, int chunkZ) { + TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + + public static T sync(Supplier task, ServerLevel level, int chunkX, int chunkZ) { + return TaskManager.taskManager().syncAt(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + + public static boolean isTickThreadFor(LevelChunk levelChunk) { + if (FoliaSupport.isFolia()) { + return TickThread.isTickThreadFor(levelChunk.level, levelChunk.locX, levelChunk.locZ); + } + return Fawe.isTickThread(); + } + + record FakeIdMapBlock(int size) implements IdMap { @Override public int getId(final net.minecraft.world.level.block.state.BlockState entry) { diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightStarlightRelighter.java index 30df914597..dd3f47c1f0 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/PaperweightStarlightRelighter.java @@ -45,10 +45,11 @@ protected CompletableFuture chunkLoadFuture(final ChunkPos chunkPos) { )); } + @Override protected void invokeRelight( - Set coords, - Consumer chunkCallback, - IntConsumer processCallback + final Set coords, + final Consumer chunkCallback, + final IntConsumer processCallback ) { try { serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback); @@ -64,12 +65,20 @@ protected void invokeRelight( protected void postProcessChunks(Set coords) { boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; for (ChunkPos pos : coords) { - int x = pos.x; - int z = pos.z; - if (delay) { // we still need to send the block changes of that chunk - PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false); - } - serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); + PaperweightPlatformAdapter.task( + () -> { + int x = pos.x; + int z = pos.z; + if (delay) { // we still need to send the block changes of that chunk + PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false); + } + serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); + }, + serverLevel, + pos.x, + pos.z + ); + } } diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/regen/PaperweightRegen.java index 99e001837a..7a850551e4 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R1/regen/PaperweightRegen.java @@ -9,6 +9,7 @@ import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.PaperweightGetBlocks; @@ -443,7 +444,7 @@ protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blo final CraftWorld world = freshWorld.getWorld(); final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); blockPopulator.populate(world, random, chunk); - }); + }, BukkitAdapter.adapt(originalBukkitWorld), levelChunk.getPos().x, levelChunk.getPos().z); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java index b452ec4cd2..434e6faf34 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweWorldNativeAccess.java @@ -1,8 +1,9 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; -import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; +import com.fastasyncworldedit.core.util.collection.FlushingPartitionedCache; import com.fastasyncworldedit.core.util.task.RunnableVal; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; @@ -14,21 +15,19 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; import javax.annotation.Nullable; import java.lang.ref.WeakReference; -import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -50,8 +49,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess level; private final AtomicInteger lastTick; - private final Set cachedChanges = new HashSet<>(); - private final Set cachedChunksToSend = new HashSet<>(); + private final FlushingPartitionedCache> cache; private SideEffectSet sideEffectSet; public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference level) { @@ -59,7 +57,28 @@ 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); + this.lastTick = new AtomicInteger(); + if (!FoliaSupport.isFolia()) { + this.lastTick.set(Bukkit.getCurrentTick()); + } + this.cache = new FlushingPartitionedCache<>( + cachedChange -> new IntPair(cachedChange.blockPos.getX() >> 4, cachedChange.blockPos.getZ() >> 4), + HashSet::new, + (chunk, changes) -> { + // TODO (folia) only send chunks based on ticks? Need to make sure everything actually flushed in the end + /*boolean nextTick = true; + if (!FoliaSupport.isFolia()) { + int currentTick = MinecraftServer.currentTick; + nextTick = lastTick.get() > currentTick; + if (nextTick) { + lastTick.set(currentTick); + } + }*/ + flushAsync(chunk, changes, true); + }, + 2048, + 16 + ); } private Level getLevel() { @@ -95,22 +114,13 @@ 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()) { + if (PaperweightPlatformAdapter.isTickThreadFor(levelChunk)) { 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; - if (nextTick || cachedChanges.size() >= 1024) { - if (nextTick) { - lastTick.set(currentTick); - } - flushAsync(nextTick); - } + cache.insert(new CachedChange(levelChunk, blockPos, blockState)); return blockState; } @@ -227,16 +237,7 @@ public void onBlockStateChange( getLevel().onBlockStateChange(blockPos, oldState, newState); } - private synchronized void flushAsync(final boolean sendChunks) { - final Set changes = Set.copyOf(cachedChanges); - cachedChanges.clear(); - final Set toSend; - if (sendChunks) { - toSend = Set.copyOf(cachedChunksToSend); - cachedChunksToSend.clear(); - } else { - toSend = Collections.emptySet(); - } + private synchronized void flushAsync(IntPair chunk, Set changes, final boolean sendChunks) { RunnableVal runnableVal = new RunnableVal<>() { @Override public void run(Object value) { @@ -246,34 +247,17 @@ public void run(Object value) { if (!sendChunks) { return; } - for (IntPair chunk : toSend) { - PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); - } + PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); } }; - TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal)); + TaskManager.taskManager().async( + () -> PaperweightPlatformAdapter.task(runnableVal, getLevel().getWorld().getHandle(), chunk.x(), chunk.z()) + ); } @Override public synchronized void flush() { - RunnableVal runnableVal = new RunnableVal<>() { - @Override - public void run(Object value) { - cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState, - sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE) - )); - for (IntPair chunk : cachedChunksToSend) { - PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); - } - } - }; - if (Fawe.isMainThread()) { - runnableVal.run(); - } else { - TaskManager.taskManager().sync(runnableVal); - } - cachedChanges.clear(); - cachedChunksToSend.clear(); + this.cache.flush(); } private record CachedChange( diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java index 1531358fbb..5df4f31ab0 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/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; @@ -20,6 +21,7 @@ import com.sk89q.jnbt.Tag; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R2.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.internal.util.LogManagerCompat; @@ -236,9 +238,9 @@ 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))); + BlockEntity blockEntity = PaperweightPlatformAdapter.sync(() -> getChunk().getBlockEntity( + new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))), + serverLevel, chunkX, chunkZ); if (blockEntity == null) { return null; } @@ -247,7 +249,10 @@ public CompoundTag getTile(int x, int y, int z) { @Override public Map getTiles() { - Map nmsTiles = getChunk().getBlockEntities(); + Map nmsTiles = PaperweightPlatformAdapter.sync( + () -> getChunk().getBlockEntities(), + serverLevel, chunkX, chunkZ + ); if (nmsTiles.isEmpty()) { return Collections.emptyMap(); } 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..66bb92897a 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 @@ -7,10 +7,12 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; @@ -20,6 +22,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.TickThread; import io.papermc.paper.world.ChunkEntitySlices; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -58,7 +61,6 @@ import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R2.CraftChunk; -import sun.misc.Unsafe; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -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; @@ -292,35 +295,43 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk; try { - chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + 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); + boolean loaded = TaskManager.taskManager().syncGlobal(() -> Bukkit.getWorld(world) != null); if (loaded) { LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); // Retry chunk load - chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); } else { throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); } } addTicket(serverLevel, chunkX, chunkZ); - return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + // chunk is loaded now, can access it directly + return serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); } catch (Throwable e) { e.printStackTrace(); } } - return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); + return TaskManager.taskManager().syncAt(() -> serverLevel.getChunk(chunkX, chunkZ), + BukkitAdapter.adapt(serverLevel.getWorld()), chunkX, chunkZ + ); } 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) { @@ -374,7 +385,7 @@ public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boole ); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); - }); + }, BukkitAdapter.adapt(nmsWorld.getWorld()), chunkX, chunkZ); } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -674,6 +685,21 @@ static List getEntities(LevelChunk chunk) { return List.of(); } + public static void task(Runnable task, ServerLevel level, int chunkX, int chunkZ) { + TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + + public static T sync(Supplier task, ServerLevel level, int chunkX, int chunkZ) { + return TaskManager.taskManager().syncAt(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + + public static boolean isTickThreadFor(LevelChunk levelChunk) { + if (FoliaSupport.isFolia()) { + return TickThread.isTickThreadFor(levelChunk.level, levelChunk.locX, levelChunk.locZ); + } + return Fawe.isTickThread(); + } + record FakeIdMapBlock(int size) implements IdMap { @Override diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java index e869046da1..b1786901f2 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightStarlightRelighter.java @@ -64,12 +64,20 @@ protected void invokeRelight( protected void postProcessChunks(Set coords) { boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING; for (ChunkPos pos : coords) { - int x = pos.x; - int z = pos.z; - if (delay) { // we still need to send the block changes of that chunk - PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false); - } - serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); + PaperweightPlatformAdapter.task( + () -> { + int x = pos.x; + int z = pos.z; + if (delay) { // we still need to send the block changes of that chunk + PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false); + } + serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE); + }, + serverLevel, + pos.x, + pos.z + ); + } } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java index 2ec8e6e41b..5e97b9fea1 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/regen/PaperweightRegen.java @@ -4,14 +4,13 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.util.ReflectionUtils; -import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.PaperweightGetBlocks; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.PaperweightPlatformAdapter; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.regions.Region; @@ -439,11 +438,11 @@ protected List getBlockPopulators() { @Override protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { // BlockPopulator#populate has to be called synchronously for TileEntity access - TaskManager.taskManager().task(() -> { + PaperweightPlatformAdapter.task(() -> { final CraftWorld world = freshWorld.getWorld(); final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); blockPopulator.populate(world, random, chunk); - }); + }, freshWorld, levelChunk.getPos().x, levelChunk.getPos().z); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java index ad04e7b03b..0d3ae96b3a 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java @@ -1,8 +1,9 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; -import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; +import com.fastasyncworldedit.core.util.collection.FlushingPartitionedCache; import com.fastasyncworldedit.core.util.task.RunnableVal; import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.internal.block.BlockStateIdAccess; @@ -14,20 +15,19 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; import org.bukkit.event.block.BlockPhysicsEvent; import javax.annotation.Nullable; import java.lang.ref.WeakReference; -import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -49,8 +49,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess level; private final AtomicInteger lastTick; - private final Set cachedChanges = new HashSet<>(); - private final Set cachedChunksToSend = new HashSet<>(); + private final FlushingPartitionedCache> cache; private SideEffectSet sideEffectSet; public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference level) { @@ -58,7 +57,28 @@ 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); + this.lastTick = new AtomicInteger(); + if (!FoliaSupport.isFolia()) { + this.lastTick.set(Bukkit.getCurrentTick()); + } + this.cache = new FlushingPartitionedCache<>( + cachedChange -> new IntPair(cachedChange.blockPos.getX() >> 4, cachedChange.blockPos.getZ() >> 4), + HashSet::new, + (chunk, changes) -> { + // TODO (folia) only send chunks based on ticks? Need to make sure everything actually flushed in the end + /*boolean nextTick = true; + if (!FoliaSupport.isFolia()) { + int currentTick = MinecraftServer.currentTick; + nextTick = lastTick.get() > currentTick; + if (nextTick) { + lastTick.set(currentTick); + } + }*/ + flushAsync(chunk, changes, true); + }, + 2048, + 16 + ); } private Level getLevel() { @@ -94,22 +114,13 @@ 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()) { + if (PaperweightPlatformAdapter.isTickThreadFor(levelChunk)) { 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; - if (nextTick || cachedChanges.size() >= 1024) { - if (nextTick) { - lastTick.set(currentTick); - } - flushAsync(nextTick); - } + cache.insert(new CachedChange(levelChunk, blockPos, blockState)); return blockState; } @@ -226,16 +237,7 @@ public void onBlockStateChange( getLevel().onBlockStateChange(blockPos, oldState, newState); } - private synchronized void flushAsync(final boolean sendChunks) { - final Set changes = Set.copyOf(cachedChanges); - cachedChanges.clear(); - final Set toSend; - if (sendChunks) { - toSend = Set.copyOf(cachedChunksToSend); - cachedChunksToSend.clear(); - } else { - toSend = Collections.emptySet(); - } + private synchronized void flushAsync(IntPair chunk, Set changes, final boolean sendChunks) { RunnableVal runnableVal = new RunnableVal<>() { @Override public void run(Object value) { @@ -245,34 +247,17 @@ public void run(Object value) { if (!sendChunks) { return; } - for (IntPair chunk : toSend) { - PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); - } + PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); } }; - TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal)); + TaskManager.taskManager().async( + () -> PaperweightPlatformAdapter.task(runnableVal, getLevel().getWorld().getHandle(), chunk.x(), chunk.z()) + ); } @Override public synchronized void flush() { - RunnableVal runnableVal = new RunnableVal<>() { - @Override - public void run(Object value) { - cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState, - sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE) - )); - for (IntPair chunk : cachedChunksToSend) { - PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); - } - } - }; - if (Fawe.isMainThread()) { - runnableVal.run(); - } else { - TaskManager.taskManager().sync(runnableVal); - } - cachedChanges.clear(); - cachedChunksToSend.clear(); + this.cache.flush(); } private record CachedChange( diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java index 42333f9e56..2eecdfedc7 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -236,9 +236,9 @@ 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))); + BlockEntity blockEntity = PaperweightPlatformAdapter.sync(() -> getChunk().getBlockEntity( + new BlockPos((x & 15) + (chunkX << 4), y, (z & 15) + (chunkZ << 4))), + serverLevel, chunkX, chunkZ); if (blockEntity == null) { return null; } @@ -247,7 +247,10 @@ public CompoundTag getTile(int x, int y, int z) { @Override public Map getTiles() { - Map nmsTiles = getChunk().getBlockEntities(); + Map nmsTiles = PaperweightPlatformAdapter.sync( + () -> getChunk().getBlockEntities(), + serverLevel, chunkX, chunkZ + ); if (nmsTiles.isEmpty()) { return Collections.emptyMap(); } 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 147ec5be76..63ef199cd7 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 @@ -7,10 +7,12 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.math.BitArrayUnstretched; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.ReflectionUtils; import com.fastasyncworldedit.core.util.TaskManager; import com.mojang.datafixers.util.Either; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.Refraction; @@ -20,6 +22,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; import io.papermc.lib.PaperLib; +import io.papermc.paper.util.TickThread; import io.papermc.paper.world.ChunkEntitySlices; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; @@ -58,7 +61,6 @@ import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R3.CraftChunk; -import sun.misc.Unsafe; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -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; @@ -292,35 +295,41 @@ public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int c } CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { - CraftChunk chunk; try { - chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + 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); + boolean loaded = TaskManager.taskManager().syncGlobal(() -> Bukkit.getWorld(world) != null); if (loaded) { LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); // Retry chunk load - chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); } else { throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); } } addTicket(serverLevel, chunkX, chunkZ); - return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + // chunk is loaded now, can access it directly + return serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); } catch (Throwable e) { e.printStackTrace(); } } - return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); + return TaskManager.taskManager().syncGlobal(() -> serverLevel.getChunk(chunkX, chunkZ)); } private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { // 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) { @@ -374,7 +383,7 @@ public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boole ); } nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); - }); + }, BukkitAdapter.adapt(nmsWorld.getWorld()), chunkX, chunkZ); } private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { @@ -674,6 +683,21 @@ static List getEntities(LevelChunk chunk) { return List.of(); } + public static void task(Runnable task, ServerLevel level, int chunkX, int chunkZ) { + TaskManager.taskManager().task(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + + public static T sync(Supplier task, ServerLevel level, int chunkX, int chunkZ) { + return TaskManager.taskManager().syncAt(task, BukkitAdapter.adapt(level.getWorld()), chunkX, chunkZ); + } + + public static boolean isTickThreadFor(LevelChunk levelChunk) { + if (FoliaSupport.isFolia()) { + return TickThread.isTickThreadFor(levelChunk.level, levelChunk.locX, levelChunk.locZ); + } + return Fawe.isTickThread(); + } + record FakeIdMapBlock(int size) implements IdMap { @Override diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java index edf9e9f90d..a2cc22a95e 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java @@ -4,13 +4,13 @@ import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.collect.ImmutableList; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightGetBlocks; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightPlatformAdapter; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.regions.Region; @@ -438,11 +438,11 @@ protected List getBlockPopulators() { @Override protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { // BlockPopulator#populate has to be called synchronously for TileEntity access - TaskManager.taskManager().task(() -> { + PaperweightPlatformAdapter.task(() -> { final CraftWorld world = freshWorld.getWorld(); final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); blockPopulator.populate(world, random, chunk); - }); + }, freshWorld, levelChunk.getPos().x, levelChunk.getPos().z); } @Override diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/FaweBukkit.java index 5e60a704ce..77787dcd27 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,7 @@ 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.bukkit.util.ItemUtil; import com.fastasyncworldedit.bukkit.util.MinecraftVersion; import com.fastasyncworldedit.bukkit.util.image.BukkitImageViewer; @@ -22,6 +23,7 @@ import com.fastasyncworldedit.core.queue.implementation.preloader.AsyncPreloader; import com.fastasyncworldedit.core.queue.implementation.preloader.Preloader; import com.fastasyncworldedit.core.regions.FaweMaskManager; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.WEManager; import com.fastasyncworldedit.core.util.image.ImageViewer; @@ -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,16 +92,16 @@ 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); + TaskManager.taskManager().taskGlobal(this::setupPlotSquared); + Bukkit.getPluginManager().registerEvents(FaweBukkit.this, FaweBukkit.this.plugin); // Registered delayed Event Listeners - TaskManager.taskManager().task(() -> { + TaskManager.taskManager().taskGlobal(() -> { // 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(); @@ -192,6 +195,9 @@ public String getDebugInfo() { */ @Override public TaskManager getTaskManager() { + if (FoliaSupport.isFolia()) { + return new FoliaTaskManager(); + } return new BukkitTaskManager(plugin); } @@ -312,6 +318,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/BukkitFoliaAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitFoliaAdapter.java new file mode 100644 index 0000000000..cd32c5a371 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/BukkitFoliaAdapter.java @@ -0,0 +1,81 @@ +package com.fastasyncworldedit.bukkit.adapter; + +import com.destroystokyo.paper.util.SneakyThrow; +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 { + + private static final MethodHandle GET_ENTITIES = createEntitiesGetter(); + + @SuppressWarnings("unchecked") + public static List getEntities(World world, Region region) { + try { + return (List) GET_ENTITIES.invoke(world, region); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + // @formatter:off + private static MethodHandle createEntitiesGetter() { + try { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + Class craftWorldClass = BukkitReflectionUtils.getCbClass("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 ifTrue = dropArguments(addConvertedReturnCollapsed, 2, Region.class); + MethodHandle ifFalse = dropArguments(arrayListIdentity, 1, nmsEntityClass, Region.class); + MethodHandle ifInRegion = guardWithTest(isInRegion, ifTrue, ifFalse); + MethodHandle iterate = iteratedLoop(null, newArrayListHandle, dropArguments(ifInRegion, 2, Iterable.class)); + return filterArguments(iterate, 0, getEntities); + } catch (Throwable t) { + SneakyThrow.sneaky(t); + return null; + } + } + // @formatter:on +} 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..135ae595ec 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.bukkit.adapter; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.bukkit.BukkitAdapter; @@ -11,7 +12,16 @@ import org.bukkit.World; import org.bukkit.block.BlockState; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; import java.util.List; +import java.util.Map; + +import static com.fastasyncworldedit.core.util.FoliaSupport.getRethrowing; +import static com.fastasyncworldedit.core.util.FoliaSupport.runRethrowing; +import static java.lang.invoke.MethodType.methodType; /** * A base class for version-specific implementations of the BukkitImplAdapter @@ -21,6 +31,39 @@ */ public abstract class FaweAdapter extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { + private static final VarHandle CAPTURE_TREE_GENERATION; + private static final VarHandle CAPTURE_BLOCK_STATES; + private static final MethodHandle CAPTURED_BLOCK_STATES; + private static final MethodHandle GET_CURRENT_WORLD_DATA; + + static { + VarHandle captureTreeGeneration = null; + VarHandle captureBlockStates = null; + MethodHandle capturedBlockStates = null; + MethodHandle getCurrentWorldData = null; + if (FoliaSupport.isFolia()) { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + Class regionizedWorldDataClass = Class.forName("io.papermc.paper.threadedregions.RegionizedWorldData"); + Class serverLevelClass = regionizedWorldDataClass.getDeclaredField("world").getType(); + captureTreeGeneration = lookup.findVarHandle(regionizedWorldDataClass, "captureTreeGeneration", boolean.class); + captureBlockStates = lookup.findVarHandle(regionizedWorldDataClass, "captureBlockStates", boolean.class); + capturedBlockStates = lookup.findGetter(regionizedWorldDataClass, "capturedBlockStates", Map.class); + getCurrentWorldData = lookup.findVirtual( + serverLevelClass, + "getCurrentWorldData", + methodType(regionizedWorldDataClass) + ); + } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError("Incompatible Folia version", e); + } + } + CAPTURE_TREE_GENERATION = captureTreeGeneration; + CAPTURE_BLOCK_STATES = captureBlockStates; + CAPTURED_BLOCK_STATES = capturedBlockStates; + GET_CURRENT_WORLD_DATA = getCurrentWorldData; + } + @Override public boolean generateTree( final TreeGenerator.TreeType treeType, @@ -35,17 +78,17 @@ public boolean generateTree( } BlockVector3 target = blockVector3; SERVER_LEVEL serverLevel = getServerLevel(world); - List placed = TaskManager.taskManager().sync(() -> { - preCaptureStates(serverLevel); + List placed = TaskManager.taskManager().syncAt(() -> { + preCaptureStatesCommon(serverLevel); try { if (!world.generateTree(BukkitAdapter.adapt(world, target), bukkitType)) { return null; } - return getCapturedBlockStatesCopy(serverLevel); + return getCapturedBlockStatesCopyCommon(serverLevel); } finally { - postCaptureBlockStates(serverLevel); + postCaptureBlockStatesCommon(serverLevel); } - }); + }, BukkitAdapter.adapt(world), blockVector3.getBlockX() >> 4, blockVector3.getBlockZ() >> 4); if (placed == null || placed.isEmpty()) { return false; @@ -61,6 +104,57 @@ public boolean generateTree( return true; } + private void preCaptureStatesCommon(SERVER_LEVEL serverLevel) { + if (FoliaSupport.isFolia()) { + preCaptureStatesFolia(serverLevel); + } else { + preCaptureStates(serverLevel); + } + } + + private List getCapturedBlockStatesCopyCommon(SERVER_LEVEL serverLevel) { + if (FoliaSupport.isFolia()) { + return getCapturedBlockStatesCopyFolia(serverLevel); + } else { + return getCapturedBlockStatesCopy(serverLevel); + } + } + + private void postCaptureBlockStatesCommon(SERVER_LEVEL serverLevel) { + if (FoliaSupport.isFolia()) { + postCaptureBlockStatesFolia(serverLevel); + } else { + postCaptureBlockStates(serverLevel); + } + } + + private void preCaptureStatesFolia(SERVER_LEVEL serverLevel) { + runRethrowing(() -> { + Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel); + CAPTURE_TREE_GENERATION.set(currentWorldData, true); + CAPTURE_BLOCK_STATES.set(currentWorldData, true); + }); + } + + private List getCapturedBlockStatesCopyFolia(SERVER_LEVEL serverLevel) { + return getRethrowing(() -> { + Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel); + @SuppressWarnings("unchecked") + var capturedBlockStates = (Map) CAPTURED_BLOCK_STATES.invoke(currentWorldData); + return new ArrayList<>(capturedBlockStates.values()); + }); + } + + private void postCaptureBlockStatesFolia(SERVER_LEVEL serverLevel) { + runRethrowing(() -> { + Object currentWorldData = GET_CURRENT_WORLD_DATA.invoke(serverLevel); + CAPTURE_TREE_GENERATION.set(currentWorldData, false); + CAPTURE_BLOCK_STATES.set(currentWorldData, false); + var capturedBlockStates = (Map) CAPTURED_BLOCK_STATES.invoke(currentWorldData); + capturedBlockStates.clear(); + }); + } + protected abstract void preCaptureStates(SERVER_LEVEL serverLevel); protected abstract List getCapturedBlockStatesCopy(SERVER_LEVEL serverLevel); diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/IBukkitAdapter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/IBukkitAdapter.java index 29162b607f..3f278edb63 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/IBukkitAdapter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/IBukkitAdapter.java @@ -389,7 +389,7 @@ default boolean generateTree(TreeGenerator.TreeType type, EditSession editSessio * @return list of {@link org.bukkit.entity.Entity} */ default List getEntities(org.bukkit.World world) { - return TaskManager.taskManager().sync(world::getEntities); + return TaskManager.taskManager().syncGlobal(world::getEntities); } } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/StarlightRelighter.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/StarlightRelighter.java index 02729cfa95..a9deefe2aa 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/StarlightRelighter.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/StarlightRelighter.java @@ -78,7 +78,8 @@ protected IntConsumer postProcessCallback(Runnable andThen, Set coord LOGGER.warn("Processed {} chunks instead of {}", i, coords.size()); } // post process chunks on main thread - TaskManager.taskManager().task(() -> postProcessChunks(coords)); + // safe to run globally on Folia as ticket modification is behind a lock + TaskManager.taskManager().taskGlobal(() -> postProcessChunks(coords)); // call callback on our own threads TaskManager.taskManager().async(andThen); }; @@ -99,7 +100,7 @@ protected abstract void invokeRelight( */ protected void fixLighting(LongSet chunks, Runnable andThen) { Set coords = convertChunkKeysToChunkPos(chunks); - TaskManager.taskManager().task(() -> { + TaskManager.taskManager().taskGlobal(() -> { // trigger chunk load and apply ticket on main thread List> futures = chunkLoadFutures(coords); // collect futures and trigger relight once all chunks are loaded diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java index 7b95990843..a4a03c1bf5 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/ChunkListener.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.bukkit.listener; import com.fastasyncworldedit.bukkit.FaweBukkit; +import com.fastasyncworldedit.core.util.FoliaSupport; import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.util.FaweTimer; @@ -59,11 +60,14 @@ public abstract class ChunkListener implements Listener { Settings.settings().TICK_LIMITER.FALLING, Settings.settings().TICK_LIMITER.ITEMS}; public ChunkListener() { + if (FoliaSupport.isFolia()) { + return; + } if (Settings.settings().TICK_LIMITER.ENABLED) { PluginManager plm = Bukkit.getPluginManager(); Plugin plugin = Fawe.platform().getPlugin(); plm.registerEvents(this, plugin); - TaskManager.taskManager().repeat(() -> { + /*TaskManager.taskManager().repeat(() -> { Location tmpLoc = lastCancelPos; if (tmpLoc != null) { LOGGER.info("[FAWE Tick Limiter] Detected and cancelled physics lag source at {}", tmpLoc); @@ -85,7 +89,7 @@ public ChunkListener() { counter.put(key, badLimit); } badChunks.clear(); - }, Settings.settings().TICK_LIMITER.INTERVAL); + }, Settings.settings().TICK_LIMITER.INTERVAL)*/; } } diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/RenderListener.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/RenderListener.java index 22a09deb80..728209336b 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/RenderListener.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/listener/RenderListener.java @@ -28,7 +28,7 @@ public class RenderListener implements Listener { public RenderListener(Plugin plugin) { Bukkit.getPluginManager().registerEvents(this, plugin); - TaskManager.taskManager().repeat(new Runnable() { + /*TaskManager.taskManager().repeat(new Runnable() { private long last = 0; @Override @@ -76,7 +76,7 @@ public void run() { } } } - }, 1); + }, 1)*/; } private void setViewDistance(Player player, int value) { diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/GriefPreventionFeature.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/GriefPreventionFeature.java index 9f302d0579..3097059a76 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/GriefPreventionFeature.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/GriefPreventionFeature.java @@ -26,8 +26,10 @@ public GriefPreventionFeature(final Plugin griefpreventionPlugin) { public boolean isAllowed(Player player, Claim claim, MaskType type) { return claim != null && (claim.getOwnerName().equalsIgnoreCase(player.getName()) || claim .getOwnerName() - .equals(player.getUniqueId()) || TaskManager.taskManager().sync(() -> type == MaskType.MEMBER && - claim.allowBuild(player, Material.AIR) == null)); + .equals(player.getUniqueId()) || TaskManager.taskManager().syncAt( + () -> type == MaskType.MEMBER && claim.allowBuild(player, Material.AIR) == null, + BukkitAdapter.adapt(claim.getLesserBoundaryCorner()) + )); } @Override diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/ResidenceFeature.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/ResidenceFeature.java index a7c13cef55..2b2a95db30 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/ResidenceFeature.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/ResidenceFeature.java @@ -27,9 +27,9 @@ public boolean isAllowed(Player player, ClaimedResidence residence, MaskType typ return residence != null && (residence.getOwner().equals(player.getName()) || residence.getOwner().equals(player.getUniqueId().toString()) || - type == MaskType.MEMBER && TaskManager.taskManager().sync(() -> residence + type == MaskType.MEMBER && TaskManager.taskManager().syncWith(() -> residence .getPermissions() - .playerHas(player, "build", false))); + .playerHas(player, "build", false), BukkitAdapter.adapt(player))); } @Override diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java index c111ff6306..c795025ced 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/FaweDelegateRegionManager.java @@ -76,7 +76,7 @@ public boolean setCuboids( e.printStackTrace(); } finally { if (whenDone != null) { - TaskManager.taskManager().task(whenDone); + com.plotsquared.core.util.task.TaskManager.runTask(whenDone); } } } @@ -203,7 +203,7 @@ public boolean handleClear( RelightMode.valueOf(com.fastasyncworldedit.core.configuration.Settings.settings().LIGHTING.MODE) ); if (whenDone != null) { - TaskManager.taskManager().task(whenDone); + com.plotsquared.core.util.task.TaskManager.runTask(whenDone); } } }); @@ -272,7 +272,7 @@ public void swap( RelightMode.valueOf(com.fastasyncworldedit.core.configuration.Settings.settings().LIGHTING.MODE) ); if (whenDone != null) { - TaskManager.taskManager().task(whenDone); + com.plotsquared.core.util.task.TaskManager.runTask(whenDone); } } }); @@ -301,7 +301,7 @@ public void setBiome(CuboidRegion region, int extendBiome, BiomeType biome, Stri e.printStackTrace(); } if (whenDone != null) { - TaskManager.taskManager().task(whenDone); + com.plotsquared.core.util.task.TaskManager.runTask(whenDone); } } }); @@ -354,7 +354,7 @@ public boolean copyRegion( } } if (whenDone != null) { - TaskManager.taskManager().task(whenDone); + com.plotsquared.core.util.task.TaskManager.runTask(whenDone); } }); return true; @@ -378,7 +378,7 @@ public boolean regenerateRegion(final Location pos1, final Location pos2, boolea editSession.flushQueue(); } if (whenDone != null) { - TaskManager.taskManager().task(whenDone); + com.plotsquared.core.util.task.TaskManager.runTask(whenDone); } } }); 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..6e9701d766 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,17 @@ package com.fastasyncworldedit.bukkit.util; +import com.fastasyncworldedit.core.Fawe; import com.fastasyncworldedit.core.util.TaskManager; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nonnull; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; public class BukkitTaskManager extends TaskManager { @@ -14,11 +21,6 @@ public BukkitTaskManager(final Plugin plugin) { this.plugin = plugin; } - @Override - public int repeat(@Nonnull final Runnable runnable, final int interval) { - return this.plugin.getServer().getScheduler().scheduleSyncRepeatingTask(this.plugin, runnable, interval, interval); - } - @Override public int repeatAsync(@Nonnull final Runnable runnable, final int interval) { return this.plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(this.plugin, runnable, interval, interval); @@ -30,13 +32,28 @@ public void async(@Nonnull final Runnable runnable) { } @Override - public void task(@Nonnull final Runnable runnable) { - this.plugin.getServer().getScheduler().runTask(this.plugin, runnable).getTaskId(); + public void task(@NotNull final Runnable runnable, @NotNull final Location location) { + this.plugin.getServer().getScheduler().runTask(this.plugin, runnable); } @Override - public void later(@Nonnull final Runnable runnable, final int delay) { - this.plugin.getServer().getScheduler().runTaskLater(this.plugin, runnable, delay).getTaskId(); + public void task(@NotNull final Runnable runnable, @NotNull final World world, final int chunkX, final int chunkZ) { + this.plugin.getServer().getScheduler().runTask(this.plugin, runnable); + } + + @Override + public void taskGlobal(final Runnable runnable) { + this.plugin.getServer().getGlobalRegionScheduler().run(this.plugin, task -> runnable.run()); + } + + @Override + public void later(@NotNull final Runnable runnable, final Location location, final int delay) { + laterGlobal(runnable, delay); + } + + @Override + public void laterGlobal(@NotNull final Runnable runnable, final int delay) { + this.plugin.getServer().getScheduler().runTaskLater(this.plugin, runnable, delay); } @Override @@ -51,4 +68,31 @@ public void cancel(final int task) { } } + @Override + public T syncAt(final Supplier supplier, final World world, final int chunkX, final int chunkZ) { + return sync(supplier); + } + + @Override + public T syncWith(final Supplier supplier, final Player context) { + return sync(supplier); + } + + @Override + public T syncGlobal(final Supplier supplier) { + return sync(supplier); + } + + private T sync(final Supplier supplier) { + if (Fawe.isTickThread()) { + return supplier.get(); + } + try { + // TODO (folia) + return Fawe.instance().getQueueHandler().sync(supplier).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + } 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..6a3f5dd2be --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/FoliaTaskManager.java @@ -0,0 +1,181 @@ +package com.fastasyncworldedit.bukkit.util; + +import com.fastasyncworldedit.core.util.FoliaSupport; +import com.fastasyncworldedit.core.util.TaskManager; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; + +import java.lang.invoke.MethodHandle; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import static java.lang.invoke.MethodHandles.lookup; +import static java.lang.invoke.MethodType.methodType; + +public class FoliaTaskManager extends TaskManager { + + private final static MethodHandle IS_GLOBAL_TICK_THREAD; + + static { + try { + IS_GLOBAL_TICK_THREAD = lookup().findStatic(Bukkit.class, "isGlobalTickThread", methodType(boolean.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError("Incompatile Folia version", e); + } + } + + private final AtomicInteger idCounter = new AtomicInteger(); + + @Override + public int repeatAsync(@NotNull final Runnable runnable, final int interval) { + // TODO (folia) return some kind of own ScheduledTask instead of int + Bukkit.getAsyncScheduler().runAtFixedRate( + WorldEditPlugin.getInstance(), + asConsumer(runnable), + 0, + ticksToMs(interval), + TimeUnit.MILLISECONDS + ); + return idCounter.getAndIncrement(); + } + + @Override + public void async(@NotNull final Runnable runnable) { + Bukkit.getAsyncScheduler().runNow(WorldEditPlugin.getInstance(), asConsumer(runnable)); + } + + @Override + public void task(@NotNull final Runnable runnable, @NotNull final World world, final int chunkX, final int chunkZ) { + Bukkit.getRegionScheduler().run( + WorldEditPlugin.getInstance(), + BukkitAdapter.adapt(world), + chunkX, + chunkZ, + asConsumer(runnable) + ); + } + + @Override + public void taskGlobal(final Runnable runnable) { + Bukkit.getGlobalRegionScheduler().run(WorldEditPlugin.getInstance(), asConsumer(runnable)); + } + + @Override + public void later(@NotNull final Runnable runnable, final Location location, final int delay) { + Bukkit.getRegionScheduler().runDelayed( + WorldEditPlugin.getInstance(), + BukkitAdapter.adapt(location), + asConsumer(runnable), + delay + ); + } + + @Override + public void laterGlobal(@NotNull final Runnable runnable, final int delay) { + Bukkit.getGlobalRegionScheduler().runDelayed( + WorldEditPlugin.getInstance(), + asConsumer(runnable), + delay + ); + } + + @Override + public void laterAsync(@NotNull final Runnable runnable, final int delay) { + Bukkit.getAsyncScheduler().runDelayed( + WorldEditPlugin.getInstance(), + asConsumer(runnable), + ticksToMs(delay), + TimeUnit.MILLISECONDS + ); + } + + @Override + public void cancel(final int task) { + fail("Not implemented"); + } + + @Override + public T syncAt(final Supplier supplier, final World world, final int chunkX, final int chunkZ) { + final org.bukkit.World adapt = BukkitAdapter.adapt(world); + if (Bukkit.isOwnedByCurrentRegion(adapt, chunkX, chunkZ)) { + return supplier.get(); + } + ensureOffTickThread(); + FutureTask task = new FutureTask<>(supplier::get); + Bukkit.getRegionScheduler().run( + WorldEditPlugin.getInstance(), + adapt, + chunkX, + chunkZ, + asConsumer(task) + ); + try { + return task.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + private Consumer asConsumer(Runnable runnable) { + return __ -> runnable.run(); + } + + @Override + public T syncWith(final Supplier supplier, final Player context) { + final org.bukkit.entity.Player adapt = BukkitAdapter.adapt(context); + if (Bukkit.isOwnedByCurrentRegion(adapt)) { + return supplier.get(); + } + ensureOffTickThread(); + FutureTask task = new FutureTask<>(supplier::get); + adapt.getScheduler().execute(WorldEditPlugin.getInstance(), task, null, 0); + try { + return task.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Override + public T syncGlobal(final Supplier supplier) { + if (isGlobalTickThread()) { + return supplier.get(); + } + FutureTask task = new FutureTask<>(supplier::get); + Bukkit.getGlobalRegionScheduler().run(WorldEditPlugin.getInstance(), asConsumer(task)); + try { + return task.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + private boolean isGlobalTickThread() { + return FoliaSupport.getRethrowing(() -> (boolean) IS_GLOBAL_TICK_THREAD.invokeExact()); + } + + private void ensureOffTickThread() { + if (FoliaSupport.isTickThread()) { + throw new IllegalStateException("Expected to be off tick thread"); + } + } + + private int ticksToMs(int ticks) { + // 1 tick = 50ms + return ticks * 50; + } + + private T fail(String message) { + throw new UnsupportedOperationException(message); + } + +} diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/image/BukkitImageViewer.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/image/BukkitImageViewer.java index 2a61f30838..7f6ca65523 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/image/BukkitImageViewer.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/image/BukkitImageViewer.java @@ -4,6 +4,7 @@ import com.fastasyncworldedit.core.util.image.Drawable; import com.fastasyncworldedit.core.util.image.ImageUtil; import com.fastasyncworldedit.core.util.image.ImageViewer; +import com.sk89q.worldedit.bukkit.BukkitAdapter; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -164,7 +165,7 @@ private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) { controller.showInFrames(player, frames, true); } else { int slot = getMapSlot(player); - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncWith(() -> { if (slot == -1) { if (initializing) { player.getInventory().setItemInMainHand(new ItemStack(Material.MAP)); @@ -175,7 +176,7 @@ private void view(@Nullable BufferedImage image, @Nullable Drawable drawable) { player.getInventory().setHeldItemSlot(slot); } return null; - }); + }, BukkitAdapter.adapt(player)); if (image == null && drawable != null) { image = drawable.draw(); } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java index 3c2e99f420..fd532824d4 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java @@ -66,12 +66,12 @@ public String getName() { @Deprecated public void printRaw(String msg) { //FAWE start - ensure executed on main thread - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncAt(() -> { for (String part : msg.split("\n")) { sender.sendMessage(part); } return null; - }); + }, getLocation()); //FAWE end } @@ -79,12 +79,12 @@ public void printRaw(String msg) { @Deprecated public void print(String msg) { //FAWE start - ensure executed on main thread - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncAt(() -> { for (String part : msg.split("\n")) { print(TextComponent.of(part, TextColor.LIGHT_PURPLE)); } return null; - }); + }, getLocation()); //FAWE end } @@ -92,12 +92,12 @@ public void print(String msg) { @Deprecated public void printDebug(String msg) { //FAWE start - ensure executed on main thread - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncAt(() -> { for (String part : msg.split("\n")) { print(TextComponent.of(part, TextColor.GRAY)); } return null; - }); + }, getLocation()); //FAWE end } @@ -105,22 +105,22 @@ public void printDebug(String msg) { @Deprecated public void printError(String msg) { //FAWE start - ensure executed on main thread - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncAt(() -> { for (String part : msg.split("\n")) { print(TextComponent.of(part, TextColor.RED)); } return null; - }); + }, getLocation()); //FAWE end } @Override public void print(Component component) { //FAWE start - ensure executed on main thread - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncAt(() -> { TextAdapter.sendMessage(sender, WorldEditText.format(component, getLocale())); return null; - }); + }, getLocation()); //FAWE end } 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..9e1d5149a9 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; @@ -52,6 +53,7 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; +import io.papermc.lib.PaperLib; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -69,7 +71,9 @@ import java.util.Locale; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; public class BukkitPlayer extends AbstractPlayerActor { @@ -161,7 +165,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 +187,7 @@ public void giveItem(BaseItemStack itemStack) { } player.updateInventory(); return null; - }); + }, this); } //FAWE end @@ -239,15 +243,21 @@ public boolean trySetPosition(Vector3 pos, float pitch, float yaw) { } } org.bukkit.World finalWorld = world; + final Location target = new Location(finalWorld, pos.getX(), pos.getY(), pos.getZ(), yaw, pitch); + Supplier> teleport = () -> PaperLib.teleportAsync(player, target); + if (FoliaSupport.isTickThread()) { + teleport.get().whenComplete((b, thr) -> { + if (thr != null) { + thr.printStackTrace(); + } + if (!b) { + player.sendMessage("Teleportation failed"); + } + }); + return true; // TODO (folia) this might not be correct + } + return TaskManager.taskManager().syncWith(() -> teleport.get().join(), this); //FAWE end - return TaskManager.taskManager().sync(() -> player.teleport(new Location( - finalWorld, - pos.getX(), - pos.getY(), - pos.getZ(), - yaw, - pitch - ))); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayerBlockBag.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayerBlockBag.java index b060d78f88..1a64d55e24 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayerBlockBag.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayerBlockBag.java @@ -172,10 +172,10 @@ public void storeBlock(BlockState blockState, int amount) throws BlockBagExcepti @Override public void flushChanges() { if (items != null) { - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncWith(() -> { player.getInventory().setContents(items); return null; - }); + }, BukkitAdapter.adapt(player)); items = null; } } 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 1598cb5474..f0055aafe5 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.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,8 +45,11 @@ 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; import com.sk89q.worldedit.util.SideEffectSet; import com.sk89q.worldedit.util.TreeGenerator; @@ -146,7 +151,10 @@ public BukkitWorld(World world) { public List getEntities(Region region) { World world = getWorld(); - List ents = TaskManager.taskManager().sync(world::getEntities); + if (FoliaSupport.isFolia()) { + return BukkitFoliaAdapter.getEntities(world, region); + } + List ents = TaskManager.taskManager().syncGlobal(world::getEntities); List entities = new ArrayList<>(); for (Entity ent : ents) { if (region.contains(BukkitAdapter.asBlockVector(ent.getLocation()))) { @@ -158,9 +166,43 @@ public List getEntities(Region region) { @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().sync(getWorld()::getEntities); + List ents = TaskManager.taskManager().syncGlobal(getWorld()::getEntities); for (Entity entity : ents) { list.add(BukkitAdapter.adapt(entity)); } @@ -290,7 +332,7 @@ public boolean clearContainerBlockContents(BlockVector3 pt) { return false; } - TaskManager.taskManager().sync(() -> { + TaskManager.taskManager().syncAt(() -> { InventoryHolder chest = (InventoryHolder) state; Inventory inven = chest.getInventory(); if (chest instanceof Chest) { @@ -298,7 +340,7 @@ public boolean clearContainerBlockContents(BlockVector3 pt) { } inven.clear(); return null; - }); + }, new Location(this, pt.toVector3())); return 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..10988bbadc 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 @@ -400,7 +400,6 @@ public void onDisable() { if (config != null) { config.unload(); } - this.getServer().getScheduler().cancelTasks(this); } /** diff --git a/worldedit-bukkit/src/main/resources/plugin.yml b/worldedit-bukkit/src/main/resources/plugin.yml index cd12b10086..b2a248ab5d 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..0dffdb58b1 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,14 +130,16 @@ private Fawe(final IFawe implementation) { this.timer = new FaweTimer(); // Delayed worldedit setup - TaskManager.taskManager().later(() -> { + TaskManager.taskManager().laterGlobal(() -> { try { WEManager.weManager().addManagers(Fawe.this.implementation.getMaskManagers()); } catch (Throwable ignored) { } - }, 0); + }, 1); - TaskManager.taskManager().repeat(timer, 1); + if (!FoliaSupport.isFolia()) { + // TODO (folia) TaskManager.taskManager().repeat(timer, 1); + } clipboardExecutor = new KeyQueuedExecutorService<>(new ThreadPoolExecutor( 1, @@ -206,10 +209,15 @@ public static void setupInjector() { } } + @Deprecated public static boolean isMainThread() { return instance == null || instance.thread == Thread.currentThread(); } + public static boolean isTickThread() { + return instance == null || instance.implementation.isTickThread(); + } + /** * Non-api. Handles an input FAWE exception if not already handled, given the input boolean array. * Looks at the {@link FaweException.Type} and decides what to do (rethrows if we want to attempt to show the error to the diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/IFawe.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/IFawe.java index 5c8a80c02f..e467ba6e24 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/IFawe.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/IFawe.java @@ -49,4 +49,6 @@ default boolean isChunksStretched() { FAWEPlatformAdapterImpl getPlatformAdapter(); + boolean isTickThread(); + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/entity/LazyBaseEntity.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/entity/LazyBaseEntity.java index f591cf8265..b88bde339c 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 @@ -1,7 +1,6 @@ package com.fastasyncworldedit.core.entity; import com.fastasyncworldedit.core.Fawe; -import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.world.entity.EntityType; @@ -27,7 +26,8 @@ public CompoundBinaryTag getNbt() { if (Fawe.isMainThread()) { setNbt(tmp.get()); } else { - setNbt(TaskManager.taskManager().sync(tmp)); + // TODO (folia) + // setNbt(TaskManager.taskManager().sync(tmp)); } } return super.getNbt(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/BlockTranslateExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/BlockTranslateExtent.java index d7b8a986eb..f04412e708 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/BlockTranslateExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/BlockTranslateExtent.java @@ -9,7 +9,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -public class BlockTranslateExtent extends AbstractDelegateExtent { +public final class BlockTranslateExtent extends AbstractDelegateExtent { private final int dx; private final int dy; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java index f1c523ce33..68307ffe1e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/DisallowedBlocksExtent.java @@ -29,7 +29,7 @@ import java.util.Set; import java.util.stream.Collectors; -public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IBatchProcessor { +public final class DisallowedBlocksExtent extends AbstractDelegateExtent implements IBatchProcessor { private static final BlockState RESERVED = BlockTypes.__RESERVED__.getDefaultState(); private final Set> remaps; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ExtentHeightCacher.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ExtentHeightCacher.java index af008d1c95..36011e594c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ExtentHeightCacher.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ExtentHeightCacher.java @@ -4,7 +4,7 @@ import java.util.Arrays; -public class ExtentHeightCacher extends PassthroughExtent { +public final class ExtentHeightCacher extends PassthroughExtent { private transient int cacheBotX = Integer.MIN_VALUE; private transient int cacheBotZ = Integer.MIN_VALUE; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HeightBoundExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HeightBoundExtent.java index c22d9e6bed..817d2c3bfa 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HeightBoundExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HeightBoundExtent.java @@ -12,7 +12,7 @@ import java.util.Collection; import java.util.Collections; -public class HeightBoundExtent extends FaweRegionExtent { +public final class HeightBoundExtent extends FaweRegionExtent { private final int min; private final int max; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HistoryExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HistoryExtent.java index 2fc5133c65..9ac0180433 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HistoryExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/HistoryExtent.java @@ -25,7 +25,7 @@ /** * Stores changes to a {@link ChangeSet}. */ -public class HistoryExtent extends AbstractDelegateExtent { +public final class HistoryExtent extends AbstractDelegateExtent { private final MutableBlockVector3 mutable = new MutableBlockVector3(); private AbstractChangeSet changeSet; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java index 24d440c575..3429a1ba58 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/LimitExtent.java @@ -37,7 +37,7 @@ import java.util.UUID; import java.util.function.Consumer; -public class LimitExtent extends AbstractDelegateExtent { +public final class LimitExtent extends AbstractDelegateExtent { private final FaweLimit limit; private final boolean[] faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length]; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MemoryCheckingExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MemoryCheckingExtent.java index e2d215c565..d33291d1c2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MemoryCheckingExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MemoryCheckingExtent.java @@ -8,7 +8,7 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.Extent; -public class MemoryCheckingExtent extends PassthroughExtent { +public final class MemoryCheckingExtent extends PassthroughExtent { private final Actor actor; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java index c13c5965b8..9e02dba945 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/MultiRegionExtent.java @@ -15,7 +15,7 @@ import java.util.List; import java.util.concurrent.Future; -public class MultiRegionExtent extends FaweRegionExtent { +public final class MultiRegionExtent extends FaweRegionExtent { @Nullable private final RegionIntersection intersection; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java index 4f0ad4960f..d4c7dc6aaf 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/NullExtent.java @@ -40,7 +40,7 @@ import java.util.concurrent.Future; //todo This should be removed in favor of com.sk89q.worldedit.extent.NullExtent -public class NullExtent extends FaweRegionExtent implements IBatchProcessor { +public final class NullExtent extends FaweRegionExtent implements IBatchProcessor { private final FaweException reason; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/PassthroughExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/PassthroughExtent.java index 5ac2f9d7be..23f0fa6031 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/PassthroughExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/PassthroughExtent.java @@ -26,7 +26,7 @@ import java.util.List; import java.util.Set; -public class PassthroughExtent extends AbstractDelegateExtent { +public abstract class PassthroughExtent extends AbstractDelegateExtent { /** * Create a new instance. diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/PositionTransformExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/PositionTransformExtent.java index 6d74acc166..99ef6d1373 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/PositionTransformExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/PositionTransformExtent.java @@ -11,7 +11,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; -public class PositionTransformExtent extends ResettableExtent { +public final class PositionTransformExtent extends ResettableExtent { private transient MutableBlockVector3 mutable = new MutableBlockVector3(); private transient BlockVector3 min; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ProcessedWEExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ProcessedWEExtent.java index 5abe903480..e9e90e0f87 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ProcessedWEExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ProcessedWEExtent.java @@ -18,7 +18,7 @@ import java.util.UUID; -public class ProcessedWEExtent extends AbstractDelegateExtent { +public final class ProcessedWEExtent extends AbstractDelegateExtent { private final FaweLimit limit; private final Extent extent; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ResettableExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ResettableExtent.java index 0e37984156..469a27c03c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ResettableExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/ResettableExtent.java @@ -14,7 +14,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -public class ResettableExtent extends AbstractDelegateExtent implements Serializable { +public abstract class ResettableExtent extends AbstractDelegateExtent implements Serializable { public ResettableExtent(Extent parent) { super(parent); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SingleRegionExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SingleRegionExtent.java index 1caea9dbce..249b9e84cb 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SingleRegionExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SingleRegionExtent.java @@ -11,7 +11,7 @@ import java.util.Collections; import java.util.concurrent.Future; -public class SingleRegionExtent extends FaweRegionExtent { +public final class SingleRegionExtent extends FaweRegionExtent { private final Region region; 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..657322ce3b 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 @@ -6,7 +6,7 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.world.block.BlockStateHolder; -public class SlowExtent extends AbstractDelegateExtent { +public final class SlowExtent extends AbstractDelegateExtent { private final long THRESHOLD = 50 * 1000000; // 1 tick private final long nanos; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SourceMaskExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SourceMaskExtent.java index 6c22303722..389f5b69f7 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SourceMaskExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SourceMaskExtent.java @@ -9,7 +9,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -public class SourceMaskExtent extends TemporalExtent { +public final class SourceMaskExtent extends TemporalExtent { private Mask mask; private final MutableBlockVector3 mutable = new MutableBlockVector3(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/StripNBTExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/StripNBTExtent.java index ded96d5aa8..4d1b287c5f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/StripNBTExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/StripNBTExtent.java @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -public class StripNBTExtent extends AbstractDelegateExtent implements IBatchProcessor { +public final class StripNBTExtent extends AbstractDelegateExtent implements IBatchProcessor { private final Set strip; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SupplyingExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SupplyingExtent.java index 640243d5d4..96952de4c4 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SupplyingExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/SupplyingExtent.java @@ -7,7 +7,7 @@ /** * An extent that delegates actions to another extent that may change at any time. */ -public class SupplyingExtent extends PassthroughExtent { +public final class SupplyingExtent extends PassthroughExtent { private final Supplier extentSupplier; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TemporalExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TemporalExtent.java index ac36bef5ae..030fcc290e 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TemporalExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TemporalExtent.java @@ -8,7 +8,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockTypes; -public class TemporalExtent extends PassthroughExtent { +public abstract class TemporalExtent extends PassthroughExtent { private int x; private int y; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TransformExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TransformExtent.java deleted file mode 100644 index 64b940d97e..0000000000 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TransformExtent.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.fastasyncworldedit.core.extent; - -import com.fastasyncworldedit.core.math.MutableBlockVector3; -import com.fastasyncworldedit.core.math.MutableVector3; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.extent.transform.BlockTransformExtent; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.Vector3; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; - -/** - * @deprecated Unused internal, will be removed in v3 - */ -@Deprecated(forRemoval = true, since = "TODO") -public class TransformExtent extends BlockTransformExtent { - - private final MutableVector3 mutable1 = new MutableVector3(); - private final MutableBlockVector3 mutable2 = new MutableBlockVector3(); - private BlockVector3 min; - - public TransformExtent(Extent parent) { - super(parent); - } - - @Override - public ResettableExtent setExtent(Extent extent) { - min = null; - return super.setExtent(extent); - } - - @Override - public BlockVector3 getMinimumPoint() { - BlockVector3 pos1 = getPos(super.getMinimumPoint()); - BlockVector3 pos2 = getPos(super.getMaximumPoint()); - return pos1.getMinimum(pos2); - } - - @Override - public BlockVector3 getMaximumPoint() { - BlockVector3 pos1 = getPos(super.getMinimumPoint()); - BlockVector3 pos2 = getPos(super.getMaximumPoint()); - return pos1.getMaximum(pos2); - } - - @Override - public void setOrigin(BlockVector3 pos) { - this.min = pos; - } - - public BlockVector3 getPos(BlockVector3 pos) { - if (min == null) { - min = pos; - } - mutable1.mutX(pos.getX() - min.getX()); - mutable1.mutY(pos.getY() - min.getY()); - mutable1.mutZ(pos.getZ() - min.getZ()); - Vector3 tmp = getTransform().apply(mutable1); - mutable2.mutX(tmp.getX() + min.getX()); - mutable2.mutY(tmp.getY() + min.getY()); - mutable2.mutZ(tmp.getZ() + min.getZ()); - return mutable2; - } - - public BlockVector3 getPos(int x, int y, int z) { - if (min == null) { - min = BlockVector3.at(x, y, z); - } - mutable1.mutX(x - min.getX()); - mutable1.mutY(y - min.getY()); - mutable1.mutZ(z - min.getZ()); - Vector3 tmp = getTransform().apply(mutable1); - mutable2.mutX(tmp.getX() + min.getX()); - mutable2.mutY(tmp.getY() + min.getY()); - mutable2.mutZ(tmp.getZ() + min.getZ()); - return tmp.toBlockPoint(); - } - - @Override - public BlockState getBlock(int x, int y, int z) { - BlockVector3 p = getPos(x, y, z); - return transform(super.getBlock(p.getX(), p.getY(), p.getZ())); - } - - @Override - public BaseBlock getFullBlock(BlockVector3 position) { - return transform(super.getFullBlock(getPos(position))); - } - - @Override - public BiomeType getBiomeType(int x, int y, int z) { - BlockVector3 p = getPos(x, y, z); - return super.getBiomeType(p.getX(), y, p.getZ()); - } - - @Override - @SuppressWarnings("unchecked") - public > boolean setBlock(int x, int y, int z, T block) - throws WorldEditException { - return super.setBlock(getPos(x, y, z), transformInverse(block)); - } - - - @Override - @SuppressWarnings("unchecked") - public > boolean setBlock(BlockVector3 location, B block) - throws WorldEditException { - return super.setBlock(getPos(location), transformInverse(block)); - } - - @Override - public boolean setBiome(int x, int y, int z, BiomeType biome) { - BlockVector3 p = getPos(x, y, z); - return super.setBiome(p.getX(), p.getY(), p.getZ(), biome); - } - -} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/NMSRelighter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/NMSRelighter.java index e38df12f54..b53997f043 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/NMSRelighter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/lighting/NMSRelighter.java @@ -914,7 +914,8 @@ public synchronized void close() { queue.flush(); finished.set(true); } else { - TaskManager.taskManager().sync(new RunnableVal<>() { + // fine to sync global, starlight is required for Folia + TaskManager.taskManager().syncGlobal(new RunnableVal<>() { @Override public void run(Object value) { queue.flush(); @@ -951,7 +952,7 @@ public void run(Object value) { if (Settings.settings().LIGHTING.ASYNC) { runnable.run(); } else { - TaskManager.taskManager().sync(runnable); + TaskManager.taskManager().syncGlobal(runnable); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/transform/RandomTransform.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/transform/RandomTransform.java index 93ab1ac995..a839ec3237 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/transform/RandomTransform.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/transform/RandomTransform.java @@ -7,10 +7,10 @@ import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; @@ -20,10 +20,9 @@ public class RandomTransform extends SelectTransform { private final SimpleRandom random; - private final Map weights = new HashMap<>(); + private final List> weights; private transient RandomCollection collection; - private transient LinkedHashSet extents = new LinkedHashSet<>(); public RandomTransform() { this(new TrueRandom()); @@ -36,27 +35,27 @@ public RandomTransform() { */ public RandomTransform(SimpleRandom random) { this.random = random; + this.weights = new ArrayList<>(); } @Override public AbstractDelegateExtent getExtent(int x, int y, int z) { - return collection.next(x, y, z); + return collection.next(this.random, x, y, z); } @Override public AbstractDelegateExtent getExtent(int x, int z) { - return collection.next(x, 0, z); + return collection.next(this.random, x, 0, z); } @Override public ResettableExtent setExtent(Extent extent) { if (collection == null) { - collection = RandomCollection.of(weights, random); - extents = new LinkedHashSet<>(weights.keySet()); + collection = RandomCollection.of(weights); } super.setExtent(extent); - for (ResettableExtent current : extents) { - current.setExtent(extent); + for (RandomCollection.Weighted current : this.weights) { + current.value().setExtent(extent); } return this; } @@ -72,13 +71,12 @@ public ResettableExtent setExtent(Extent extent) { */ public void add(ResettableExtent extent, double chance) { checkNotNull(extent); - weights.put(extent, chance); - collection = RandomCollection.of(weights, random); - this.extents.add(extent); + weights.add(new RandomCollection.Weighted<>(extent, chance)); + collection = RandomCollection.of(weights); } public Set getExtents() { - return extents; + return this.weights.stream().map(RandomCollection.Weighted::value).collect(Collectors.toSet()); } public RandomCollection getCollection() { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/BlockPositionChange.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/BlockPositionChange.java new file mode 100644 index 0000000000..e9c233269b --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/BlockPositionChange.java @@ -0,0 +1,16 @@ +package com.fastasyncworldedit.core.history.change; + +import com.sk89q.worldedit.history.change.Change; +import org.jetbrains.annotations.ApiStatus; + +/** + * Represents a change that is associated with {@code (x, y, z)} block coordinates. + * @since TODO + */ +@ApiStatus.Internal +public sealed abstract class BlockPositionChange implements Change + permits MutableBlockChange, MutableFullBlockChange { + public int x; + public int y; + public int z; +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableBlockChange.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableBlockChange.java index f4797581fa..4739405073 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableBlockChange.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableBlockChange.java @@ -2,17 +2,13 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.history.UndoContext; -import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.world.block.BlockState; +import org.jetbrains.annotations.ApiStatus; -public class MutableBlockChange implements Change { - - public int z; - public int y; - public int x; +@ApiStatus.Internal +public final class MutableBlockChange extends BlockPositionChange { public int ordinal; - public MutableBlockChange(int x, int y, int z, int ordinal) { this.x = x; this.y = y; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableFullBlockChange.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableFullBlockChange.java index 2b99cb8a91..b23903f452 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableFullBlockChange.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/MutableFullBlockChange.java @@ -4,15 +4,12 @@ import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.extent.inventory.BlockBagException; import com.sk89q.worldedit.history.UndoContext; -import com.sk89q.worldedit.history.change.Change; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockTypesCache; +import org.jetbrains.annotations.ApiStatus; -public class MutableFullBlockChange implements Change { - - public int z; - public int y; - public int x; +@ApiStatus.Internal +public final class MutableFullBlockChange extends BlockPositionChange { public int from; public int to; public BlockBag blockBag; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java index 71232a31c0..f20af641b3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/FaweStreamChangeSet.java @@ -6,6 +6,7 @@ import com.fastasyncworldedit.core.history.change.MutableEntityChange; import com.fastasyncworldedit.core.history.change.MutableFullBlockChange; import com.fastasyncworldedit.core.history.change.MutableTileChange; +import com.fastasyncworldedit.core.history.change.BlockPositionChange; import com.fastasyncworldedit.core.internal.exception.FaweSmallEditUnsupportedException; import com.fastasyncworldedit.core.internal.io.FaweInputStream; import com.fastasyncworldedit.core.internal.io.FaweOutputStream; @@ -20,6 +21,7 @@ import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockTypes; +import org.jetbrains.annotations.ApiStatus; import java.io.EOFException; import java.io.IOException; @@ -32,6 +34,7 @@ /** * FAWE stream ChangeSet offering support for extended-height worlds */ +@ApiStatus.Internal public abstract class FaweStreamChangeSet extends AbstractChangeSet { public static final int HEADER_SIZE = 9; @@ -68,19 +71,15 @@ private void init(boolean storeRedo, boolean smallLoc) { } } - public interface FaweStreamPositionDelegate { + interface FaweStreamPositionDelegate { void write(OutputStream out, int x, int y, int z) throws IOException; - int readX(FaweInputStream in) throws IOException; - - int readY(FaweInputStream in) throws IOException; - - int readZ(FaweInputStream in) throws IOException; + void read(FaweInputStream in, BlockPositionChange change) throws IOException; } - public interface FaweStreamIdDelegate { + interface FaweStreamIdDelegate { void writeChange(FaweOutputStream out, int from, int to) throws IOException; @@ -138,6 +137,7 @@ public void readCombined(FaweInputStream is, MutableFullBlockChange change) thro } if (mode == 1 || mode == 4) { // small posDel = new FaweStreamPositionDelegate() { + final byte[] buffer = new byte[4]; int lx; int ly; int lz; @@ -162,23 +162,14 @@ public void write(OutputStream out, int x, int y, int z) throws IOException { out.write(b4); } - final byte[] buffer = new byte[4]; - @Override - public int readX(FaweInputStream in) throws IOException { + public void read(final FaweInputStream in, final BlockPositionChange change) throws IOException { in.readFully(buffer); - return lx = lx + ((((buffer[1] & 0xFF) | ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20); + change.x = lx = lx + ((((buffer[1] & 0xFF) | ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20); + change.y = (ly = ly + buffer[0]) & 0xFF; + change.z = lz = lz + ((((buffer[2] & 0xFF) | ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20); } - @Override - public int readY(FaweInputStream in) { - return (ly = ly + buffer[0]) & 0xFF; - } - - @Override - public int readZ(FaweInputStream in) throws IOException { - return lz = lz + ((((buffer[2] & 0xFF) | ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20); - } }; } else { posDel = new FaweStreamPositionDelegate() { @@ -201,19 +192,11 @@ public void write(OutputStream stream, int x, int y, int z) throws IOException { } @Override - public int readX(FaweInputStream is) throws IOException { - is.readFully(buffer); - return lx = lx + ((buffer[0] & 0xFF) | (buffer[1] << 8)); - } - - @Override - public int readY(FaweInputStream is) throws IOException { - return ly = ly + ((buffer[4] & 0xFF) | (buffer[5]) << 8); - } - - @Override - public int readZ(FaweInputStream is) throws IOException { - return lz = lz + ((buffer[2] & 0xFF) | (buffer[3]) << 8); + public void read(final FaweInputStream in, final BlockPositionChange change) throws IOException { + in.readFully(buffer); + change.x = lx = lx + ((buffer[0] & 0xFF) | (buffer[1] << 8)); + change.z = lz = lz + ((buffer[2] & 0xFF) | (buffer[3]) << 8); + change.y = ly = ly + ((buffer[4] & 0xFF) | (buffer[5]) << 8); } }; } @@ -428,9 +411,9 @@ public Iterator getBlockIterator(final boolean dir) throws I public MutableBlockChange read() { try { - change.x = posDel.readX(is) + originX; - change.y = posDel.readY(is); - change.z = posDel.readZ(is) + originZ; + posDel.read(is, change); + change.x += originX; + change.z += originZ; idDel.readCombined(is, change, dir); return change; } catch (EOFException ignored) { @@ -545,9 +528,9 @@ public Iterator getFullBlockIterator(BlockBag blockBag, public MutableFullBlockChange read() { try { - change.x = posDel.readX(is) + originX; - change.y = posDel.readY(is); - change.z = posDel.readZ(is) + originZ; + posDel.read(is, change); + change.x += originX; + change.z += originZ; idDel.readCombined(is, change); return change; } catch (EOFException ignored) { @@ -765,11 +748,9 @@ public SimpleChangeSetSummary summarize(Region region, boolean shallow) { int amount = (Settings.settings().HISTORY.BUFFER_SIZE - HEADER_SIZE) / 9; MutableFullBlockChange change = new MutableFullBlockChange(null, 0, false); for (int i = 0; i < amount; i++) { - int x = posDel.readX(fis) + ox; - int y = posDel.readY(fis); - int z = posDel.readZ(fis) + ox; + posDel.read(fis, change); idDel.readCombined(fis, change); - summary.add(x, z, change.to); + summary.add(change.x + ox, change.z + oz, change.to); } } } catch (EOFException ignored) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index e88a9ccd3b..3d16024dec 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -43,7 +43,7 @@ import java.util.concurrent.ForkJoinTask; import java.util.stream.IntStream; -public class ParallelQueueExtent extends PassthroughExtent { +public final class ParallelQueueExtent extends PassthroughExtent { private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final ThreadLocal extents = new ThreadLocal<>(); 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 7bdbf46455..0b11d70bfc 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; @@ -90,13 +91,14 @@ public abstract class QueueHandler implements Trimable, Runnable { private long allocate = 50; protected QueueHandler() { - TaskManager.taskManager().repeat(this, 1); + // TODO (folia) make main thread independent + // TaskManager.taskManager().repeat(this, 1); } @Override public void run() { - if (!Fawe.isMainThread()) { - throw new IllegalStateException("Not main thread"); + if (!Fawe.isTickThread()) { + throw new IllegalStateException("Not ticking thread"); } if (!syncTasks.isEmpty()) { long currentAllocate = getAllocate(); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index 6e06ce3481..a27c3dd1a0 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 @@ -43,7 +43,7 @@ * This queue is reusable {@link #init(Extent, IChunkCache, IChunkCache)} */ @SuppressWarnings({"unchecked", "rawtypes"}) -public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implements IQueueExtent { +public final class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implements IQueueExtent { private static final Logger LOGGER = LogManagerCompat.getLogger(); 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..e5823c6870 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/FoliaSupport.java @@ -0,0 +1,69 @@ +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 implementation details are present + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + 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()); + } + + + @FunctionalInterface + public interface ThrowingSupplier { + + T get() throws Throwable; + + } + @FunctionalInterface + public interface ThrowingRunnable { + + void run() throws Throwable; + + } + + public static void runRethrowing(ThrowingRunnable runnable) { + getRethrowing(() -> { + runnable.run(); + return null; + }); + } + + public static T getRethrowing(ThrowingSupplier supplier) { + try { + return supplier.get(); + } catch (RuntimeException | Error e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/TaskManager.java index 176e02673b..40e7085b2e 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 @@ -3,15 +3,15 @@ import com.fastasyncworldedit.core.Fawe; 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.Player; import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.World; import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -46,14 +46,6 @@ public static TaskManager taskManager() { return INSTANCE; } - /** - * Run a repeating task on the main thread. - * - * @param runnable the task to run - * @param interval in ticks - */ - public abstract int repeat(@Nonnull final Runnable runnable, final int interval); - /** * Run a repeating task asynchronously. * @@ -75,7 +67,13 @@ public static TaskManager taskManager() { * * @param runnable the task to run */ - public abstract void task(@Nonnull final Runnable runnable); + public void task(@Nonnull final Runnable runnable, @Nonnull Location location) { + task(runnable, (World) location.getExtent(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + } + + public abstract void task(@Nonnull final Runnable runnable, @Nonnull World world, int chunkX, int chunkZ); + + public abstract void taskGlobal(Runnable runnable); /** * Get the public ForkJoinPool. @@ -159,6 +157,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()); @@ -171,66 +170,21 @@ public void runUnsafe(Runnable run) { } /** - * Run a task on the current thread or asynchronously. - * - If it's already the main thread, it will just call run() - * - * @param runnable the task to run - * @param async whether the task should run on the main thread - */ - public void taskNow(@Nonnull final Runnable runnable, boolean async) { - if (async) { - async(runnable); - } else { - runnable.run(); - } - } - - /** - * Run a task as soon as possible on the main thread. - * - Non blocking if not calling from the main thread + * Run a task later on the ticking thread at the given location. * * @param runnable the task to run + * @param location the location context to run at + * @param delay in ticks */ - public void taskNowMain(@Nonnull final Runnable runnable) { - if (Fawe.isMainThread()) { - runnable.run(); - } else { - task(runnable); - } - } + public abstract void later(@Nonnull final Runnable runnable, Location location, final int delay); /** - * Run a task as soon as possible not on the main thread. - * - * @param runnable the task to run - * @see Fawe#isMainThread() - */ - public void taskNowAsync(@Nonnull final Runnable runnable) { - taskNow(runnable, Fawe.isMainThread()); - } - - /** - * Run a task on the main thread at the next tick or now async. - * - * @param runnable the task to run. - * @param async whether the task should run on the main thread - */ - public void taskSoonMain(@Nonnull final Runnable runnable, boolean async) { - if (async) { - async(runnable); - } else { - task(runnable); - } - } - - - /** - * Run a task later on the main thread. + * Run a task later on the global tick thread. * * @param runnable the task to run * @param delay in ticks */ - public abstract void later(@Nonnull final Runnable runnable, final int delay); + public abstract void laterGlobal(@Nonnull final Runnable runnable, final int delay); /** * Run a task later asynchronously. @@ -247,34 +201,6 @@ public void taskSoonMain(@Nonnull final Runnable runnable, boolean async) { */ public abstract void cancel(final int task); - /** - * Break up a task and run it in fragments of 5ms.
- * - Each task will run on the main thread.
- * - * @param objects the list of objects to run the task for - * @param task the task to run on each object - * @param whenDone when the object task completes - */ - public void objectTask(Collection objects, final RunnableVal task, final Runnable whenDone) { - final Iterator iterator = objects.iterator(); - task(new Runnable() { - @Override - public void run() { - long start = System.currentTimeMillis(); - boolean hasNext; - while ((hasNext = iterator.hasNext()) && System.currentTimeMillis() - start < 5) { - task.value = iterator.next(); - task.run(); - } - if (!hasNext) { - later(whenDone, 1); - } else { - later(this, 1); - } - } - }); - } - /** * @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do. */ @@ -307,6 +233,7 @@ public void notify(AtomicBoolean running) { } } + @Deprecated public void taskWhenFree(@Nonnull Runnable run) { if (Fawe.isMainThread()) { run.run(); @@ -315,62 +242,14 @@ public void taskWhenFree(@Nonnull Runnable run) { } } - /** - * Run a task on the main thread when the TPS is high enough, and wait for execution to finish. - * - Useful if you need to access something from the Bukkit API from another thread
- * - Usually wait time is around 25ms
- */ - public T syncWhenFree(@Nonnull final RunnableVal function) { - if (Fawe.isMainThread()) { - function.run(); - return function.value; - } - try { - return Fawe.instance().getQueueHandler().sync((Supplier) function).get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } + public T syncAt(Supplier supplier, Location context) { + return syncAt(supplier, (World) context.getExtent(), context.getBlockX() >> 4, context.getBlockZ() >> 4); } - /** - * Run a task on the main thread when the TPS is high enough, and wait for execution to finish. - * - Useful if you need to access something from the Bukkit API from another thread
- * - Usually wait time is around 25ms
- */ - public T syncWhenFree(@Nonnull final Supplier supplier) { - if (Fawe.isMainThread()) { - return supplier.get(); - } - try { - return Fawe.instance().getQueueHandler().sync(supplier).get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } + public abstract T syncAt(Supplier supplier, World world, int chunkX, int chunkZ); - /** - * Quickly run a task on the main thread, and wait for execution to finish. - * - Useful if you need to access something from the Bukkit API from another thread
- * - Usually wait time is around 25ms - */ - public T sync(@Nonnull final RunnableVal function) { - return sync((Supplier) function); - } + public abstract T syncWith(Supplier supplier, Player context); - /** - * Quickly run a task on the main thread, and wait for execution to finish. - * - Useful if you need to access something from the Bukkit API from another thread
- * - Usually wait time is around 25ms
- */ - public T sync(final Supplier function) { - if (Fawe.isMainThread()) { - return function.get(); - } - try { - return Fawe.instance().getQueueHandler().sync(function).get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } + public abstract T syncGlobal(Supplier supplier); } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java index 61afe2dcd2..e332ad2311 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java @@ -4,15 +4,14 @@ import com.fastasyncworldedit.core.util.MathMan; import java.util.ArrayList; -import java.util.Map; +import java.util.List; import java.util.Optional; -public class FastRandomCollection extends RandomCollection { +public final class FastRandomCollection implements RandomCollection { private final T[] values; - private FastRandomCollection(T[] values, SimpleRandom random) { - super(random); + private FastRandomCollection(T[] values) { this.values = values; } @@ -22,16 +21,15 @@ private FastRandomCollection(T[] values, SimpleRandom random) { * {@code Optional} in any case. * * @param weights the weight of the values. - * @param random the random generator to use for this collection. * @param the value type. * @return an {@link Optional} containing the new collection if it could be created, {@link * Optional#empty()} otherwise. * @see RandomCollection for API usage. */ - public static Optional> create(Map weights, SimpleRandom random) { + public static Optional> create(List> weights) { int max = 0; int[] counts = new int[weights.size()]; - Double[] weightDoubles = weights.values().toArray(new Double[0]); + double[] weightDoubles = weights.stream().mapToDouble(Weighted::weight).toArray(); for (int i = 0; i < weightDoubles.length; i++) { int weight = (int) (weightDoubles[i] * 100); counts[i] = weight; @@ -47,21 +45,21 @@ public static Optional> create(Map weights, S return Optional.empty(); } ArrayList parsed = new ArrayList<>(); - for (Map.Entry entry : weights.entrySet()) { - int num = (int) (100 * entry.getValue()); + for (Weighted entry : weights) { + int num = (int) (100 * entry.weight()); for (int j = 0; j < num / gcd; j++) { - parsed.add(entry.getKey()); + parsed.add(entry.value()); } } @SuppressWarnings("unchecked") T[] values = (T[]) parsed.toArray(); - FastRandomCollection fastRandomCollection = new FastRandomCollection<>(values, random); + FastRandomCollection fastRandomCollection = new FastRandomCollection<>(values); return Optional.of(fastRandomCollection); } @Override - public T next(int x, int y, int z) { - return values[getRandom().nextInt(x, y, z, values.length)]; + public T next(final SimpleRandom random, int x, int y, int z) { + return values[random.nextInt(x, y, z, values.length)]; } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FlushingPartitionedCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FlushingPartitionedCache.java new file mode 100644 index 0000000000..7ff18bf2c3 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FlushingPartitionedCache.java @@ -0,0 +1,70 @@ +package com.fastasyncworldedit.core.util.collection; + +import org.jetbrains.annotations.ApiStatus; + +import java.io.Flushable; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * + * @param

the partition key type + * @param the element type + * @param the partition value type + * @since TODO + */ +@ApiStatus.Internal +public class FlushingPartitionedCache> implements Flushable { + + private final Map map; + private final Function partition; + private final Supplier constructor; + private final BiConsumer flusher; + private final int maxEntriesPerCollection; + + public FlushingPartitionedCache( + Function partition, + Supplier constructor, + BiConsumer flusher, + int maxEntriesPerCollection, + int maxCollections + ) { + this.partition = partition; + this.constructor = constructor; + this.flusher = flusher; + this.maxEntriesPerCollection = maxEntriesPerCollection; + this.map = new LinkedHashMap<>(16, 0.75f, true) { + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + final boolean remove = size() > maxCollections; + if (remove) { + flusher.accept(eldest.getKey(), eldest.getValue()); + } + return remove; + } + }; + } + + public void insert(E element) { + final P partition = this.partition.apply(element); + final C coll = this.map.computeIfAbsent(partition, k -> this.constructor.get()); + coll.add(element); + if (coll.size() > this.maxEntriesPerCollection) { + this.flusher.accept(partition, coll); + this.map.remove(partition); + } + } + + @Override + public void flush() { + for (final Map.Entry entry : this.map.entrySet()) { + this.flusher.accept(entry.getKey(), entry.getValue()); + } + this.map.clear(); + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java index 6214777e52..0988185587 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java @@ -2,49 +2,35 @@ import com.fastasyncworldedit.core.math.random.SimpleRandom; -import java.util.Map; +import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; /** * A RandomCollection holds multiple values that can be accessed by using - * {@link RandomCollection#next(int, int, int)}. The returned value is + * {@link RandomCollection#next(SimpleRandom, int, int, int)}. The returned value is * determined by a given {@link SimpleRandom} implementation. * * @param the type of values the collection holds. */ -public abstract class RandomCollection { - - private SimpleRandom random; - - protected RandomCollection(SimpleRandom random) { - this.random = random; - } +public sealed interface RandomCollection permits FastRandomCollection, SimpleRandomCollection { /** * Return a new RandomCollection. The implementation may differ depending on the * given arguments but there is no need to differ. * * @param weights the weighted map. - * @param random the random number generator. * @param the type the collection holds. * @return a RandomCollection using the given weights and the RNG. */ - public static RandomCollection of(Map weights, SimpleRandom random) { - checkNotNull(random); - return FastRandomCollection.create(weights, random) - .orElseGet(() -> new SimpleRandomCollection<>(weights, random)); + static RandomCollection of(List> weights) { + return FastRandomCollection.create(weights) + .orElseGet(() -> new SimpleRandomCollection<>(weights)); } - public void setRandom(SimpleRandom random) { - checkNotNull(random); - this.random = random; - } + T next(SimpleRandom random, int x, int y, int z); - public SimpleRandom getRandom() { - return random; - } - - public abstract T next(int x, int y, int z); + record Weighted(T value, double weight) { + } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java index 5b7dca98fc..8457a88be0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/SimpleRandomCollection.java @@ -2,41 +2,39 @@ import com.fastasyncworldedit.core.math.random.SimpleRandom; -import java.util.Map; +import java.util.List; import java.util.NavigableMap; import java.util.TreeMap; -public class SimpleRandomCollection extends RandomCollection { +public final class SimpleRandomCollection implements RandomCollection { - private final NavigableMap map = new TreeMap<>(); - private double total = 0; + private final NavigableMap map; + private final double total; /** * Create a {@link RandomCollection} from a weighted map and a RNG. - * It is recommended to use {@link RandomCollection#of(Map, SimpleRandom)} + * It is recommended to use {@link RandomCollection#of(List)} * instead of this constructor. * * @param weights the weighted map. - * @param random the random number generator. */ - public SimpleRandomCollection(Map weights, SimpleRandom random) { - super(random); - for (Map.Entry entry : weights.entrySet()) { - add(entry.getValue(), entry.getKey()); + public SimpleRandomCollection(List> weights) { + this.map = new TreeMap<>(); + double total = 0; + for (Weighted entry : weights) { + final double weight = entry.weight(); + if (weight <= 0) { + throw new IllegalArgumentException("Weights must be positive"); + } + total += weight; + this.map.put(total, entry.value()); } - } - - public void add(double weight, E result) { - if (weight <= 0) { - return; - } - total += weight; - map.put(total, result); + this.total = total; } @Override - public E next(int x, int y, int z) { - return map.ceilingEntry(getRandom().nextDouble(x, y, z, this.total)).getValue(); + public T next(final SimpleRandom random, int x, int y, int z) { + return map.ceilingEntry(random.nextDouble(x, y, z, this.total)).getValue(); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/progress/DefaultProgressTracker.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/progress/DefaultProgressTracker.java index 4aa846fc3d..7708d246be 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/progress/DefaultProgressTracker.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/progress/DefaultProgressTracker.java @@ -3,7 +3,6 @@ import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.util.StringMan; -import com.fastasyncworldedit.core.util.TaskManager; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; @@ -93,14 +92,13 @@ public void accept(ProgressType type, Integer amount) { } private void done() { - TaskManager.taskManager().task(this::doneTask); + doneTask(); } private long lastTick = 0; private void send() { - // Run on main thread - TaskManager.taskManager().task(this::sendTask); + sendTask(); } public void doneTask() { 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..3316e7d6b8 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 { @@ -38,42 +40,42 @@ public World getWorld() { @Override public void findFreePosition(Location searchPos) { - TaskManager.taskManager().sync(new RunnableVal() { + TaskManager.taskManager().task(new RunnableVal() { @Override public void run(Boolean value) { getBasePlayer().findFreePosition(searchPos); } - }); + }, searchPos); } @Override public void setOnGround(Location searchPos) { - TaskManager.taskManager().sync(new RunnableVal() { + TaskManager.taskManager().task(new RunnableVal() { @Override public void run(Boolean value) { getBasePlayer().setOnGround(searchPos); } - }); + }, searchPos); } @Override public void findFreePosition() { - TaskManager.taskManager().sync(new RunnableVal() { + TaskManager.taskManager().syncWith(new RunnableVal() { @Override public void run(Boolean value) { getBasePlayer().findFreePosition(); } - }); + }, this); } @Override public boolean ascendLevel() { - return TaskManager.taskManager().sync(() -> getBasePlayer().ascendLevel()); + return TaskManager.taskManager().syncWith(() -> getBasePlayer().ascendLevel(), this); } @Override public boolean descendLevel() { - return TaskManager.taskManager().sync(() -> getBasePlayer().descendLevel()); + return TaskManager.taskManager().syncWith(() -> getBasePlayer().descendLevel(), this); } @Override @@ -177,27 +179,21 @@ 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().syncWith(() -> super.getBlockTraceFace(range, useLastBlock, stopMask), this); } @Override public Location getSolidBlockTrace(int range) { - return TaskManager.taskManager().sync(() -> { + return TaskManager.taskManager().syncWith(() -> { TargetBlock tb = new TargetBlock(AsyncPlayer.this, range, 0.2D); return tb.getSolidTargetBlock(); - }); + }, this); } @Override @@ -207,7 +203,7 @@ public Direction getCardinalDirection() { @Override public boolean passThroughForwardWall(int range) { - return TaskManager.taskManager().sync(() -> { + return TaskManager.taskManager().syncWith(() -> { int searchDist = 0; TargetBlock hitBlox = new TargetBlock(AsyncPlayer.this, range, 0.2); Extent world = getLocation().getExtent(); @@ -252,7 +248,7 @@ public boolean passThroughForwardWall(int range) { } return false; - }); + }, this); } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java index 05616a4148..2546737aa4 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/wrappers/WorldWrapper.java @@ -253,18 +253,20 @@ public void dropItem(Vector3 position, BaseItemStack item) { @Override public void simulateBlockMine(BlockVector3 pt) { - TaskManager.taskManager().sync(new RunnableVal() { + TaskManager.taskManager().syncAt(new RunnableVal() { @Override public void run(Object value) { parent.simulateBlockMine(pt); } - }); + }, new Location(this, pt.toVector3())); } //FAWE start @Override public Collection getBlockDrops(final BlockVector3 position) { - return TaskManager.taskManager().sync(() -> parent.getBlockDrops(position)); + return TaskManager.taskManager().syncAt( + () -> parent.getBlockDrops(position), + new Location(this, position.toVector3())); } //FAWE end diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index ecf859896a..b6b39ac3f1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -3819,7 +3819,7 @@ public boolean regenerate(Region region, BiomeType biome, Long seed) { } if (containsAny) { changes++; - TaskManager.taskManager().sync(new RunnableVal() { + TaskManager.taskManager().syncGlobal(new RunnableVal() { @Override public void run(Object value) { regenerateChunk(cx, cz, biome, seed); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java index aca13bc46d..c11a57c8c1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/UtilityCommands.java @@ -50,19 +50,12 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.function.EntityFunction; -import com.sk89q.worldedit.function.block.BlockReplace; import com.sk89q.worldedit.function.mask.BlockTypeMask; -import com.sk89q.worldedit.function.mask.BoundedHeightMask; import com.sk89q.worldedit.function.mask.ExistingBlockMask; import com.sk89q.worldedit.function.mask.Mask; -import com.sk89q.worldedit.function.mask.MaskIntersection; -import com.sk89q.worldedit.function.mask.Masks; -import com.sk89q.worldedit.function.mask.RegionMask; import com.sk89q.worldedit.function.operation.Operations; import com.sk89q.worldedit.function.pattern.Pattern; -import com.sk89q.worldedit.function.visitor.DownwardVisitor; import com.sk89q.worldedit.function.visitor.EntityVisitor; -import com.sk89q.worldedit.function.visitor.RecursiveVisitor; import com.sk89q.worldedit.internal.annotation.Direction; import com.sk89q.worldedit.internal.annotation.VertHeight; import com.sk89q.worldedit.internal.expression.EvaluationException; @@ -70,10 +63,8 @@ import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector2; -import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.CylinderRegion; -import com.sk89q.worldedit.regions.EllipsoidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.formatting.component.SubtleFormat; import com.sk89q.worldedit.util.formatting.text.Component; @@ -89,7 +80,7 @@ import org.enginehub.piston.annotation.param.Switch; import javax.imageio.ImageIO; -import java.awt.RenderingHints; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -709,7 +700,16 @@ public int butcher( //FAWE start - run this sync int finalRadius = radius; - int killed = TaskManager.taskManager().sync(() -> killMatchingEntities(finalRadius, actor, flags::createFunction)); + int killed; + // TODO (folia) location context might be somewhere else actually + if (actor instanceof Player player) { + killed = TaskManager.taskManager().syncWith( + () -> killMatchingEntities(finalRadius, actor, flags::createFunction), + player + ); + } else { + killed = 0; + } //FAWE end actor.print(Caption.of( @@ -741,12 +741,25 @@ public int remove( } //FAWE start - run this sync - int removed = TaskManager.taskManager().sync(() -> killMatchingEntities(radius, actor, remover::createFunction)); + int removed = 0; + // TODO (folia) location context might be somewhere else actually + // removed = TaskManager.taskManager().sync(() -> killMatchingEntities(radius, actor, remover::createFunction)); //FAWE end actor.print(Caption.of("worldedit.remove.removed", TextComponent.of(removed))); return removed; } + private int killMatchingEntitiesSync(Integer radius, Actor actor, Supplier func) { + LocalSession session = we.getSessionManager().get(actor); + BlockVector3 center = session.getPlacementPosition(actor); + EditSession editSession = session.createEditSession(actor); + World world = editSession.getWorld(); + return TaskManager.taskManager().syncAt( + () -> killMatchingEntities(radius, actor, func), + world, center.getBlockX() >> 4, center.getBlockZ() >> 4 + ); + } + private int killMatchingEntities(Integer radius, Actor actor, Supplier func) throws IncompleteRegionException, MaxChangedBlocksException { List visitors = new ArrayList<>(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java index f2310a211f..0664f03620 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/EntityRemover.java @@ -148,7 +148,7 @@ public EntityFunction createFunction() { if (registryType != null) { if (type.matches(registryType)) { //FAWE start - Calling this async violates thread safety - TaskManager.taskManager().sync(entity::remove); + TaskManager.taskManager().syncAt(entity::remove, entity.getLocation()); //FAWE end return true; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java index 8337a7327f..2f1168a5d5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractNonPlayerActor.java @@ -20,7 +20,6 @@ package com.sk89q.worldedit.extension.platform; import com.fastasyncworldedit.core.internal.exception.FaweException; -import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.task.AsyncNotifyKeyedQueue; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.internal.cui.CUIEvent; @@ -109,7 +108,8 @@ public boolean runAction(Runnable ifFree, boolean checkFree, boolean async) { if (async) { asyncNotifyQueue.run(wrapped); } else { - TaskManager.taskManager().taskNow(wrapped, false); + // TODO (folia) + // TaskManager.taskManager().taskNow(wrapped, false); } return true; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java index ae4eb21443..eaa2b87831 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/AbstractPlayerActor.java @@ -23,7 +23,6 @@ import com.fastasyncworldedit.core.internal.exception.FaweException; import com.fastasyncworldedit.core.math.MutableBlockVector3; import com.fastasyncworldedit.core.regions.FaweMaskManager; -import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.WEManager; import com.fastasyncworldedit.core.util.task.AsyncNotifyKeyedQueue; import com.sk89q.worldedit.EditSession; @@ -677,7 +676,8 @@ public boolean runAction(Runnable ifFree, boolean checkFree, boolean async) { if (async) { asyncNotifyQueue.run(wrapped); } else { - TaskManager.taskManager().taskNow(wrapped, false); + // TODO (folia) + // TaskManager.taskManager().taskNow(wrapped, false); } return true; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 7b493cf985..c98dbfe1a3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -687,7 +687,11 @@ public void handleCommand(CommandEvent event) { Command cmd = optional.get(); PermissionCondition queued = cmd.getCondition().as(PermissionCondition.class).orElse(null); if (queued != null && !queued.isQueued()) { - TaskManager.taskManager().taskNow(() -> handleCommandOnCurrentThread(event), Fawe.isMainThread()); + if (actor instanceof Player player) { + TaskManager.taskManager().task(() -> handleCommandOnCurrentThread(event), player.getLocation()); + } else { + // TODO (folia) + } return; } else { actor.decline(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java index 88cf4fc6ff..1619502ee0 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/world/SurvivalModeExtent.java @@ -26,6 +26,8 @@ import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -99,14 +101,15 @@ public > boolean setBlock(BlockVector3 location, B Collection drops = world.getBlockDrops(location); boolean canSet = super.setBlock(location, block); if (canSet) { - TaskManager.taskManager().sync(new RunnableVal<>() { + final Vector3 position = location.toVector3(); + TaskManager.taskManager().syncAt(new RunnableVal<>() { @Override public void run(Object value) { for (BaseItemStack stack : drops) { - world.dropItem(location.toVector3(), stack); + world.dropItem(position, stack); } } - }); + }, new Location(world, position)); return true; } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java index fa3ef1dc45..c14abee738 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/entity/ExtentEntityCopy.java @@ -170,7 +170,7 @@ public boolean apply(Entity entity) throws WorldEditException { uuid ); } else { - TaskManager.taskManager().sync(entity::remove); + TaskManager.taskManager().syncAt(entity::remove, entity.getLocation()); //FAWE end } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java index c45629e5f0..7656a16567 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java @@ -27,10 +27,10 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; @@ -39,11 +39,10 @@ */ public class RandomPattern extends AbstractPattern { - //FAWE start - SimpleRandom > Random, LHS

> List + //FAWE start - SimpleRandom > Random, RandomCollection private final SimpleRandom random; - private Map weights = new LinkedHashMap<>(); + private final List> weights; private RandomCollection collection; - private LinkedHashSet patterns = new LinkedHashSet<>(); //FAWE end //FAWE start @@ -53,6 +52,7 @@ public RandomPattern() { public RandomPattern(SimpleRandom random) { this.random = random; + this.weights = new ArrayList<>(); } /** @@ -63,16 +63,14 @@ public RandomPattern(SimpleRandom random) { */ public RandomPattern(SimpleRandom random, RandomPattern parent) { this.random = random; - this.weights = parent.weights; - this.collection = RandomCollection.of(weights, random); - this.patterns = parent.patterns; + this.weights = new ArrayList<>(parent.weights); + this.collection = RandomCollection.of(weights); } - private RandomPattern(SimpleRandom random, Map weights) { + private RandomPattern(SimpleRandom random, List> weights) { this.random = random; this.weights = weights; - this.collection = RandomCollection.of(weights, random); - this.patterns = new LinkedHashSet<>(weights.keySet()); + this.collection = RandomCollection.of(weights); } //FAWE end @@ -87,18 +85,15 @@ private RandomPattern(SimpleRandom random, Map weights) { */ public void add(Pattern pattern, double chance) { checkNotNull(pattern); - //FAWE start - Double, weights, patterns and collection - Double existingWeight = weights.get(pattern); - if (existingWeight != null) { - chance += existingWeight; - } - weights.put(pattern, chance); - collection = RandomCollection.of(weights, random); - this.patterns.add(pattern); + //FAWE start - Double, weights, repeating patterns, and collection + this.weights.add(new RandomCollection.Weighted<>(pattern, chance)); + this.collection = RandomCollection.of(weights); } public Set getPatterns() { - return patterns; + return this.weights.stream() + .map(RandomCollection.Weighted::value) + .collect(Collectors.toSet()); } public RandomCollection getCollection() { @@ -107,18 +102,18 @@ public RandomCollection getCollection() { @Override public BaseBlock applyBlock(BlockVector3 position) { - return collection.next(position.getBlockX(), position.getBlockY(), position.getBlockZ()).applyBlock(position); + return collection.next(this.random, position.getBlockX(), position.getBlockY(), position.getBlockZ()).applyBlock(position); } @Override public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { - return collection.next(get.getBlockX(), get.getBlockY(), get.getBlockZ()).apply(extent, get, set); + return collection.next(this.random, get.getBlockX(), get.getBlockY(), get.getBlockZ()).apply(extent, get, set); } @Override public Pattern fork() { - final LinkedHashMap newWeights = new LinkedHashMap<>(); - this.weights.forEach((p, w) -> newWeights.put(p.fork(), w)); + List> newWeights = new ArrayList<>(); + this.weights.forEach((w) -> newWeights.add(new RandomCollection.Weighted<>(w.value().fork(), w.weight()))); return new RandomPattern(this.random, newWeights); }