From a64d24c6cfecc4bfaf78326be163dc82f6a4fc8f Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sat, 18 Nov 2023 15:02:02 +0000 Subject: [PATCH 001/114] feat: more informative BoundedHeightMask error message --- .../com/sk89q/worldedit/function/mask/BoundedHeightMask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java index 301f2104c..dfe1841d4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BoundedHeightMask.java @@ -41,7 +41,7 @@ public class BoundedHeightMask extends AbstractMask { * @param maxY the maximum Y (must be equal to or greater than minY) */ public BoundedHeightMask(int minY, int maxY) { - checkArgument(minY <= maxY, "minY <= maxY required"); + checkArgument(minY <= maxY, "minY <= maxY required. minY:" + minY + " and maxY:" + maxY + " were given."); this.minY = minY; this.maxY = maxY; } From c0a2eef648b3ca6067d41656b41d24895c07d6c0 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Tue, 21 Nov 2023 18:26:11 +0100 Subject: [PATCH 002/114] Reuse generateTree code across versions (#2482) --- .../v1_17_R1_2/PaperweightFaweAdapter.java | 92 ++++++------------- .../fawe/v1_18_R2/PaperweightFaweAdapter.java | 92 ++++++------------- .../fawe/v1_19_R3/PaperweightFaweAdapter.java | 90 ++++++------------ .../fawe/v1_20_R1/PaperweightFaweAdapter.java | 90 ++++++------------ .../fawe/v1_20_R2/PaperweightFaweAdapter.java | 90 ++++++------------ .../bukkit/adapter/FaweAdapter.java | 72 +++++++++++++++ 6 files changed, 214 insertions(+), 312 deletions(-) create mode 100644 worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java index 2029be6f9..ad6e6ae80 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java @@ -1,7 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; -import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; -import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; @@ -10,16 +9,13 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.NbtUtils; -import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag; @@ -39,7 +35,6 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; @@ -79,14 +74,12 @@ import net.minecraft.world.level.chunk.LevelChunkSection; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.TreeType; +import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.v1_17_R1.CraftChunk; import org.bukkit.craftbukkit.v1_17_R1.CraftServer; import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlockState; import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; @@ -110,8 +103,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; -public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements - IDelegateBukkitImplAdapter { +public final class PaperweightFaweAdapter extends FaweAdapter { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -235,11 +227,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BlockState getBlock(Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -255,12 +246,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BaseBlock getFullBlock(final Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -344,10 +334,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { - return new PaperweightFaweWorldNativeAccess( - this, - new WeakReference<>(((CraftWorld) world).getHandle()) - ); + return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world))); } @Override @@ -492,7 +479,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { - ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); + ServerLevel nmsWorld = getServerLevel(world); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); if (map != null && map.wasAccessibleSinceLastSave()) { boolean flag = false; @@ -530,7 +517,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements int internalId = BlockStateIdAccess.getBlockStateId(blockState); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( - ((CraftWorld) world).getHandle(), + getServerLevel(world), new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) ); } @@ -546,54 +533,33 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements } @Override - public boolean generateTree( - TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3, - org.bukkit.World bukkitWorld - ) { - TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType); - if (bukkitType == TreeType.CHORUS_PLANT) { - blockVector3 = blockVector3.add( - 0, - 1, - 0 - ); // bukkit skips the feature gen which does this offset normally, so we have to add it back - } - ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle(); - final BlockVector3 finalBlockVector = blockVector3; - // Sync to main thread to ensure no clashes occur - Map placed = TaskManager.taskManager().sync(() -> { - serverLevel.captureTreeGeneration = true; - serverLevel.captureBlockStates = true; - try { - if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) { - return null; - } - return ImmutableMap.copyOf(serverLevel.capturedBlockStates); - } finally { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); - } - }); - if (placed == null || placed.isEmpty()) { - return false; - } - for (CraftBlockState craftBlockState : placed.values()) { - if (craftBlockState == null || craftBlockState.getType() == Material.AIR) { - continue; - } - editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(), - BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData()) - ); - } - return true; + protected void preCaptureStates(final ServerLevel serverLevel) { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + } + + @Override + protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { + return new ArrayList<>(serverLevel.capturedBlockStates.values()); + } + + @Override + protected void postCaptureBlockStates(final ServerLevel serverLevel) { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + + @Override + protected ServerLevel getServerLevel(final World world) { + return ((CraftWorld) world).getHandle(); } @Override public List getEntities(org.bukkit.World world) { // Quickly add each entity to a list copy. List mcEntities = new ArrayList<>(); - ((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add); + getServerLevel(world).entityManager.getEntityGetter().getAll().forEach(mcEntities::add); List list = new ArrayList<>(); mcEntities.forEach((mcEnt) -> { diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java index 49125529d..8b96e2ea6 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightFaweAdapter.java @@ -1,7 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2; -import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; -import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; @@ -10,16 +9,13 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.NbtUtils; -import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.TileEntityBlock; import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag; @@ -39,7 +35,6 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; @@ -80,14 +75,12 @@ import net.minecraft.world.level.chunk.LevelChunkSection; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.TreeType; +import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.v1_18_R2.CraftChunk; import org.bukkit.craftbukkit.v1_18_R2.CraftServer; import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_18_R2.block.CraftBlockState; import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; @@ -111,8 +104,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; -public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements - IDelegateBukkitImplAdapter { +public final class PaperweightFaweAdapter extends FaweAdapter { private static final Logger LOGGER = LogManagerCompat.getLogger(); @@ -235,11 +227,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BlockState getBlock(Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -255,12 +246,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BaseBlock getFullBlock(final Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -337,10 +327,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { - return new PaperweightFaweWorldNativeAccess( - this, - new WeakReference<>(((CraftWorld) world).getHandle()) - ); + return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world))); } @Override @@ -485,7 +472,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { - ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); + ServerLevel nmsWorld = getServerLevel(world); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); if (map != null && map.wasAccessibleSinceLastSave()) { // PlayerChunk.d players = map.players; @@ -522,7 +509,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements int internalId = BlockStateIdAccess.getBlockStateId(blockState); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( - ((CraftWorld) world).getHandle(), + getServerLevel(world), new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) ); } @@ -538,54 +525,33 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements } @Override - public boolean generateTree( - TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3, - org.bukkit.World bukkitWorld - ) { - TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType); - if (bukkitType == TreeType.CHORUS_PLANT) { - blockVector3 = blockVector3.add( - 0, - 1, - 0 - ); // bukkit skips the feature gen which does this offset normally, so we have to add it back - } - ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle(); - final BlockVector3 finalBlockVector = blockVector3; - // Sync to main thread to ensure no clashes occur - Map placed = TaskManager.taskManager().sync(() -> { - serverLevel.captureTreeGeneration = true; - serverLevel.captureBlockStates = true; - try { - if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) { - return null; - } - return ImmutableMap.copyOf(serverLevel.capturedBlockStates); - } finally { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); - } - }); - if (placed == null || placed.isEmpty()) { - return false; - } - for (CraftBlockState craftBlockState : placed.values()) { - if (craftBlockState == null || craftBlockState.getType() == Material.AIR) { - continue; - } - editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(), - BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData()) - ); - } - return true; + protected void preCaptureStates(final ServerLevel serverLevel) { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + } + + @Override + protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { + return new ArrayList<>(serverLevel.capturedBlockStates.values()); + } + + @Override + protected void postCaptureBlockStates(final ServerLevel serverLevel) { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + + @Override + protected ServerLevel getServerLevel(final World world) { + return ((CraftWorld) world).getHandle(); } @Override public List getEntities(org.bukkit.World world) { // Quickly add each entity to a list copy. List mcEntities = new ArrayList<>(); - ((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add); + getServerLevel(world).entityManager.getEntityGetter().getAll().forEach(mcEntities::add); List list = new ArrayList<>(); mcEntities.forEach((mcEnt) -> { diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java index 82aba5fb6..82b13843b 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java @@ -1,7 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3; -import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; -import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; @@ -10,15 +9,12 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.NbtUtils; -import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R3.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag; @@ -38,7 +34,6 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; @@ -77,13 +72,11 @@ import net.minecraft.world.level.chunk.LevelChunk; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.TreeType; +import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.v1_19_R3.CraftServer; import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState; import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; @@ -111,8 +104,7 @@ import java.util.stream.Stream; import static net.minecraft.core.registries.Registries.BIOME; -public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements - IDelegateBukkitImplAdapter { +public final class PaperweightFaweAdapter extends FaweAdapter { private static final Logger LOGGER = LogManagerCompat.getLogger(); private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; @@ -244,11 +236,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BlockState getBlock(Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -264,12 +255,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BaseBlock getFullBlock(final Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -298,10 +288,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { - return new PaperweightFaweWorldNativeAccess( - this, - new WeakReference<>(((CraftWorld) world).getHandle()) - ); + return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world))); } @Override @@ -446,7 +433,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { - ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); + ServerLevel nmsWorld = getServerLevel(world); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; @@ -484,7 +471,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements int internalId = BlockStateIdAccess.getBlockStateId(blockState); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( - ((CraftWorld) world).getHandle(), + getServerLevel(world), new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) ); } @@ -501,47 +488,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements } @Override - public boolean generateTree( - TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3, - org.bukkit.World bukkitWorld - ) { - TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType); - if (bukkitType == TreeType.CHORUS_PLANT) { - blockVector3 = blockVector3.add( - 0, - 1, - 0 - ); // bukkit skips the feature gen which does this offset normally, so we have to add it back - } - ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle(); - final BlockVector3 finalBlockVector = blockVector3; - // Sync to main thread to ensure no clashes occur - Map placed = TaskManager.taskManager().sync(() -> { - serverLevel.captureTreeGeneration = true; - serverLevel.captureBlockStates = true; - try { - if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) { - return null; - } - return ImmutableMap.copyOf(serverLevel.capturedBlockStates); - } finally { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); - } - }); - if (placed == null || placed.isEmpty()) { - return false; - } - for (CraftBlockState craftBlockState : placed.values()) { - if (craftBlockState == null || craftBlockState.getType() == Material.AIR) { - continue; - } - editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(), - BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData()) - ); - } - return true; + protected void preCaptureStates(final ServerLevel serverLevel) { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + } + + @Override + protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { + return new ArrayList<>(serverLevel.capturedBlockStates.values()); + } + + @Override + protected void postCaptureBlockStates(final ServerLevel serverLevel) { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + + @Override + protected ServerLevel getServerLevel(final World world) { + return ((CraftWorld) world).getHandle(); } @Override 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 aad40c611..e13ff700f 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 @@ -1,7 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1; -import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; -import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; @@ -10,15 +9,12 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.NbtUtils; -import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1.PaperweightAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag; @@ -38,7 +34,6 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; @@ -77,13 +72,11 @@ import net.minecraft.world.level.chunk.LevelChunk; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.TreeType; +import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; @@ -111,8 +104,7 @@ import java.util.stream.Stream; import static net.minecraft.core.registries.Registries.BIOME; -public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements - IDelegateBukkitImplAdapter { +public final class PaperweightFaweAdapter extends FaweAdapter { private static final Logger LOGGER = LogManagerCompat.getLogger(); private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; @@ -244,11 +236,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BlockState getBlock(Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -264,12 +255,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BaseBlock getFullBlock(final Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -298,10 +288,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { - return new PaperweightFaweWorldNativeAccess( - this, - new WeakReference<>(((CraftWorld) world).getHandle()) - ); + return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world))); } @Override @@ -446,7 +433,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { - ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); + ServerLevel nmsWorld = getServerLevel(world); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; @@ -484,7 +471,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements int internalId = BlockStateIdAccess.getBlockStateId(blockState); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( - ((CraftWorld) world).getHandle(), + getServerLevel(world), new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) ); } @@ -501,47 +488,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements } @Override - public boolean generateTree( - TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3, - org.bukkit.World bukkitWorld - ) { - TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType); - if (bukkitType == TreeType.CHORUS_PLANT) { - blockVector3 = blockVector3.add( - 0, - 1, - 0 - ); // bukkit skips the feature gen which does this offset normally, so we have to add it back - } - ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle(); - final BlockVector3 finalBlockVector = blockVector3; - // Sync to main thread to ensure no clashes occur - Map placed = TaskManager.taskManager().sync(() -> { - serverLevel.captureTreeGeneration = true; - serverLevel.captureBlockStates = true; - try { - if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) { - return null; - } - return ImmutableMap.copyOf(serverLevel.capturedBlockStates); - } finally { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); - } - }); - if (placed == null || placed.isEmpty()) { - return false; - } - for (CraftBlockState craftBlockState : placed.values()) { - if (craftBlockState == null || craftBlockState.getType() == Material.AIR) { - continue; - } - editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(), - BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData()) - ); - } - return true; + protected void preCaptureStates(final ServerLevel serverLevel) { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + } + + @Override + protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { + return new ArrayList<>(serverLevel.capturedBlockStates.values()); + } + + @Override + protected void postCaptureBlockStates(final ServerLevel serverLevel) { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + + @Override + protected ServerLevel getServerLevel(final World world) { + return ((CraftWorld) world).getHandle(); } @Override diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java index 48bc935cb..851de9a0b 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightFaweAdapter.java @@ -1,7 +1,6 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2; -import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; -import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; import com.fastasyncworldedit.core.FaweCache; import com.fastasyncworldedit.core.entity.LazyBaseEntity; @@ -10,15 +9,12 @@ import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; import com.fastasyncworldedit.core.util.NbtUtils; -import com.fastasyncworldedit.core.util.TaskManager; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.BukkitWorld; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R2.regen.PaperweightRegen; @@ -37,7 +33,6 @@ import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.nbt.BinaryTag; import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; @@ -76,13 +71,11 @@ import net.minecraft.world.level.chunk.LevelChunk; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.TreeType; +import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.v1_20_R2.CraftServer; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R2.block.CraftBlockState; import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; @@ -110,8 +103,7 @@ import java.util.stream.Stream; import static net.minecraft.core.registries.Registries.BIOME; -public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements - IDelegateBukkitImplAdapter { +public final class PaperweightFaweAdapter extends FaweAdapter { private static final Logger LOGGER = LogManagerCompat.getLogger(); private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; @@ -247,11 +239,10 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BlockState getBlock(Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -267,12 +258,11 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements public BaseBlock getFullBlock(final Location location) { Preconditions.checkNotNull(location); - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); int x = location.getBlockX(); int y = location.getBlockY(); int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); + final ServerLevel handle = getServerLevel(location.getWorld()); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); @@ -301,10 +291,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { - return new PaperweightFaweWorldNativeAccess( - this, - new WeakReference<>(((CraftWorld) world).getHandle()) - ); + return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world))); } @Override @@ -449,7 +436,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements @Override public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { - ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); + ServerLevel nmsWorld = getServerLevel(world); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); if (map != null && wasAccessibleSinceLastSave(map)) { boolean flag = false; @@ -487,7 +474,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements int internalId = BlockStateIdAccess.getBlockStateId(blockState); net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); return blockState1.hasPostProcess( - ((CraftWorld) world).getHandle(), + getServerLevel(world), new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) ); } @@ -504,47 +491,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements } @Override - public boolean generateTree( - TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3, - org.bukkit.World bukkitWorld - ) { - TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType); - if (bukkitType == TreeType.CHORUS_PLANT) { - blockVector3 = blockVector3.add( - 0, - 1, - 0 - ); // bukkit skips the feature gen which does this offset normally, so we have to add it back - } - ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle(); - final BlockVector3 finalBlockVector = blockVector3; - // Sync to main thread to ensure no clashes occur - Map placed = TaskManager.taskManager().sync(() -> { - serverLevel.captureTreeGeneration = true; - serverLevel.captureBlockStates = true; - try { - if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) { - return null; - } - return ImmutableMap.copyOf(serverLevel.capturedBlockStates); - } finally { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); - } - }); - if (placed == null || placed.isEmpty()) { - return false; - } - for (CraftBlockState craftBlockState : placed.values()) { - if (craftBlockState == null || craftBlockState.getType() == Material.AIR) { - continue; - } - editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(), - BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData()) - ); - } - return true; + protected void preCaptureStates(final ServerLevel serverLevel) { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + } + + @Override + protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { + return new ArrayList<>(serverLevel.capturedBlockStates.values()); + } + + @Override + protected void postCaptureBlockStates(final ServerLevel serverLevel) { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + + @Override + protected ServerLevel getServerLevel(final World world) { + return ((CraftWorld) world).getHandle(); } @Override 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 new file mode 100644 index 000000000..e116daf17 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/adapter/FaweAdapter.java @@ -0,0 +1,72 @@ +package com.fastasyncworldedit.bukkit.adapter; + +import com.fastasyncworldedit.core.util.TaskManager; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.BukkitWorld; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.util.TreeGenerator; +import org.bukkit.Material; +import org.bukkit.TreeType; +import org.bukkit.World; +import org.bukkit.block.BlockState; + +import java.util.List; + +/** + * A base class for version-specific implementations of the BukkitImplAdapter + * + * @param the version-specific NBT tag type + * @param the version-specific ServerLevel type + */ +public abstract class FaweAdapter extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { + + @Override + public boolean generateTree( + final TreeGenerator.TreeType treeType, + final EditSession editSession, + BlockVector3 blockVector3, + final World world + ) { + TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType); + if (bukkitType == TreeType.CHORUS_PLANT) { + // bukkit skips the feature gen which does this offset normally, so we have to add it back + blockVector3 = blockVector3.add(BlockVector3.UNIT_Y); + } + BlockVector3 target = blockVector3; + SERVER_LEVEL serverLevel = getServerLevel(world); + List placed = TaskManager.taskManager().sync(() -> { + preCaptureStates(serverLevel); + try { + if (!world.generateTree(BukkitAdapter.adapt(world, target), bukkitType)) { + return null; + } + return getCapturedBlockStatesCopy(serverLevel); + } finally { + postCaptureBlockStates(serverLevel); + } + }); + + if (placed == null || placed.isEmpty()) { + return false; + } + for (BlockState blockState : placed) { + if (blockState == null || blockState.getType() == Material.AIR) { + continue; + } + editSession.setBlock(blockState.getX(), blockState.getY(), blockState.getZ(), + BukkitAdapter.adapt(blockState.getBlockData()) + ); + } + return true; + } + + protected abstract void preCaptureStates(SERVER_LEVEL serverLevel); + + protected abstract List getCapturedBlockStatesCopy(SERVER_LEVEL serverLevel); + + protected abstract void postCaptureBlockStates(SERVER_LEVEL serverLevel); + + protected abstract SERVER_LEVEL getServerLevel(World world); + +} From 6722d73c686c5009d23997f8d06bc8c2b3ad5b3b Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 25 Nov 2023 20:32:29 +0000 Subject: [PATCH 003/114] fix: fixed history rollback (#2367) - Also go ahead and clean up other parts of this, with more appropriate operators used - Fixes #2366 --- .../core/database/RollbackDatabase.java | 41 ++++++++------ .../core/history/DiskStorageHistory.java | 55 +++++++++++++++---- .../history/RollbackOptimizedHistory.java | 21 +++++++ .../history/changeset/AbstractChangeSet.java | 14 +++-- .../changeset/FaweStreamChangeSet.java | 12 ++-- .../worldedit/command/HistorySubCommands.java | 17 +++--- .../src/main/resources/lang/strings.json | 3 +- 7 files changed, 112 insertions(+), 51 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java index 3624e34c0..603e8061a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java @@ -159,15 +159,20 @@ public class RollbackDatabase extends AsyncNotifyQueue { Future future = call(() -> { try { int count = 0; - String stmtStr = ascending ? uuid == null ? "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND" + - " `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` , `id`" : - "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND" + - " `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC" : - uuid == null ? "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? " + - "AND `z1`<=? AND `y2`>=? AND `y1`<=? ORDER BY `time` DESC, `id` DESC" : - "SELECT * FROM`" + this.prefix + "edits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND" + - " `z2`>=? AND `z1`<=? AND `y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC"; - try (PreparedStatement stmt = connection.prepareStatement(stmtStr)) { + String stmtStr; + if (ascending) { + if (uuid == null) { + stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + + "`y2`>=? AND `y1`<=? ORDER BY `time` , `id`"; + } else { + stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + + "`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC"; + } + } else { + stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + + "`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC"; + } + try (PreparedStatement stmt = connection.prepareStatement(stmtStr.formatted(this.prefix))) { stmt.setInt(1, (int) (minTime / 1000)); stmt.setInt(2, pos1.getBlockX()); stmt.setInt(3, pos2.getBlockX()); @@ -193,20 +198,20 @@ public class RollbackDatabase extends AsyncNotifyQueue { if (delete && uuid != null) { try (PreparedStatement stmt = connection.prepareStatement("DELETE FROM`" + this.prefix + "edits` WHERE `player`=? AND `time`>? AND `x2`>=? AND `x1`<=? AND `y2`>=? AND `y1`<=? AND `z2`>=? AND `z1`<=?")) { - stmt.setInt(1, (int) (minTime / 1000)); - stmt.setInt(2, pos1.getBlockX()); - stmt.setInt(3, pos2.getBlockX()); - stmt.setInt(4, pos1.getBlockZ()); - stmt.setInt(5, pos2.getBlockZ()); - // Keep 128 offset for backwards-compatibility - stmt.setInt(6, pos1.getBlockY() - 128); - stmt.setInt(7, pos2.getBlockY() - 128); byte[] uuidBytes = ByteBuffer .allocate(16) .putLong(uuid.getMostSignificantBits()) .putLong(uuid.getLeastSignificantBits()) .array(); - stmt.setBytes(8, uuidBytes); + stmt.setBytes(1, uuidBytes); + stmt.setInt(2, (int) (minTime / 1000)); + stmt.setInt(3, pos1.getBlockX()); + stmt.setInt(4, pos2.getBlockX()); + stmt.setInt(5, pos1.getBlockZ()); + stmt.setInt(6, pos2.getBlockZ()); + // Keep 128 offset for backwards-compatibility + stmt.setInt(7, pos1.getBlockY() - 128); + stmt.setInt(8, pos2.getBlockY() - 128); } } return count; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java index 7d0bfe76e..eb2e3da59 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/DiskStorageHistory.java @@ -15,8 +15,10 @@ import com.sk89q.jnbt.NBTOutputStream; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.function.operation.ChangeSetExecutor; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.world.World; +import org.apache.logging.log4j.Logger; import java.io.File; import java.io.FileInputStream; @@ -35,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class DiskStorageHistory extends FaweStreamChangeSet { + private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Map> NEXT_INDEX = new ConcurrentHashMap<>(); private UUID uuid; @@ -141,9 +144,9 @@ public class DiskStorageHistory extends FaweStreamChangeSet { e.printStackTrace(); return; } - EditSession session = toEditSession(actor, regions); - session.setBlocks(this, ChangeSetExecutor.Type.UNDO); - deleteFiles(); + try (EditSession session = toEditSession(actor, regions)) { + session.setBlocks(this, ChangeSetExecutor.Type.UNDO); + } } public void undo(Actor actor) { @@ -371,9 +374,14 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!bdFile.exists()) { return null; } - FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile)); - readHeader(is); - return is; + try { + FaweInputStream is = MainUtil.getCompressedIS(new FileInputStream(bdFile)); + readHeader(is); + return is; + } catch (IOException e) { + LOGGER.error("Could not load block history file {}", bdFile); + throw e; + } } @Override @@ -381,7 +389,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!bioFile.exists()) { return null; } - return MainUtil.getCompressedIS(new FileInputStream(bioFile)); + try { + return MainUtil.getCompressedIS(new FileInputStream(bioFile)); + } catch (IOException e) { + LOGGER.error("Could not load biome history file {}", bdFile); + throw e; + } } @Override @@ -389,7 +402,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!enttFile.exists()) { return null; } - return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile))); + try { + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(enttFile))); + } catch (IOException e) { + LOGGER.error("Could not load entity create history file {}", bdFile); + throw e; + } } @Override @@ -397,7 +415,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!entfFile.exists()) { return null; } - return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile))); + try { + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(entfFile))); + } catch (IOException e) { + LOGGER.error("Could not load entity remove history file {}", bdFile); + throw e; + } } @Override @@ -405,7 +428,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!nbttFile.exists()) { return null; } - return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile))); + try { + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbttFile))); + } catch (IOException e) { + LOGGER.error("Could not load tile create history file {}", bdFile); + throw e; + } } @Override @@ -413,7 +441,12 @@ public class DiskStorageHistory extends FaweStreamChangeSet { if (!nbtfFile.exists()) { return null; } - return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile))); + try { + return new NBTInputStream(MainUtil.getCompressedIS(new FileInputStream(nbtfFile))); + } catch (IOException e) { + LOGGER.error("Could not load tile remove history file {}", bdFile); + throw e; + } } @Override diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/RollbackOptimizedHistory.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/RollbackOptimizedHistory.java index 6e7e4068b..1f01f99de 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/RollbackOptimizedHistory.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/RollbackOptimizedHistory.java @@ -7,6 +7,7 @@ import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BiomeType; import org.apache.logging.log4j.Logger; import java.io.IOException; @@ -125,6 +126,26 @@ public class RollbackOptimizedHistory extends DiskStorageHistory { } } + @Override + public void addBiomeChange(int x, int y, int z, BiomeType from, BiomeType to) { + super.addBiomeChange(x, y, z, from, to); + if (x < minX) { + minX = x; + } else if (x > maxX) { + maxX = x; + } + if (y < minY) { + minY = y; + } else if (y > maxY) { + maxY = y; + } + if (z < minZ) { + minZ = z; + } else if (z > maxZ) { + maxZ = z; + } + } + @Override public void writeHeader(OutputStream os, int x, int y, int z) throws IOException { minX = x; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java index c6a2d9bb0..4bbe1a109 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/history/changeset/AbstractChangeSet.java @@ -257,12 +257,14 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor { } public EditSession toEditSession(Actor actor, Region[] regions) { - EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(getWorld()).actor(actor). - fastMode(false).checkMemory(false).changeSet(this).limitUnlimited(); - if (regions != null) { - builder.allowedRegions(regions); - } else { - builder.allowedRegionsEverywhere(); + EditSessionBuilder builder = WorldEdit.getInstance().newEditSessionBuilder().world(world) + .checkMemory(false) + .changeSetNull() + .fastMode(false) + .limitUnprocessed(actor) + .actor(actor); + if (!actor.getLimit().RESTRICT_HISTORY_TO_REGIONS) { + builder = builder.allowedRegionsEverywhere(); } EditSession editSession = builder.build(); editSession.setSize(1); 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 c2ae362ae..71232a31c 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 @@ -167,7 +167,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { @Override public int readX(FaweInputStream in) throws IOException { in.readFully(buffer); - return lx = lx + ((((buffer[1] & 0xFF) + ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20); + return lx = lx + ((((buffer[1] & 0xFF) | ((MathMan.unpair16x(buffer[3])) << 8)) << 20) >> 20); } @Override @@ -177,7 +177,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { @Override public int readZ(FaweInputStream in) throws IOException { - return lz = lz + ((((buffer[2] & 0xFF) + ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20); + return lz = lz + ((((buffer[2] & 0xFF) | ((MathMan.unpair16y(buffer[3])) << 8)) << 20) >> 20); } }; } else { @@ -203,17 +203,17 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { @Override public int readX(FaweInputStream is) throws IOException { is.readFully(buffer); - return lx = (lx + (buffer[0] & 0xFF) + (buffer[1] << 8)); + 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)); + 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)); + return lz = lz + ((buffer[2] & 0xFF) | (buffer[3]) << 8); } }; } @@ -353,7 +353,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { os.write((byte) (z)); // only need to store biomes in the 4x4x4 chunks so only need one byte for y still (signed byte -128 -> 127) // means -512 -> 508. Add 128 to avoid negative value casting. - os.write((byte) (y + 128)); + os.write((byte) (y + 32)); os.writeVarInt(from.getInternalId()); os.writeVarInt(to.getInternalId()); } catch (IOException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java index 6b30e9e7e..08f48b265 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java @@ -13,20 +13,22 @@ import com.fastasyncworldedit.core.util.MainUtil; import com.fastasyncworldedit.core.util.StringMan; import com.google.common.base.Function; import com.google.common.collect.Lists; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.EditSessionBuilder; import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.argument.Arguments; import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; -import com.sk89q.worldedit.command.util.annotation.AllowedRegion; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Time; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.function.operation.ChangeSetExecutor; import com.sk89q.worldedit.history.changeset.ChangeSet; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Identifiable; @@ -80,7 +82,6 @@ public class HistorySubCommands { @Confirm public synchronized void rerun( Player player, World world, RollbackDatabase database, - @AllowedRegion Region[] allowedRegions, @ArgFlag(name = 'u', desc = "String user", def = "me") UUID other, @ArgFlag(name = 'r', def = "0", desc = "radius") @@ -90,7 +91,7 @@ public class HistorySubCommands { @Time long timeDiff ) throws WorldEditException { - rollback(player, world, database, allowedRegions, other, radius, timeDiff, true); + rollback(player, world, database, other, radius, timeDiff, true); } @Command( @@ -102,7 +103,6 @@ public class HistorySubCommands { @Confirm public synchronized void rollback( Player player, World world, RollbackDatabase database, - @AllowedRegion Region[] allowedRegions, @ArgFlag(name = 'u', desc = "String user", def = "") UUID other, @ArgFlag(name = 'r', def = "0", desc = "radius") @@ -149,9 +149,9 @@ public class HistorySubCommands { count++; RollbackOptimizedHistory edit = supplier.get(); if (restore) { - edit.redo(player, allowedRegions); + edit.redo(player); } else { - edit.undo(player, allowedRegions); + edit.undo(player); } String path = edit.getWorld().getName() + "/" + finalOther + "-" + edit.getIndex(); player.print(Caption.of("fawe.worldedit.rollback.rollback.element", path)); @@ -201,8 +201,7 @@ public class HistorySubCommands { .at(summary.maxX, world.getMaxY(), summary.maxZ) ); rollback.setTime(historyFile.lastModified()); - RollbackDatabase db = DBHandler.dbHandler() - .getDatabase(world); + RollbackDatabase db = DBHandler.dbHandler().getDatabase(world); db.logEdit(rollback); actor.print(TextComponent.of("Logging: " + historyFile)); } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 5e922cdae..d702ead53 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -54,7 +54,8 @@ "fawe.worldedit.brush.brush.source.mask": "Brush source mask set", "fawe.worldedit.brush.brush.transform.disabled": "Brush transform disabled", "fawe.worldedit.brush.brush.transform": "Brush transform set", - "fawe.worldedit.rollback.rollback.element": "Undoing {0}", + "fawe.worldedit.rollback.rollingback.index": "Undoing {0} ...", + "fawe.worldedit.rollback.rollback.element": "{0} undone.", "fawe.worldedit.tool.tool.inspect": "Inspect tool bound to {0}.", "fawe.worldedit.tool.tool.inspect.info": "{0} changed {1} to {2} {3} ago", "fawe.worldedit.tool.tool.inspect.info.footer": "Total: {0} changes", From 53ec728f97be0310ea6d3333f042f8fe2433bd37 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 25 Nov 2023 22:43:22 +0100 Subject: [PATCH 004/114] Update paperweight adapters --- worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts index 2f525f245..70c495b7b 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - the().paperDevBundle("1.20.2-R0.1-20231029.153906-63") + the().paperDevBundle("1.20.2-R0.1-20231125.095734-103") compileOnly(libs.paperlib) } From d1798b7408aae327c9185dce16fd729e952b0311 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sun, 26 Nov 2023 12:28:47 +0000 Subject: [PATCH 005/114] chore: remove bad range annotations --- .../main/java/com/fastasyncworldedit/core/queue/Filter.java | 5 ++--- .../main/java/com/fastasyncworldedit/core/queue/IChunk.java | 3 --- .../java/com/fastasyncworldedit/core/queue/IChunkCache.java | 4 +--- .../java/com/fastasyncworldedit/core/queue/IQueueExtent.java | 5 ++--- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java index 19fa1293a..366faa8c5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/Filter.java @@ -2,7 +2,6 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.filter.block.FilterBlock; import com.sk89q.worldedit.regions.Region; -import org.jetbrains.annotations.Range; import javax.annotation.Nullable; @@ -18,8 +17,8 @@ public interface Filter { * @param chunkZ the z coordinate in the chunk */ default boolean appliesChunk( - @Range(from = 0, to = 15) int chunkX, - @Range(from = 0, to = 15) int chunkZ + int chunkX, + int chunkZ ) { return true; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java index 126a5cd13..0acfa4a22 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java @@ -2,7 +2,6 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.sk89q.worldedit.regions.Region; -import org.jetbrains.annotations.Range; import javax.annotation.Nullable; @@ -25,7 +24,6 @@ public interface IChunk extends Trimable, IChunkGet, IChunkSet { * * @return the x coordinate of the chunk */ - @Range(from = 0, to = 15) int getX(); /** @@ -33,7 +31,6 @@ public interface IChunk extends Trimable, IChunkGet, IChunkSet { * * @return the z coordinate of the chunk */ - @Range(from = 0, to = 15) int getZ(); /** diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkCache.java index ce2761390..8a704e9cb 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunkCache.java @@ -1,14 +1,12 @@ package com.fastasyncworldedit.core.queue; -import org.jetbrains.annotations.Range; - /** * IGetBlocks may be cached by the WorldChunkCache so that it can be used between multiple * IQueueExtents - avoids conversion between a palette and raw data on every block get */ public interface IChunkCache extends Trimable { - T get(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); + T get(int chunkX, int chunkZ); @Override default boolean trim(boolean aggressive) { diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java index 01503ad55..104167d30 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java @@ -7,7 +7,6 @@ import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; -import org.jetbrains.annotations.Range; import javax.annotation.Nullable; import java.io.Flushable; @@ -51,12 +50,12 @@ public interface IQueueExtent extends Flushable, Trimable, ICh * Get the cached get object. This is faster than getting the object using NMS and allows for * wrapping. */ - IChunkGet getCachedGet(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); + IChunkGet getCachedGet(int chunkX, int chunkZ); /** * Get the cached chunk set object. */ - IChunkSet getCachedSet(@Range(from = 0, to = 15) int chunkX, @Range(from = 0, to = 15) int chunkZ); + IChunkSet getCachedSet(int chunkX, int chunkZ); /** * Submit the chunk so that it's changes are applied to the world From b754bc01e25c2e2fdbc9b012b0f264211a800fcb Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 26 Nov 2023 13:35:29 +0100 Subject: [PATCH 006/114] Drop support for 1.16.5 and 1.17 (#2497) Drop support for 1.16.5 --- settings.gradle.kts | 2 +- .../adapters/adapter-legacy/build.gradle.kts | 7 ------- .../resources/fastasyncworldedit-adapters.jar | Bin 434371 -> 0 bytes worldedit-bukkit/build.gradle.kts | 2 +- .../fastasyncworldedit/bukkit/FaweBukkit.java | 8 -------- .../bukkit/util/MinecraftVersion.java | 1 - 6 files changed, 2 insertions(+), 18 deletions(-) delete mode 100644 worldedit-bukkit/adapters/adapter-legacy/build.gradle.kts delete mode 100644 worldedit-bukkit/adapters/adapter-legacy/src/main/resources/fastasyncworldedit-adapters.jar diff --git a/settings.gradle.kts b/settings.gradle.kts index 4c6995e2e..d8b8012cf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit" include("worldedit-libs") -listOf("legacy", "1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2").forEach { +listOf("1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2").forEach { include("worldedit-bukkit:adapters:adapter-$it") } diff --git a/worldedit-bukkit/adapters/adapter-legacy/build.gradle.kts b/worldedit-bukkit/adapters/adapter-legacy/build.gradle.kts deleted file mode 100644 index 822c1f70b..000000000 --- a/worldedit-bukkit/adapters/adapter-legacy/build.gradle.kts +++ /dev/null @@ -1,7 +0,0 @@ -plugins { - base -} - -artifacts { - add("default", file("./src/main/resources/fastasyncworldedit-adapters.jar")) -} diff --git a/worldedit-bukkit/adapters/adapter-legacy/src/main/resources/fastasyncworldedit-adapters.jar b/worldedit-bukkit/adapters/adapter-legacy/src/main/resources/fastasyncworldedit-adapters.jar deleted file mode 100644 index 9000989ef76582a02074889aaf55ad7f3645347d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 434371 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%0AQ1#F?4!9 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 256) with 1.18 world heights. Are you sure?"); } - - if (version.isEqualOrLowerThan(MinecraftVersion.ONE_DOT_SIXTEEN_EOL)) { - LOGGER.warn("You are running Minecraft 1.16.5. This version has been released over two years ago (January 2021)."); - LOGGER.warn("FastAsyncWorldEdit will stop operating on this version in the near future."); - LOGGER.warn("Neither Mojang, nor Spigot or other software vendors support this version anymore." + - "Please update your server to a newer version of Minecraft (1.20+) to continue receiving updates and " + - "support."); - } } @Override diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/MinecraftVersion.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/MinecraftVersion.java index f719d430d..08ee52de6 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/MinecraftVersion.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/util/MinecraftVersion.java @@ -13,7 +13,6 @@ import java.util.regex.Pattern; public class MinecraftVersion implements Comparable { public static final MinecraftVersion NETHER = new MinecraftVersion(1, 16); - public static final MinecraftVersion ONE_DOT_SIXTEEN_EOL = new MinecraftVersion(1, 16, 5); public static final MinecraftVersion CAVES_17 = new MinecraftVersion(1, 17); public static final MinecraftVersion CAVES_18 = new MinecraftVersion(1, 18); private static MinecraftVersion current = null; From f6aa02fe50c485c747edb3a98cd500cc3ccdee19 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:48:16 +0100 Subject: [PATCH 007/114] Update actions/setup-java action to v4 (#2508) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-pr.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/upload-release-assets.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 98319f7d2..394cb9f56 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -13,7 +13,7 @@ jobs: - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: temurin cache: gradle diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6cda7331f..571658fb4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: temurin cache: gradle diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a0cd830bd..8dd831550 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: temurin cache: gradle diff --git a/.github/workflows/upload-release-assets.yml b/.github/workflows/upload-release-assets.yml index cb1d0a9d1..8f27396ab 100644 --- a/.github/workflows/upload-release-assets.yml +++ b/.github/workflows/upload-release-assets.yml @@ -11,7 +11,7 @@ jobs: - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: temurin cache: gradle From 980caba97c45ef2e241fd4e96795385154c49299 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:48:24 +0100 Subject: [PATCH 008/114] Update dependency org.mockito:mockito-core to v5.8.0 (#2505) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- worldedit-sponge/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 397834962..8af2ec263 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,7 +46,7 @@ text = "3.0.4" piston = "0.5.7" # Tests -mockito = "5.6.0" +mockito = "5.8.0" # Gradle plugins pluginyml = "0.6.0" diff --git a/worldedit-sponge/build.gradle.kts b/worldedit-sponge/build.gradle.kts index f1aebbeba..8d3ba1539 100644 --- a/worldedit-sponge/build.gradle.kts +++ b/worldedit-sponge/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { }) api("org.apache.logging.log4j:log4j-api") api("org.bstats:bstats-sponge:1.7") - testImplementation("org.mockito:mockito-core:5.6.0") + testImplementation("org.mockito:mockito-core:5.8.0") } <<<<<<< HEAD From 785ac35fd4c5b842752797f415728c7484426d0d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:48:33 +0100 Subject: [PATCH 009/114] Update AButler/upload-release-assets action to v3 (#2507) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/upload-release-assets.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/upload-release-assets.yml b/.github/workflows/upload-release-assets.yml index 8f27396ab..7315f1d6c 100644 --- a/.github/workflows/upload-release-assets.yml +++ b/.github/workflows/upload-release-assets.yml @@ -19,7 +19,7 @@ jobs: - name: Clean Build run: ./gradlew clean build --no-daemon - name: Upload Release Assets - uses: AButler/upload-release-assets@v2.0 + uses: AButler/upload-release-assets@v3.0 with: files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar' repo-token: ${{ secrets.GITHUB_TOKEN }} From d7a7803a8535a9ac7138a56f6bf7245f1d0a2c76 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:48:43 +0100 Subject: [PATCH 010/114] Update plotsquared to v7.2.0 (#2506) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8af2ec263..b3865b3d9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" towny = "0.100.0.1" -plotsquared = "7.1.0" +plotsquared = "7.2.0" # Third party bstats = "3.0.2" From 2247dca23b0c7d1598fc497cc282816c563a5e90 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:49:05 +0100 Subject: [PATCH 011/114] Update dependency gradle to v8.5 (#2504) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 63721 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49b765f8051ef9d0a6055ff8e46073d8..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 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 literal 63721 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鯊%1Et zwk~hMkhq;MOw^Q5`7oC{CUUyTw9x>^%*FHx^qJw(LB+E0WBX@{Ghw;)6aA-KyYg8p z7XDveQOpEr;B4je@2~usI5BlFadedX^ma{b{ypd|RNYqo#~d*mj&y`^iojR}s%~vF z(H!u`yx68D1Tj(3(m;Q+Ma}s2n#;O~bcB1`lYk%Irx60&-nWIUBr2x&@}@76+*zJ5 ze&4?q8?m%L9c6h=J$WBzbiTf1Z-0Eb5$IZs>lvm$>1n_Mezp*qw_pr8<8$6f)5f<@ zyV#tzMCs51nTv_5ca`x`yfE5YA^*%O_H?;tWYdM_kHPubA%vy47i=9>Bq) zRQ&0UwLQHeswmB1yP)+BiR;S+Vc-5TX84KUA;8VY9}yEj0eESSO`7HQ4lO z4(CyA8y1G7_C;6kd4U3K-aNOK!sHE}KL_-^EDl(vB42P$2Km7$WGqNy=%fqB+ zSLdrlcbEH=T@W8V4(TgoXZ*G1_aq$K^@ek=TVhoKRjw;HyI&coln|uRr5mMOy2GXP zwr*F^Y|!Sjr2YQXX(Fp^*`Wk905K%$bd03R4(igl0&7IIm*#f`A!DCarW9$h$z`kYk9MjjqN&5-DsH@8xh63!fTNPxWsFQhNv z#|3RjnP$Thdb#Ys7M+v|>AHm0BVTw)EH}>x@_f4zca&3tXJhTZ8pO}aN?(dHo)44Z z_5j+YP=jMlFqwvf3lq!57-SAuRV2_gJ*wsR_!Y4Z(trO}0wmB9%f#jNDHPdQGHFR; zZXzS-$`;7DQ5vF~oSgP3bNV$6Z(rwo6W(U07b1n3UHqml>{=6&-4PALATsH@Bh^W? z)ob%oAPaiw{?9HfMzpGb)@Kys^J$CN{uf*HX?)z=g`J(uK1YO^8~s1(ZIbG%Et(|q z$D@_QqltVZu9Py4R0Ld8!U|#`5~^M=b>fnHthzKBRr=i+w@0Vr^l|W;=zFT#PJ?*a zbC}G#It}rQP^Ait^W&aa6B;+0gNvz4cWUMzpv(1gvfw-X4xJ2Sv;mt;zb2Tsn|kSS zo*U9N?I{=-;a-OybL4r;PolCfiaL=y@o9{%`>+&FI#D^uy#>)R@b^1ue&AKKwuI*` zx%+6r48EIX6nF4o;>)zhV_8(IEX})NGU6Vs(yslrx{5fII}o3SMHW7wGtK9oIO4OM&@@ECtXSICLcPXoS|{;=_yj>hh*%hP27yZwOmj4&Lh z*Nd@OMkd!aKReoqNOkp5cW*lC)&C$P?+H3*%8)6HcpBg&IhGP^77XPZpc%WKYLX$T zsSQ$|ntaVVOoRat$6lvZO(G-QM5s#N4j*|N_;8cc2v_k4n6zx9c1L4JL*83F-C1Cn zaJhd;>rHXB%%ZN=3_o3&Qd2YOxrK~&?1=UuN9QhL$~OY-Qyg&})#ez*8NpQW_*a&kD&ANjedxT0Ar z<6r{eaVz3`d~+N~vkMaV8{F?RBVemN(jD@S8qO~L{rUw#=2a$V(7rLE+kGUZ<%pdr z?$DP|Vg#gZ9S}w((O2NbxzQ^zTot=89!0^~hE{|c9q1hVzv0?YC5s42Yx($;hAp*E zyoGuRyphQY{Q2ee0Xx`1&lv(l-SeC$NEyS~8iil3_aNlnqF_G|;zt#F%1;J)jnPT& z@iU0S;wHJ2$f!juqEzPZeZkjcQ+Pa@eERSLKsWf=`{R@yv7AuRh&ALRTAy z8=g&nxsSJCe!QLchJ=}6|LshnXIK)SNd zRkJNiqHwKK{SO;N5m5wdL&qK`v|d?5<4!(FAsDxR>Ky#0#t$8XCMptvNo?|SY?d8b z`*8dVBlXTUanlh6n)!EHf2&PDG8sXNAt6~u-_1EjPI1|<=33T8 zEnA00E!`4Ave0d&VVh0e>)Dc}=FfAFxpsC1u9ATfQ`-Cu;mhc8Z>2;uyXtqpLb7(P zd2F9<3cXS} znMg?{&8_YFTGRQZEPU-XPq55%51}RJpw@LO_|)CFAt62-_!u_Uq$csc+7|3+TV_!h z+2a7Yh^5AA{q^m|=KSJL+w-EWDBc&I_I1vOr^}P8i?cKMhGy$CP0XKrQzCheG$}G# zuglf8*PAFO8%xop7KSwI8||liTaQ9NCAFarr~psQt)g*pC@9bORZ>m`_GA`_K@~&% zijH0z;T$fd;-Liw8%EKZas>BH8nYTqsK7F;>>@YsE=Rqo?_8}UO-S#|6~CAW0Oz1} z3F(1=+#wrBJh4H)9jTQ_$~@#9|Bc1Pd3rAIA_&vOpvvbgDJOM(yNPhJJq2%PCcMaI zrbe~toYzvkZYQ{ea(Wiyu#4WB#RRN%bMe=SOk!CbJZv^m?Flo5p{W8|0i3`hI3Np# zvCZqY%o258CI=SGb+A3yJe~JH^i{uU`#U#fvSC~rWTq+K`E%J@ zasU07&pB6A4w3b?d?q}2=0rA#SA7D`X+zg@&zm^iA*HVi z009#PUH<%lk4z~p^l0S{lCJk1Uxi=F4e_DwlfHA`X`rv(|JqWKAA5nH+u4Da+E_p+ zVmH@lg^n4ixs~*@gm_dgQ&eDmE1mnw5wBz9Yg?QdZwF|an67Xd*x!He)Gc8&2!urh z4_uXzbYz-aX)X1>&iUjGp;P1u8&7TID0bTH-jCL&Xk8b&;;6p2op_=y^m@Nq*0{#o!!A;wNAFG@0%Z9rHo zcJs?Th>Ny6+hI`+1XoU*ED$Yf@9f91m9Y=#N(HJP^Y@ZEYR6I?oM{>&Wq4|v0IB(p zqX#Z<_3X(&{H+{3Tr|sFy}~=bv+l=P;|sBz$wk-n^R`G3p0(p>p=5ahpaD7>r|>pm zv;V`_IR@tvZreIuv2EM7ZQHhO+qUgw#kOs%*ekY^n|=1#x9&c;Ro&I~{rG-#_3ZB1 z?|9}IFdbP}^DneP*T-JaoYHt~r@EfvnPE5EKUwIxjPbsr$% zfWW83pgWST7*B(o=kmo)74$8UU)v0{@4DI+ci&%=#90}!CZz|rnH+Mz=HN~97G3~@ z;v5(9_2%eca(9iu@J@aqaMS6*$TMw!S>H(b z4(*B!|H|8&EuB%mITr~O?vVEf%(Gr)6E=>H~1VR z&1YOXluJSG1!?TnT)_*YmJ*o_Q@om~(GdrhI{$Fsx_zrkupc#y{DK1WOUR>tk>ZE) ziOLoBkhZZ?0Uf}cm>GsA>Rd6V8@JF)J*EQlQ<=JD@m<)hyElXR0`pTku*3MU`HJn| zIf7$)RlK^pW-$87U;431;Ye4Ie+l~_B3*bH1>*yKzn23cH0u(i5pXV! z4K?{3oF7ZavmmtTq((wtml)m6i)8X6ot_mrE-QJCW}Yn!(3~aUHYG=^fA<^~`e3yc z-NWTb{gR;DOUcK#zPbN^D*e=2eR^_!(!RKkiwMW@@yYtEoOp4XjOGgzi`;=8 zi3`Ccw1%L*y(FDj=C7Ro-V?q)-%p?Ob2ZElu`eZ99n14-ZkEV#y5C+{Pq87Gu3&>g zFy~Wk7^6v*)4pF3@F@rE__k3ikx(hzN3@e*^0=KNA6|jC^B5nf(XaoQaZN?Xi}Rn3 z$8&m*KmWvPaUQ(V<#J+S&zO|8P-#!f%7G+n_%sXp9=J%Z4&9OkWXeuZN}ssgQ#Tcj z8p6ErJQJWZ+fXLCco=RN8D{W%+*kko*2-LEb))xcHwNl~Xmir>kmAxW?eW50Osw3# zki8Fl$#fvw*7rqd?%E?}ZX4`c5-R&w!Y0#EBbelVXSng+kUfeUiqofPehl}$ormli zg%r)}?%=?_pHb9`Cq9Z|B`L8b>(!+8HSX?`5+5mm81AFXfnAt1*R3F z%b2RPIacKAddx%JfQ8l{3U|vK@W7KB$CdLqn@wP^?azRks@x8z59#$Q*7q!KilY-P zHUbs(IFYRGG1{~@RF;Lqyho$~7^hNC`NL3kn^Td%A7dRgr_&`2k=t+}D-o9&C!y^? z6MsQ=tc3g0xkK(O%DzR9nbNB(r@L;1zQrs8mzx&4dz}?3KNYozOW5;=w18U6$G4U2 z#2^qRLT*Mo4bV1Oeo1PKQ2WQS2Y-hv&S|C7`xh6=Pj7MNLC5K-zokZ67S)C;(F0Dd zloDK2_o1$Fmza>EMj3X9je7e%Q`$39Dk~GoOj89-6q9|_WJlSl!!+*{R=tGp z8u|MuSwm^t7K^nUe+^0G3dkGZr3@(X+TL5eah)K^Tn zXEtHmR9UIaEYgD5Nhh(s*fcG_lh-mfy5iUF3xxpRZ0q3nZ=1qAtUa?(LnT9I&~uxX z`pV?+=|-Gl(kz?w!zIieXT}o}7@`QO>;u$Z!QB${a08_bW0_o@&9cjJUXzVyNGCm8 zm=W+$H!;_Kzp6WQqxUI;JlPY&`V}9C$8HZ^m?NvI*JT@~BM=()T()Ii#+*$y@lTZBkmMMda>7s#O(1YZR+zTG@&}!EXFG{ zEWPSDI5bFi;NT>Yj*FjH((=oe%t%xYmE~AGaOc4#9K_XsVpl<4SP@E!TgC0qpe1oi zNpxU2b0(lEMcoibQ-G^cxO?ySVW26HoBNa;n0}CWL*{k)oBu1>F18X061$SP{Gu67 z-v-Fa=Fl^u3lnGY^o5v)Bux}bNZ~ z5pL+7F_Esoun8^5>z8NFoIdb$sNS&xT8_|`GTe8zSXQzs4r^g0kZjg(b0bJvz`g<70u9Z3fQILX1Lj@;@+##bP|FAOl)U^9U>0rx zGi)M1(Hce)LAvQO-pW!MN$;#ZMX?VE(22lTlJrk#pB0FJNqVwC+*%${Gt#r_tH9I_ z;+#)#8cWAl?d@R+O+}@1A^hAR1s3UcW{G+>;X4utD2d9X(jF555}!TVN-hByV6t+A zdFR^aE@GNNgSxxixS2p=on4(+*+f<8xrwAObC)D5)4!z7)}mTpb7&ofF3u&9&wPS< zB62WHLGMhmrmOAgmJ+|c>qEWTD#jd~lHNgT0?t-p{T=~#EMcB| z=AoDKOL+qXCfk~F)-Rv**V}}gWFl>liXOl7Uec_8v)(S#av99PX1sQIVZ9eNLkhq$ zt|qu0b?GW_uo}TbU8!jYn8iJeIP)r@;!Ze_7mj{AUV$GEz6bDSDO=D!&C9!M@*S2! zfGyA|EPlXGMjkH6x7OMF?gKL7{GvGfED=Jte^p=91FpCu)#{whAMw`vSLa`K#atdN zThnL+7!ZNmP{rc=Z>%$meH;Qi1=m1E3Lq2D_O1-X5C;!I0L>zur@tPAC9*7Jeh)`;eec}1`nkRP(%iv-`N zZ@ip-g|7l6Hz%j%gcAM}6-nrC8oA$BkOTz^?dakvX?`^=ZkYh%vUE z9+&)K1UTK=ahYiaNn&G5nHUY5niLGus@p5E2@RwZufRvF{@$hW{;{3QhjvEHMvduO z#Wf-@oYU4ht?#uP{N3utVzV49mEc9>*TV_W2TVC`6+oI)zAjy$KJrr=*q##&kobiQ z1vNbya&OVjK`2pdRrM?LuK6BgrLN7H_3m z!qpNKg~87XgCwb#I=Q&0rI*l$wM!qTkXrx1ko5q-f;=R2fImRMwt5Qs{P*p^z@9ex z`2#v(qE&F%MXlHpdO#QEZyZftn4f05ab^f2vjxuFaat2}jke{j?5GrF=WYBR?gS(^ z9SBiNi}anzBDBRc+QqizTTQuJrzm^bNA~A{j%ugXP7McZqJ}65l10({wk++$=e8O{ zxWjG!Qp#5OmI#XRQQM?n6?1ztl6^D40hDJr?4$Wc&O_{*OfMfxe)V0=e{|N?J#fgE>j9jAajze$iN!*yeF%jJU#G1c@@rm zolGW!j?W6Q8pP=lkctNFdfgUMg92wlM4E$aks1??M$~WQfzzzXtS)wKrr2sJeCN4X zY(X^H_c^PzfcO8Bq(Q*p4c_v@F$Y8cHLrH$`pJ2}=#*8%JYdqsqnGqEdBQMpl!Ot04tUGSXTQdsX&GDtjbWD=prcCT9(+ z&UM%lW%Q3yrl1yiYs;LxzIy>2G}EPY6|sBhL&X&RAQrSAV4Tlh2nITR?{6xO9ujGu zr*)^E`>o!c=gT*_@6S&>0POxcXYNQd&HMw6<|#{eSute2C3{&h?Ah|cw56-AP^f8l zT^kvZY$YiH8j)sk7_=;gx)vx-PW`hbSBXJGCTkpt;ap(}G2GY=2bbjABU5)ty%G#x zAi07{Bjhv}>OD#5zh#$0w;-vvC@^}F! z#X$@)zIs1L^E;2xDAwEjaXhTBw2<{&JkF*`;c3<1U@A4MaLPe{M5DGGkL}#{cHL%* zYMG+-Fm0#qzPL#V)TvQVI|?_M>=zVJr9>(6ib*#z8q@mYKXDP`k&A4A};xMK0h=yrMp~JW{L?mE~ph&1Y1a#4%SO)@{ zK2juwynUOC)U*hVlJU17%llUxAJFuKZh3K0gU`aP)pc~bE~mM!i1mi!~LTf>1Wp< zuG+ahp^gH8g8-M$u{HUWh0m^9Rg@cQ{&DAO{PTMudV6c?ka7+AO& z746QylZ&Oj`1aqfu?l&zGtJnpEQOt;OAFq19MXTcI~`ZcoZmyMrIKDFRIDi`FH)w; z8+*8tdevMDv*VtQi|e}CnB_JWs>fhLOH-+Os2Lh!&)Oh2utl{*AwR)QVLS49iTp{6 z;|172Jl!Ml17unF+pd+Ff@jIE-{Oxv)5|pOm@CkHW?{l}b@1>Pe!l}VccX#xp@xgJ zyE<&ep$=*vT=}7vtvif0B?9xw_3Gej7mN*dOHdQPtW5kA5_zGD zpA4tV2*0E^OUimSsV#?Tg#oiQ>%4D@1F5@AHwT8Kgen$bSMHD3sXCkq8^(uo7CWk`mT zuslYq`6Yz;L%wJh$3l1%SZv#QnG3=NZ=BK4yzk#HAPbqXa92;3K5?0kn4TQ`%E%X} z&>Lbt!!QclYKd6+J7Nl@xv!uD%)*bY-;p`y^ZCC<%LEHUi$l5biu!sT3TGGSTPA21 zT8@B&a0lJHVn1I$I3I1I{W9fJAYc+8 zVj8>HvD}&O`TqU2AAb={?eT;0hyL(R{|h23=4fDSZKC32;wWxsVj`P z3J3{M$PwdH!ro*Cn!D&=jnFR>BNGR<<|I8CI@+@658Dy(lhqbhXfPTVecY@L8%`3Q z1Fux2w?2C3th60jI~%OC9BtpNF$QPqcG+Pz96qZJ71_`0o0w_q7|h&O>`6U+^BA&5 zXd5Zp1Xkw~>M%RixTm&OqpNl8Q+ue=92Op_>T~_9UON?ZM2c0aGm=^A4ejrXj3dV9 zhh_bCt-b9`uOX#cFLj!vhZ#lS8Tc47OH>*)y#{O9?AT~KR9LntM|#l#Dlm^8{nZdk zjMl#>ZM%#^nK2TPzLcKxqx24P7R1FPlBy7LSBrRvx>fE$9AJ;7{PQm~^LBX^k#6Zq zw*Z(zJC|`!6_)EFR}8|n8&&Rbj8y028~P~sFXBFRt+tmqH-S3<%N;C&WGH!f3{7cm zy_fCAb9@HqaXa1Y5vFbxWf%#zg6SI$C+Uz5=CTO}e|2fjWkZ;Dx|84Ow~bkI=LW+U zuq;KSv9VMboRvs9)}2PAO|b(JCEC_A0wq{uEj|3x@}*=bOd zwr{TgeCGG>HT<@Zeq8y}vTpwDg#UBvD)BEs@1KP$^3$sh&_joQPn{hjBXmLPJ{tC) z*HS`*2+VtJO{|e$mM^|qv1R*8i(m1`%)}g=SU#T#0KlTM2RSvYUc1fP+va|4;5}Bfz98UvDCpq7}+SMV&;nX zQw~N6qOX{P55{#LQkrZk(e5YGzr|(B;Q;ju;2a`q+S9bsEH@i1{_Y0;hWYn1-79jl z5c&bytD*k)GqrVcHn6t-7kinadiD>B{Tl`ZY@`g|b~pvHh5!gKP4({rp?D0aFd_cN zhHRo4dd5^S6ViN(>(28qZT6E>??aRhc($kP`>@<+lIKS5HdhjVU;>f7<4))E*5|g{ z&d1}D|vpuV^eRj5j|xx9nwaCxXFG?Qbjn~_WSy=N}P0W>MP zG-F%70lX5Xr$a)2i6?i|iMyM|;Jtf*hO?=Jxj12oz&>P=1#h~lf%#fc73M2_(SUM- zf&qnjS80|_Y0lDgl&I?*eMumUklLe_=Td!9G@eR*tcPOgIShJipp3{A10u(4eT~DY zHezEj8V+7m!knn7)W!-5QI3=IvC^as5+TW1@Ern@yX| z7Nn~xVx&fGSr+L%4iohtS3w^{-H1A_5=r&x8}R!YZvp<2T^YFvj8G_vm}5q;^UOJf ztl=X3iL;;^^a#`t{Ae-%5Oq{?M#s6Npj+L(n-*LMI-yMR{)qki!~{5z{&`-iL}lgW zxo+tnvICK=lImjV$Z|O_cYj_PlEYCzu-XBz&XC-JVxUh9;6*z4fuBG+H{voCC;`~GYV|hj%j_&I zDZCj>Q_0RCwFauYoVMiUSB+*Mx`tg)bWmM^SwMA+?lBg12QUF_x2b)b?qb88K-YUd z0dO}3k#QirBV<5%jL$#wlf!60dizu;tsp(7XLdI=eQs?P`tOZYMjVq&jE)qK*6B^$ zBe>VvH5TO>s>izhwJJ$<`a8fakTL!yM^Zfr2hV9`f}}VVUXK39p@G|xYRz{fTI+Yq z20d=)iwjuG9RB$%$^&8#(c0_j0t_C~^|n+c`Apu|x7~;#cS-s=X1|C*YxX3ailhg_|0`g!E&GZJEr?bh#Tpb8siR=JxWKc{#w7g zWznLwi;zLFmM1g8V5-P#RsM@iX>TK$xsWuujcsVR^7TQ@!+vCD<>Bk9tdCo7Mzgq5 zv8d>dK9x8C@Qoh01u@3h0X_`SZluTb@5o;{4{{eF!-4405x8X7hewZWpz z2qEi4UTiXTvsa(0X7kQH{3VMF>W|6;6iTrrYD2fMggFA&-CBEfSqPlQDxqsa>{e2M z(R5PJ7uOooFc|9GU0ELA%m4&4Ja#cQpNw8i8ACAoK6?-px+oBl_yKmenZut#Xumjz zk8p^OV2KY&?5MUwGrBOo?ki`Sxo#?-Q4gw*Sh0k`@ zFTaYK2;}%Zk-68`#5DXU$2#=%YL#S&MTN8bF+!J2VT6x^XBci6O)Q#JfW{YMz) zOBM>t2rSj)n#0a3cjvu}r|k3od6W(SN}V-cL?bi*Iz-8uOcCcsX0L>ZXjLqk zZu2uHq5B|Kt>e+=pPKu=1P@1r9WLgYFq_TNV1p9pu0erHGd!+bBp!qGi+~4A(RsYN@CyXNrC&hxGmW)u5m35OmWwX`I+0yByglO`}HC4nGE^_HUs^&A(uaM zKPj^=qI{&ayOq#z=p&pnx@@k&I1JI>cttJcu@Ihljt?6p^6{|ds`0MoQwp+I{3l6` zB<9S((RpLG^>=Kic`1LnhpW2=Gu!x`m~=y;A`Qk!-w`IN;S8S930#vBVMv2vCKi}u z6<-VPrU0AnE&vzwV(CFC0gnZYcpa-l5T0ZS$P6(?9AM;`Aj~XDvt;Jua=jIgF=Fm? zdp=M$>`phx%+Gu};;-&7T|B1AcC#L4@mW5SV_^1BRbo6;2PWe$r+npRV`yc;T1mo& z+~_?7rA+(Um&o@Tddl zL_hxvWk~a)yY}%j`Y+200D%9$bWHy&;(yj{jpi?Rtz{J66ANw)UyPOm;t6FzY3$hx zcn)Ir79nhFvNa7^a{SHN7XH*|Vlsx`CddPnA&Qvh8aNhEA;mPVv;Ah=k<*u!Zq^7 z<=xs*iQTQOMMcg|(NA_auh@x`3#_LFt=)}%SQppP{E>mu_LgquAWvh<>L7tf9+~rO znwUDS52u)OtY<~!d$;m9+87aO+&`#2ICl@Y>&F{jI=H(K+@3M1$rr=*H^dye#~TyD z!){#Pyfn+|ugUu}G;a~!&&0aqQ59U@UT3|_JuBlYUpT$2+11;}JBJ`{+lQN9T@QFY z5+`t;6(TS0F?OlBTE!@7D`8#URDNqx2t6`GZ{ZgXeS@v%-eJzZOHz18aS|svxII$a zZeFjrJ*$IwX$f-Rzr_G>xbu@euGl)B7pC&S+CmDJBg$BoV~jxSO#>y z33`bupN#LDoW0feZe0%q8un0rYN|eRAnwDHQ6e_)xBTbtoZtTA=Fvk){q}9Os~6mQ zKB80VI_&6iSq`LnK7*kfHZoeX6?WE}8yjuDn=2#JG$+;-TOA1%^=DnXx%w{b=w}tS zQbU3XxtOI8E(!%`64r2`zog;5<0b4i)xBmGP^jiDZ2%HNSxIf3@wKs~uk4%3Mxz;~ zts_S~E4>W+YwI<-*-$U8*^HKDEa8oLbmqGg?3vewnaNg%Mm)W=)lcC_J+1ov^u*N3 zXJ?!BrH-+wGYziJq2Y#vyry6Z>NPgkEk+Ke`^DvNRdb>Q2Nlr#v%O@<5hbflI6EKE z9dWc0-ORk^T}jP!nkJ1imyjdVX@GrjOs%cpgA8-c&FH&$(4od#x6Y&=LiJZPINVyW z0snY$8JW@>tc2}DlrD3StQmA0Twck~@>8dSix9CyQOALcREdxoM$Sw*l!}bXKq9&r zysMWR@%OY24@e`?+#xV2bk{T^C_xSo8v2ZI=lBI*l{RciPwuE>L5@uhz@{!l)rtVlWC>)6(G)1~n=Q|S!{E9~6*fdpa*n z!()-8EpTdj=zr_Lswi;#{TxbtH$8*G=UM`I+icz7sr_SdnHXrv=?iEOF1UL+*6O;% zPw>t^kbW9X@oEXx<97%lBm-9?O_7L!DeD)Me#rwE54t~UBu9VZ zl_I1tBB~>jm@bw0Aljz8! zXBB6ATG6iByKIxs!qr%pz%wgqbg(l{65DP4#v(vqhhL{0b#0C8mq`bnqZ1OwFV z7mlZZJFMACm>h9v^2J9+^_zc1=JjL#qM5ZHaThH&n zXPTsR8(+)cj&>Un{6v*z?@VTLr{TmZ@-fY%*o2G}*G}#!bmqpoo*Ay@U!JI^Q@7gj;Kg-HIrLj4}#ec4~D2~X6vo;ghep-@&yOivYP zC19L0D`jjKy1Yi-SGPAn94(768Tcf$urAf{)1)9W58P`6MA{YG%O?|07!g9(b`8PXG1B1Sh0?HQmeJtP0M$O$hI z{5G`&9XzYhh|y@qsF1GnHN|~^ru~HVf#)lOTSrv=S@DyR$UKQk zjdEPFDz{uHM&UM;=mG!xKvp;xAGHOBo~>_=WFTmh$chpC7c`~7?36h)7$fF~Ii}8q zF|YXxH-Z?d+Q+27Rs3X9S&K3N+)OBxMHn1u(vlrUC6ckBY@@jl+mgr#KQUKo#VeFm zFwNYgv0<%~Wn}KeLeD9e1$S>jhOq&(e*I@L<=I5b(?G(zpqI*WBqf|Zge0&aoDUsC zngMRA_Kt0>La+Erl=Uv_J^p(z=!?XHpenzn$%EA`JIq#yYF?JLDMYiPfM(&Csr#f{ zdd+LJL1by?xz|D8+(fgzRs~(N1k9DSyK@LJygwaYX8dZl0W!I&c^K?7)z{2is;OkE zd$VK-(uH#AUaZrp=1z;O*n=b?QJkxu`Xsw&7yrX0?(CX=I-C#T;yi8a<{E~?vr3W> zQrpPqOW2M+AnZ&p{hqmHZU-;Q(7?- zP8L|Q0RM~sB0w1w53f&Kd*y}ofx@c z5Y6B8qGel+uT1JMot$nT1!Tim6{>oZzJXdyA+4euOLME?5Fd_85Uk%#E*ln%y{u8Q z$|?|R@Hpb~yTVK-Yr_S#%NUy7EBfYGAg>b({J|5b+j-PBpPy$Ns`PaJin4JdRfOaS zE|<HjH%NuJgsd2wOlv>~y=np%=2)$M9LS|>P)zJ+Fei5vYo_N~B0XCn+GM76 z)Xz3tg*FRVFgIl9zpESgdpWAavvVViGlU8|UFY{{gVJskg*I!ZjWyk~OW-Td4(mZ6 zB&SQreAAMqwp}rjy`HsG({l2&q5Y52<@AULVAu~rWI$UbFuZs>Sc*x+XI<+ez%$U)|a^unjpiW0l0 zj1!K0(b6$8LOjzRqQ~K&dfbMIE=TF}XFAi)$+h}5SD3lo z%%Qd>p9se=VtQG{kQ;N`sI)G^u|DN#7{aoEd zkksYP%_X$Rq08);-s6o>CGJ<}v`qs%eYf+J%DQ^2k68C%nvikRsN?$ap--f+vCS`K z#&~)f7!N^;sdUXu54gl3L=LN>FB^tuK=y2e#|hWiWUls__n@L|>xH{%8lIJTd5`w? zSwZbnS;W~DawT4OwSJVdAylbY+u5S+ZH{4hAi2&}Iv~W(UvHg(1GTZRPz`@{SOqzy z(8g&Dz=$PfRV=6FgxN~zo+G8OoPI&d-thcGVR*_^(R8COTM@bq?fDwY{}WhsQS1AK zF6R1t8!RdFmfocpJ6?9Yv~;WYi~XPgs(|>{5})j!AR!voO7y9&cMPo#80A(`za@t>cx<0;qxM@S*m(jYP)dMXr*?q0E`oL;12}VAep179uEr8c<=D zr5?A*C{eJ`z9Ee;E$8)MECqatHkbHH z&Y+ho0B$31MIB-xm&;xyaFCtg<{m~M-QDbY)fQ>Q*Xibb~8ytxZQ?QMf9!%cV zU0_X1@b4d+Pg#R!`OJ~DOrQz3@cpiGy~XSKjZQQ|^4J1puvwKeScrH8o{bscBsowomu z^f12kTvje`yEI3eEXDHJ6L+O{Jv$HVj%IKb|J{IvD*l6IG8WUgDJ*UGz z3!C%>?=dlfSJ>4U88)V+`U-!9r^@AxJBx8R;)J4Fn@`~k>8>v0M9xp90OJElWP&R5 zM#v*vtT}*Gm1^)Bv!s72T3PB0yVIjJW)H7a)ilkAvoaH?)jjb`MP>2z{%Y?}83 zUIwBKn`-MSg)=?R)1Q0z3b>dHE^)D8LFs}6ASG1|daDly_^lOSy&zIIhm*HXm1?VS=_iacG);_I9c zUQH1>i#*?oPIwBMJkzi_*>HoUe}_4o>2(SHWzqQ=;TyhAHS;Enr7!#8;sdlty&(>d zl%5cjri8`2X^Ds`jnw7>A`X|bl=U8n+3LKLy(1dAu8`g@9=5iw$R0qk)w8Vh_Dt^U zIglK}sn^)W7aB(Q>HvrX=rxB z+*L)3DiqpQ_%~|m=44LcD4-bxO3OO*LPjsh%p(k?&jvLp0py57oMH|*IMa(<|{m1(0S|x)?R-mqJ=I;_YUZA>J z62v*eSK;5w!h8J+6Z2~oyGdZ68waWfy09?4fU&m7%u~zi?YPHPgK6LDwphgaYu%0j zurtw)AYOpYKgHBrkX189mlJ`q)w-f|6>IER{5Lk97%P~a-JyCRFjejW@L>n4vt6#hq;!|m;hNE||LK3nw1{bJOy+eBJjK=QqNjI;Q6;Rp5 z&035pZDUZ#%Oa;&_7x0T<7!RW`#YBOj}F380Bq?MjjEhrvlCATPdkCTTl+2efTX$k zH&0zR1n^`C3ef~^sXzJK-)52(T}uTG%OF8yDhT76L~|^+hZ2hiSM*QA9*D5odI1>& z9kV9jC~twA5MwyOx(lsGD_ggYmztXPD`2=_V|ks_FOx!_J8!zM zTzh^cc+=VNZ&(OdN=y4Juw)@8-85lwf_#VMN!Ed(eQiRiLB2^2e`4dp286h@v@`O%_b)Y~A; zv}r6U?zs&@uD_+(_4bwoy7*uozNvp?bXFoB8?l8yG0qsm1JYzIvB_OH4_2G*IIOwT zVl%HX1562vLVcxM_RG*~w_`FbIc!(T=3>r528#%mwwMK}uEhJ()3MEby zQQjzqjWkwfI~;Fuj(Lj=Ug0y`>~C7`w&wzjK(rPw+Hpd~EvQ-ufQOiB4OMpyUKJhw zqEt~jle9d7S~LI~$6Z->J~QJ{Vdn3!c}g9}*KG^Kzr^(7VI5Gk(mHLL{itj_hG?&K4Ws0+T4gLfi3eu$N=`s36geNC?c zm!~}vG6lx9Uf^5M;bWntF<-{p^bruy~f?sk9 zcETAPQZLoJ8JzMMg<-=ju4keY@SY%Wo?u9Gx=j&dfa6LIAB|IrbORLV1-H==Z1zCM zeZcOYpm5>U2fU7V*h;%n`8 zN95QhfD994={1*<2vKLCNF)feKOGk`R#K~G=;rfq}|)s20&MCa65 zUM?xF5!&e0lF%|U!#rD@I{~OsS_?=;s_MQ_b_s=PuWdC)q|UQ&ea)DMRh5>fpQjXe z%9#*x=7{iRCtBKT#H>#v%>77|{4_slZ)XCY{s3j_r{tdpvb#|r|sbS^dU1x70$eJMU!h{Y7Kd{dl}9&vxQl6Jt1a` zHQZrWyY0?!vqf@u-fxU_@+}u(%Wm>0I#KP48tiAPYY!TdW(o|KtVI|EUB9V`CBBNaBLVih7+yMVF|GSoIQD0Jfb{ z!OXq;(>Z?O`1gap(L~bUcp>Lc@Jl-})^=6P%<~~9ywY=$iu8pJ0m*hOPzr~q`23eX zgbs;VOxxENe0UMVeN*>uCn9Gk!4siN-e>x)pIKAbQz!G)TcqIJ0`JBBaX>1-4_XO_-HCS^vr2vjv#7KltDZdyQ{tlWh4$Gm zB>|O1cBDC)yG(sbnc*@w6e%e}r*|IhpXckx&;sQCwGdKH+3oSG-2)Bf#x`@<4ETAr z0My%7RFh6ZLiZ_;X6Mu1YmXx7C$lSZ^}1h;j`EZd6@%JNUe=btBE z%s=Xmo1Ps?8G`}9+6>iaB8bgjUdXT?=trMu|4yLX^m0Dg{m7rpKNJey|EwHI+nN1e zL^>qN%5Fg)dGs4DO~uwIdXImN)QJ*Jhpj7$fq_^`{3fwpztL@WBB}OwQ#Epo-mqMO zsM$UgpFiG&d#)lzEQ{3Q;)&zTw;SzGOah-Dpm{!q7<8*)Ti_;xvV2TYXa}=faXZy? z3y?~GY@kl)>G&EvEijk9y1S`*=zBJSB1iet>0;x1Ai)*`^{pj0JMs)KAM=@UyOGtO z3y0BouW$N&TnwU6!%zS%nIrnANvZF&vB1~P5_d`x-giHuG zPJ;>XkVoghm#kZXRf>qxxEix;2;D1CC~NrbO6NBX!`&_$iXwP~P*c($EVV|669kDO zKoTLZNF4Cskh!Jz5ga9uZ`3o%7Pv`d^;a=cXI|>y;zC3rYPFLQkF*nv(r>SQvD*## z(Vo%^9g`%XwS0t#94zPq;mYGLKu4LU3;txF26?V~A0xZbU4Lmy`)>SoQX^m7fd^*E z+%{R4eN!rIk~K)M&UEzxp9dbY;_I^c} zOc{wlIrN_P(PPqi51k_$>Lt|X6A^|CGYgKAmoI#Li?;Wq%q~q*L7ehZkUrMxW67Jl zhsb~+U?33QS>eqyN{(odAkbopo=Q$Az?L+NZW>j;#~@wCDX?=L5SI|OxI~7!Pli;e zELMFcZtJY3!|=Gr2L4>z8yQ-{To>(f80*#;6`4IAiqUw`=Pg$%C?#1 z_g@hIGerILSU>=P>z{gM|DS91A4cT@PEIB^hSop!uhMo#2G;+tQSpDO_6nOnPWSLU zS;a9m^DFMXR4?*X=}d7l;nXuHk&0|m`NQn%d?8|Ab3A9l9Jh5s120ibWBdB z$5YwsK3;wvp!Kn@)Qae{ef`0#NwlRpQ}k^r>yos_Ne1;xyKLO?4)t_G4eK~wkUS2A&@_;)K0-03XGBzU+5f+uMDxC z(s8!8!RvdC#@`~fx$r)TKdLD6fWEVdEYtV#{ncT-ZMX~eI#UeQ-+H(Z43vVn%Yj9X zLdu9>o%wnWdvzA-#d6Z~vzj-}V3FQ5;axDIZ;i(95IIU=GQ4WuU{tl-{gk!5{l4_d zvvb&uE{%!iFwpymz{wh?bKr1*qzeZb5f6e6m_ozRF&zux2mlK=v_(_s^R6b5lu?_W4W3#<$zeG~Pd)^!4tzhs}-Sx$FJP>)ZGF(hVTH|C3(U zs0PO&*h_ zNA-&qZpTP$$LtIgfiCn07}XDbK#HIXdmv8zdz4TY;ifNIH-0jy(gMSByG2EF~Th#eb_TueZC` zE?3I>UTMpKQ})=C;6p!?G)M6w^u*A57bD?2X`m3X^6;&4%i_m(uGJ3Z5h`nwxM<)H z$I5m?wN>O~8`BGnZ=y^p6;0+%_0K}Dcg|K;+fEi|qoBqvHj(M&aHGqNF48~XqhtU? z^ogwBzRlOfpAJ+Rw7IED8lRbTdBdyEK$gPUpUG}j-M42xDj_&qEAQEtbs>D#dRd7Y z<&TpSZ(quQDHiCFn&0xsrz~4`4tz!CdL8m~HxZM_agu@IrBpyeL1Ft}V$HX_ZqDPm z-f89)pjuEzGdq-PRu`b1m+qBGY{zr_>{6Ss>F|xHZlJj9dt5HD$u`1*WZe)qEIuDSR)%z+|n zatVlhQ?$w#XRS7xUrFE;Y8vMGhQS5*T{ZnY=q1P?w5g$OKJ#M&e??tAmPWHMj3xhS ziGxapy?kn@$~2%ZY;M8Bc@%$pkl%Rvj!?o%agBvpQ-Q61n9kznC4ttrRNQ4%GFR5u zyv%Yo9~yxQJWJSfj z?#HY$y=O~F|2pZs22pu|_&Ajd+D(Mt!nPUG{|1nlvP`=R#kKH zO*s$r_%ss5h1YO7k0bHJ2CXN)Yd6CHn~W!R=SqkWe=&nAZu(Q1G!xgcUilM@YVei@2@a`8he z9@pM`)VB*=e7-MWgLlXlc)t;fF&-AwM{E-EX}pViFn0I0CNw2bNEnN2dj!^4(^zS3 zobUm1uQnpqk_4q{pl*n06=TfK_C>UgurKFjRXsK_LEn};=79`TB12tv6KzwSu*-C8 z;=~ohDLZylHQ|Mpx-?yql>|e=vI1Z!epyUpAcDCp4T|*RV&X`Q$0ogNwy6mFALo^@ z9=&(9txO8V@E!@6^(W0{*~CT>+-MA~vnJULBxCTUW>X5>r7*eXYUT0B6+w@lzw%n> z_VjJ<2qf|(d6jYq2(x$(ZDf!yVkfnbvNmb5c|hhZ^2TV_LBz`9w!e_V*W_(MiA7|= z&EeIIkw*+$Xd!)j8<@_<}A5;~A_>3JT*kX^@}cDoLd>Qj<`Se^wdUa(j0dp+Tl8EptwBm{9OGsdFEq zM`!pjf(Lm(`$e3FLOjqA5LnN5o!}z{ zNf}rJuZh@yUtq&ErjHeGzX4(!luV!jB&;FAP|!R_QHYw#^Z1LwTePAKJ6X&IDNO#; z)#I@Xnnzyij~C@UH~X51JCgQeF0&hTXnuoElz#m{heZRexWc0k4<>0+ClX7%0 zEBqCCld1tD9Zwkr4{?Nor19#E5-YKfB8d?qgR82-Ow2^AuNevly2*tHA|sK!ybYkX zm-sLQH72P&{vEAW6+z~O5d0qd=xW~rua~5a?ymYFSD@8&gV)E5@RNNBAj^C99+Z5Z zR@Pq55mbCQbz+Mn$d_CMW<-+?TU960agEk1J<>d>0K=pF19yN))a~4>m^G&tc*xR+yMD*S=yip-q=H zIlredHpsJV8H(32@Zxc@bX6a21dUV95Th--8pE6C&3F>pk=yv$yd6@Haw;$v4+Fcb zRwn{Qo@0`7aPa2LQOP}j9v>sjOo5Kqvn|`FLizX zB+@-u4Lw|jsvz{p^>n8Vo8H2peIqJJnMN}A)q6%$Tmig7eu^}K2 zrh$X?T|ZMsoh{6pdw1G$_T<`Ds-G=jc;qcGdK4{?dN2-XxjDNbb(7pk|3JUVCU4y; z)?LXR>f+AAu)JEiti_Zy#z5{RgsC}R(@jl%9YZ>zu~hKQ*AxbvhC378-I@{~#%Y`Z zy=a=9YpewPIC+gkEUUwtUL7|RU7=!^Aa}Mk^6uxOgRGA#JXjWLsjFUnix|Mau{hDT z7mn*z1m5g`vP(#tjT0Zy4eAY(br&!RiiXE=ZI!{sE1#^#%x^Z7t1U)b<;%Y}Q9=5v z;wpDCEZ@OE36TWT=|gxigT@VaW9BvHS05;_P(#s z8zI4XFQys}q)<`tkX$WnSarn{3e!s}4(J!=Yf>+Y>cP3f;vr63f2{|S^`_pWc)^5_!R z*(x-fuBxL51@xe!lnDBKi}Br$c$BMZ3%f2Sa6kLabiBS{pq*yj;q|k(86x`PiC{p6 z_bxCW{>Q2BA8~Ggz&0jkrcU+-$ANBsOop*ms>34K9lNYil@}jC;?cYP(m^P}nR6FV zk(M%48Z&%2Rx$A&FhOEirEhY0(dn;-k(qkTU)sFQ`+-ih+s@A8g?r8Pw+}2;35WYf zi}VO`jS`p(tc)$X$a>-#WXoW!phhatC*$}|rk>|wUU71eUJG^$c6_jwX?iSHM@6__ zvV|6%U*$sSXJu9SX?2%M^kK|}a2QJ8AhF{fuXrHZxXsI~O zGKX45!K7p*MCPEQ=gp?eu&#AW*pR{lhQR##P_*{c_DjMGL|3T3-bSJ(o$|M{ytU}> zAV>wq*uE*qFo9KvnA^@juy{x<-u*#2NvkV={Ly}ysKYB-k`K3@K#^S1Bb$8Y#0L0# z`6IkSG&|Z$ODy|VLS+y5pFJx&8tvPmMd8c9FhCyiU8~k6FwkakUd^(_ml8`rnl>JS zZV){9G*)xBqPz^LDqRwyS6w86#D^~xP4($150M)SOZRe9sn=>V#aG0Iy(_^YcPpIz8QYM-#s+n% z@Jd?xQq?Xk6=<3xSY7XYP$$yd&Spu{A#uafiIfy8gRC`o0nk{ezEDjb=q_qRAlR1d zFq^*9Gn)yTG4b}R{!+3hWQ+u3GT~8nwl2S1lpw`s0X_qpxv)g+JIkVKl${sYf_nV~B>Em>M;RlqGb5WVil(89 zs=ld@|#;dq1*vQGz=7--Br-|l) zZ%Xh@v8>B7P?~}?Cg$q9_={59l%m~O&*a6TKsCMAzG&vD>k2WDzJ6!tc!V)+oxF;h zJH;apM=wO?r_+*#;ulohuP=E>^zon}a$NnlcQ{1$SO*i=jnGVcQa^>QOILc)e6;eNTI>os=eaJ{*^DE+~jc zS}TYeOykDmJ=6O%>m`i*>&pO_S;qMySJIyP=}4E&J%#1zju$RpVAkZbEl+p%?ZP^C z*$$2b4t%a(e+%>a>d_f_<JjxI#J1x;=hPd1zFPx=6T$;;X1TD*2(edZ3f46zaAoW>L53vS_J*N8TMB|n+;LD| zC=GkQPpyDY#Am4l49chDv*gojhRj_?63&&8#doW`INATAo(qY#{q}%nf@eTIXmtU< zdB<7YWfyCmBs|c)cK>1)v&M#!yNj#4d$~pVfDWQc_ke1?fw{T1Nce_b`v|Vp5ig(H zJvRD^+ps46^hLX;=e2!2e;w9y1D@!D$c@Jc&%%%IL=+xzw55&2?darw=9g~>P z9>?Kdc$r?6c$m%x2S$sdpPl>GQZ{rC9mPS63*qjCVa?OIBj!fW zm|g?>CVfGXNjOfcyqImXR_(tXS(F{FcoNzKvG5R$IgGaxC@)i(e+$ME}vPVIhd|mx2IIE+f zM?9opQHIVgBWu)^A|RzXw!^??S!x)SZOwZaJkGjc<_}2l^eSBm!eAJG9T>EC6I_sy z?bxzDIAn&K5*mX)$RQzDA?s)-no-XF(g*yl4%+GBf`##bDXJ==AQk*xmnatI;SsLp zP9XTHq5mmS=iWu~9ES>b%Q=1aMa|ya^vj$@qz9S!ih{T8_PD%Sf_QrNKwgrXw9ldm zHRVR98*{C?_XNpJn{abA!oix_mowRMu^2lV-LPi;0+?-F(>^5#OHX-fPED zCu^l7u3E%STI}c4{J2!)9SUlGP_@!d?5W^QJXOI-Ea`hFMKjR7TluLvzC-ozCPn1`Tpy z!vlv@_Z58ILX6>nDjTp-1LlFMx~-%GA`aJvG$?8*Ihn;mH37eK**rmOEwqegf-Ccx zrIX4;{c~RK>XuTXxYo5kMiWMy)!IC{*DHG@E$hx?RwP@+wuad(P1{@%tRkyJRqD)3 zMHHHZ4boqDn>-=DgR5VlhQTpfVy182Gk;A_S8A1-;U1RR>+$62>(MUx@Nox$vTjHq z%QR=j!6Gdyb5wu7y(YUktwMuW5<@jl?m4cv4BODiT5o8qVdC0MBqGr@-YBIwnpZAY znX9(_uQjP}JJ=!~Ve9#5I~rUnN|P_3D$LqZcvBnywYhjlMSFHm`;u9GPla{5QD7(7*6Tb3Svr8;(nuAd81q$*uq6HC_&~je*Ca7hP4sJp0av{M8480wF zxASi7Qv+~@2U%Nu1Ud;s-G4CTVWIPyx!sg&8ZG0Wq zG_}i3C(6_1>q3w!EH7$Kwq8uBp2F2N7}l65mk1p*9v0&+;th=_E-W)E;w}P(j⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fa8f862f..1af9e0930 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From af1f22e8fe3f410bf11eabc2cb694f0abd1ac37b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:49:48 +0100 Subject: [PATCH 012/114] Update plugin xyz.jpenilla.run-paper to v2.2.2 (#2503) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 63e05aad2..02df265c0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ import xyz.jpenilla.runpaper.task.RunServer plugins { id("io.github.gradle-nexus.publish-plugin") version "1.3.0" - id("xyz.jpenilla.run-paper") version "2.2.0" + id("xyz.jpenilla.run-paper") version "2.2.2" } if (!File("$rootDir/.git").exists()) { From 11069ee34b8d7932b8d87aa59b7b8fd4ef736950 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:50:05 +0100 Subject: [PATCH 013/114] Update dependency com.modrinth.minotaur to v2.8.6 (#2501) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b3865b3d9..6c922efa0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -50,7 +50,7 @@ mockito = "5.8.0" # Gradle plugins pluginyml = "0.6.0" -minotaur = "2.8.4" +minotaur = "2.8.6" [libraries] # Minecraft expectations From 769d8b3a7a660bf471eea00f26013fe1f41663e3 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 7 Dec 2023 17:24:05 +0000 Subject: [PATCH 014/114] fix: set biome biome on clear (#2510) * fix: set biome biome on clear - fixes #2509 * Nicer biome setting method --- .../bukkit/regions/plotsquared/FaweDelegateRegionManager.java | 4 ++++ 1 file changed, 4 insertions(+) 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 4ef1a3d05..65f8863f8 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 @@ -160,6 +160,10 @@ public class FaweDelegateRegionManager { ); editSession.setBlocks(onTop, air); } + + FlatRegionFunction replace = new BiomeReplace(editSession, biome); + FlatRegionVisitor visitor = new FlatRegionVisitor((CuboidRegion) floorRegion, replace, editSession); + Operations.completeLegacy(visitor); } if (hybridPlotWorld.PLOT_SCHEMATIC) { From d5c8b08fe90f968c921a20472a71238b0c70d36f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:24:20 +0100 Subject: [PATCH 015/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.0.8 (#2502) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6c922efa0..5ac2c2ab9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.0.1" +towny = "0.100.0.8" plotsquared = "7.2.0" # Third party From ea6138ce1f1bd53113cb900d1e1686ccc610a961 Mon Sep 17 00:00:00 2001 From: RedstoneFuture Date: Thu, 7 Dec 2023 18:26:21 +0100 Subject: [PATCH 016/114] Unify the limit permission (#2420) --- worldedit-bukkit/src/main/resources/plugin.yml | 2 +- .../com/fastasyncworldedit/core/configuration/Settings.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/worldedit-bukkit/src/main/resources/plugin.yml b/worldedit-bukkit/src/main/resources/plugin.yml index b4f845265..cd12b1008 100644 --- a/worldedit-bukkit/src/main/resources/plugin.yml +++ b/worldedit-bukkit/src/main/resources/plugin.yml @@ -21,7 +21,7 @@ permissions: default: op children: fawe.bypass.regions: true - fawe.limit.*: true + fawe.limit.unlimited: true fawe.tips: default: op fawe.admin: diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index 672dcebe8..099386f00 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -102,7 +102,7 @@ public class Settings extends Config { public FaweLimit getLimit(Actor actor) { FaweLimit limit; - if (actor.hasPermission("fawe.bypass") || actor.hasPermission("fawe.limit.unlimited")) { + if (actor.hasPermission("fawe.limit.unlimited")) { return FaweLimit.MAX.copy(); } limit = new FaweLimit(); From 605743321f018738f00059bf14a76ab7e6a8993e Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 8 Dec 2023 07:30:08 +0100 Subject: [PATCH 017/114] Add support for 1.20.3 and 1.20.4 (#2512) * 1.20.3 * 1.20.3 ItemTypes * 1.20.3 * 1.20.3 * 1.20.4 * Fixup refractions * Move adapters to _4 --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- build.gradle.kts | 4 +- settings.gradle.kts | 2 +- .../adapter/ext/fawe/PaperweightAdapter.java | 2 +- .../adapters/adapter-1_20_4/build.gradle.kts | 17 + .../ext.fawe/v1_20_R3/PaperweightAdapter.java | 1019 ++++++ .../v1_20_R3/PaperweightDataConverters.java | 2801 +++++++++++++++++ .../v1_20_R3/PaperweightFakePlayer.java | 98 + .../PaperweightWorldNativeAccess.java | 181 ++ .../v1_20_R3/PaperweightBlockMaterial.java | 185 ++ .../fawe/v1_20_R3/PaperweightFaweAdapter.java | 617 ++++ .../PaperweightFaweWorldNativeAccess.java | 286 ++ .../fawe/v1_20_R3/PaperweightGetBlocks.java | 1181 +++++++ .../v1_20_R3/PaperweightGetBlocks_Copy.java | 254 ++ .../v1_20_R3/PaperweightMapChunkUtil.java | 34 + .../v1_20_R3/PaperweightPlatformAdapter.java | 719 +++++ .../v1_20_R3/PaperweightPostProcessor.java | 175 + .../PaperweightStarlightRelighter.java | 76 + .../PaperweightStarlightRelighterFactory.java | 25 + .../nbt/PaperweightLazyCompoundTag.java | 161 + .../fawe/v1_20_R3/regen/PaperweightRegen.java | 590 ++++ worldedit-bukkit/build.gradle.kts | 2 +- .../extent/reorder/MultiStageReorder.java | 5 + .../function/generator/FloraGenerator.java | 8 +- .../world/block/BlockCategories.java | 2 + .../worldedit/world/block/BlockTypes.java | 114 +- .../worldedit/world/entity/EntityTypes.java | 4 + .../sk89q/worldedit/world/item/ItemTypes.java | 118 +- 28 files changed, 8673 insertions(+), 9 deletions(-) create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightDataConverters.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightFakePlayer.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightWorldNativeAccess.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightMapChunkUtil.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPostProcessor.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighter.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighterFactory.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/nbt/PaperweightLazyCompoundTag.java create mode 100644 worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 560478e0e..b687075ad 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -27,7 +27,7 @@ body: description: Which server version version you using? If your server version is not listed, it is not supported. Update to a supported version first. multiple: false options: - - '1.20.2' + - '1.20.4' - '1.20' - '1.19.4' - '1.18.2' diff --git a/build.gradle.kts b/build.gradle.kts index 02df265c0..390870651 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -83,7 +83,7 @@ allprojects { } applyCommonConfiguration() -val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.2") +val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.4") tasks { supportedVersions.forEach { @@ -97,7 +97,7 @@ tasks { } } runServer { - minecraftVersion("1.20.2") + minecraftVersion("1.20.4") pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } .toTypedArray()) diff --git a/settings.gradle.kts b/settings.gradle.kts index d8b8012cf..6f9b3e8e0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit" include("worldedit-libs") -listOf("1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2").forEach { +listOf("1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2", "1_20_4").forEach { include("worldedit-bukkit:adapters:adapter-$it") } diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightAdapter.java index 52974fb02..7d1fcc84e 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightAdapter.java @@ -198,7 +198,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter().paperDevBundle("1.20.3-R0.1-20231207.043048-3") + compileOnly(libs.paperlib) +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java new file mode 100644 index 000000000..2dc7d4b2b --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java @@ -0,0 +1,1019 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Futures; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Lifecycle; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.registry.state.BooleanProperty; +import com.sk89q.worldedit.registry.state.DirectionalProperty; +import com.sk89q.worldedit.registry.state.EnumProperty; +import com.sk89q.worldedit.registry.state.IntegerProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.concurrency.LazyReference; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.util.io.file.SafeFiles; +import com.sk89q.worldedit.util.nbt.BinaryTag; +import com.sk89q.worldedit.util.nbt.ByteArrayBinaryTag; +import com.sk89q.worldedit.util.nbt.ByteBinaryTag; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import com.sk89q.worldedit.util.nbt.DoubleBinaryTag; +import com.sk89q.worldedit.util.nbt.EndBinaryTag; +import com.sk89q.worldedit.util.nbt.FloatBinaryTag; +import com.sk89q.worldedit.util.nbt.IntArrayBinaryTag; +import com.sk89q.worldedit.util.nbt.IntBinaryTag; +import com.sk89q.worldedit.util.nbt.ListBinaryTag; +import com.sk89q.worldedit.util.nbt.LongArrayBinaryTag; +import com.sk89q.worldedit.util.nbt.LongBinaryTag; +import com.sk89q.worldedit.util.nbt.ShortBinaryTag; +import com.sk89q.worldedit.util.nbt.StringBinaryTag; +import com.sk89q.worldedit.world.DataFixer; +import com.sk89q.worldedit.world.RegenOptions; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.item.ItemType; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.util.StringRepresentable; +import net.minecraft.util.thread.BlockableEventLoop; +import net.minecraft.world.Clearable; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.StructureBlockEntity; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.WorldOptions; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World.Environment; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R3.util.CraftMagicNumbers; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.generator.ChunkGenerator; +import org.spigotmc.SpigotConfig; +import org.spigotmc.WatchdogThread; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +public final class PaperweightAdapter implements BukkitImplAdapter { + + private final Logger logger = Logger.getLogger(getClass().getCanonicalName()); + + private final Field serverWorldsField; + private final Method getChunkFutureMethod; + private final Field chunkProviderExecutorField; + private final Watchdog watchdog; + + // ------------------------------------------------------------------------ + // Code that may break between versions of Minecraft + // ------------------------------------------------------------------------ + + public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException { + // A simple test + CraftServer.class.cast(Bukkit.getServer()); + + int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion(); + if (dataVersion != 3698) { + throw new UnsupportedClassVersionError("Not 1.20.4"); + } + + serverWorldsField = CraftServer.class.getDeclaredField("worlds"); + serverWorldsField.setAccessible(true); + + getChunkFutureMethod = ServerChunkCache.class.getDeclaredMethod( + Refraction.pickName("getChunkFutureMainThread", "c"), + int.class, int.class, ChunkStatus.class, boolean.class + ); + getChunkFutureMethod.setAccessible(true); + + chunkProviderExecutorField = ServerChunkCache.class.getDeclaredField( + Refraction.pickName("mainThreadProcessor", "g") + ); + chunkProviderExecutorField.setAccessible(true); + + new PaperweightDataConverters(CraftMagicNumbers.INSTANCE.getDataVersion(), this).buildUnoptimized(); + + Watchdog watchdog; + try { + Class.forName("org.spigotmc.WatchdogThread"); + watchdog = new SpigotWatchdog(); + } catch (ClassNotFoundException | NoSuchFieldException e) { + try { + watchdog = new MojangWatchdog(((CraftServer) Bukkit.getServer()).getServer()); + } catch (NoSuchFieldException ex) { + watchdog = null; + } + } + this.watchdog = watchdog; + + try { + Class.forName("org.spigotmc.SpigotConfig"); + SpigotConfig.config.set("world-settings.faweregentempworld.verbose", false); + } catch (ClassNotFoundException ignored) { + } + } + + @Override + public DataFixer getDataFixer() { + return PaperweightDataConverters.INSTANCE; + } + + /** + * Read the given NBT data into the given tile entity. + * + * @param tileEntity the tile entity + * @param tag the tag + */ + static void readTagIntoTileEntity(net.minecraft.nbt.CompoundTag tag, BlockEntity tileEntity) { + tileEntity.load(tag); + tileEntity.setChanged(); + } + + /** + * Get the ID string of the given entity. + * + * @param entity the entity + * @return the entity ID + */ + private static String getEntityId(Entity entity) { + return EntityType.getKey(entity.getType()).toString(); + } + + /** + * Create an entity using the given entity ID. + * + * @param id the entity ID + * @param world the world + * @return an entity or null + */ + @Nullable + private static Entity createEntityFromId(String id, net.minecraft.world.level.Level world) { + return EntityType.byString(id).map(t -> t.create(world)).orElse(null); + } + + /** + * Write the given NBT data into the given entity. + * + * @param entity the entity + * @param tag the tag + */ + private static void readTagIntoEntity(net.minecraft.nbt.CompoundTag tag, Entity entity) { + entity.load(tag); + } + + /** + * Write the entity's NBT data to the given tag. + * + * @param entity the entity + * @param tag the tag + */ + private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag tag) { + entity.save(tag); + } + + private static Block getBlockFromType(BlockType blockType) { + + return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK).get(ResourceLocation.tryParse(blockType.getId())); + } + + private static Item getItemFromType(ItemType itemType) { + return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(itemType.getId())); + } + + @Override + public OptionalInt getInternalBlockStateId(BlockData data) { + net.minecraft.world.level.block.state.BlockState state = ((CraftBlockData) data).getState(); + int combinedId = Block.getId(state); + return combinedId == 0 && state.getBlock() != Blocks.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId); + } + + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + Block mcBlock = getBlockFromType(state.getBlockType()); + net.minecraft.world.level.block.state.BlockState newState = mcBlock.defaultBlockState(); + Map, Object> states = state.getStates(); + newState = applyProperties(mcBlock.getStateDefinition(), newState, states); + final int combinedId = Block.getId(newState); + return combinedId == 0 && state.getBlockType() != BlockTypes.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId); + } + + @Override + public BlockState getBlock(Location location) { + checkNotNull(location); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + final BlockPos blockPos = new BlockPos(x, y, z); + final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); + int internalId = Block.getId(blockData); + BlockState state = BlockStateIdAccess.getBlockStateById(internalId); + if (state == null) { + org.bukkit.block.Block bukkitBlock = location.getBlock(); + state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); + } + + return state; + } + + @Override + public BaseBlock getFullBlock(Location location) { + BlockState state = getBlock(location); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + final BlockPos blockPos = new BlockPos(x, y, z); + + // Read the NBT data + BlockEntity te = chunk.getBlockEntity(blockPos); + if (te != null) { + net.minecraft.nbt.CompoundTag tag = te.saveWithId(); + return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag)); + } + + return state.toBaseBlock(); + } + + private static final HashMap> biomeTypeToNMSCache = new HashMap<>(); + private static final HashMap, BiomeType> biomeTypeFromNMSCache = new HashMap<>(); + + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); + } + + private static net.minecraft.core.Direction adapt(Direction face) { + switch (face) { + case NORTH: + return net.minecraft.core.Direction.NORTH; + case SOUTH: + return net.minecraft.core.Direction.SOUTH; + case WEST: + return net.minecraft.core.Direction.WEST; + case EAST: + return net.minecraft.core.Direction.EAST; + case DOWN: + return net.minecraft.core.Direction.DOWN; + case UP: + default: + return net.minecraft.core.Direction.UP; + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private net.minecraft.world.level.block.state.BlockState applyProperties( + StateDefinition stateContainer, + net.minecraft.world.level.block.state.BlockState newState, + Map, Object> states + ) { + for (Map.Entry, Object> state : states.entrySet()) { + net.minecraft.world.level.block.state.properties.Property property = + stateContainer.getProperty(state.getKey().getName()); + Comparable value = (Comparable) state.getValue(); + // we may need to adapt this value, depending on the source prop + if (property instanceof DirectionProperty) { + Direction dir = (Direction) value; + value = adapt(dir); + } else if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { + String enumName = (String) value; + value = ((net.minecraft.world.level.block.state.properties.EnumProperty) property) + .getValue(enumName).orElseThrow(() -> + new IllegalStateException( + "Enum property " + property.getName() + " does not contain " + enumName + ) + ); + } + + newState = newState.setValue( + (net.minecraft.world.level.block.state.properties.Property) property, + (Comparable) value + ); + } + return newState; + } + + @Override + public BaseEntity getEntity(org.bukkit.entity.Entity entity) { + checkNotNull(entity); + + CraftEntity craftEntity = ((CraftEntity) entity); + Entity mcEntity = craftEntity.getHandle(); + + // Do not allow creating of passenger entity snapshots, passengers are included in the vehicle entity + if (mcEntity.isPassenger()) { + return null; + } + + String id = getEntityId(mcEntity); + + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + readEntityIntoTag(mcEntity, tag); + return new BaseEntity( + com.sk89q.worldedit.world.entity.EntityTypes.get(id), + LazyReference.from(() -> (CompoundBinaryTag) toNativeBinary(tag)) + ); + } + + @Nullable + @Override + public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) { + checkNotNull(location); + checkNotNull(state); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + ServerLevel worldServer = craftWorld.getHandle(); + + Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle()); + + if (createdEntity != null) { + CompoundBinaryTag nativeTag = state.getNbt(); + if (nativeTag != null) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(nativeTag); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + readTagIntoEntity(tag, createdEntity); + } + + createdEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + worldServer.addFreshEntity(createdEntity, SpawnReason.CUSTOM); + return createdEntity.getBukkitEntity(); + } else { + return null; + } + } + + // This removes all unwanted tags from the main entity and all its passengers + private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) { + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + + // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive + if (tag.contains("Passengers", NBTConstants.TYPE_LIST)) { + net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", NBTConstants.TYPE_COMPOUND); + + for (int i = 0; i < nbttaglist.size(); ++i) { + removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i)); + } + } + } + + @Override + public Component getRichBlockName(BlockType blockType) { + return TranslatableComponent.of(getBlockFromType(blockType).getDescriptionId()); + } + + @Override + public Component getRichItemName(ItemType itemType) { + return TranslatableComponent.of(getItemFromType(itemType).getDescriptionId()); + } + + @Override + public Component getRichItemName(BaseItemStack itemStack) { + return TranslatableComponent.of(CraftItemStack.asNMSCopy(BukkitAdapter.adapt(itemStack)).getDescriptionId()); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader>() { + @Override + public Property load(net.minecraft.world.level.block.state.properties.Property state) throws Exception { + if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { + return new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + } else if (state instanceof DirectionProperty) { + return new DirectionalProperty(state.getName(), + (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).collect(Collectors.toList())); + } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { + return new EnumProperty(state.getName(), + (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).collect(Collectors.toList())); + } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { + return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + } else { + throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName()); + } + } + }); + + @SuppressWarnings({ "rawtypes" }) + @Override + public Map> getProperties(BlockType blockType) { + Map> properties = new TreeMap<>(); + Block block = getBlockFromType(blockType); + StateDefinition blockStateList = + block.getStateDefinition(); + for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { + Property property = PROPERTY_CACHE.getUnchecked(state); + properties.put(property.getName(), property); + } + return properties; + } + + @Override + public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) { + ((CraftPlayer) player).getHandle().connection.send(ClientboundBlockEntityDataPacket.create( + new StructureBlockEntity( + new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()), + Blocks.STRUCTURE_BLOCK.defaultBlockState() + ), + __ -> (net.minecraft.nbt.CompoundTag) fromNativeBinary(nbtData) + )); + } + + @Override + public void sendFakeOP(Player player) { + ((CraftPlayer) player).getHandle().connection.send(new ClientboundEntityEventPacket( + ((CraftPlayer) player).getHandle(), (byte) 28 + )); + } + + @Override + public org.bukkit.inventory.ItemStack adapt(BaseItemStack item) { + ItemStack stack = new ItemStack( + DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM).get(ResourceLocation.tryParse(item.getType().getId())), + item.getAmount() + ); + stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData()))); + return CraftItemStack.asCraftMirror(stack); + } + + @Override + public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { + final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); + final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount()); + weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag()))); + return weStack; + } + + private final LoadingCache fakePlayers + = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new)); + + @Override + public boolean simulateItemUse(org.bukkit.World world, BlockVector3 position, BaseItem item, Direction face) { + CraftWorld craftWorld = (CraftWorld) world; + ServerLevel worldServer = craftWorld.getHandle(); + ItemStack stack = CraftItemStack.asNMSCopy(BukkitAdapter.adapt(item instanceof BaseItemStack + ? ((BaseItemStack) item) : new BaseItemStack(item.getType(), item.getNbtData(), 1))); + stack.setTag((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData())); + + PaperweightFakePlayer fakePlayer; + try { + fakePlayer = fakePlayers.get(worldServer); + } catch (ExecutionException ignored) { + return false; + } + fakePlayer.setItemInHand(InteractionHand.MAIN_HAND, stack); + fakePlayer.absMoveTo(position.getBlockX(), position.getBlockY(), position.getBlockZ(), + (float) face.toVector().toYaw(), (float) face.toVector().toPitch()); + + final BlockPos blockPos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + final Vec3 blockVec = Vec3.atLowerCornerOf(blockPos); + final net.minecraft.core.Direction enumFacing = adapt(face); + BlockHitResult rayTrace = new BlockHitResult(blockVec, enumFacing, blockPos, false); + UseOnContext context = new UseOnContext(fakePlayer, InteractionHand.MAIN_HAND, rayTrace); + InteractionResult result = stack.useOn(context); + if (result != InteractionResult.SUCCESS) { + if (worldServer.getBlockState(blockPos).use(worldServer, fakePlayer, InteractionHand.MAIN_HAND, rayTrace).consumesAction()) { + result = InteractionResult.SUCCESS; + } else { + result = stack.getItem().use(worldServer, fakePlayer, InteractionHand.MAIN_HAND).getResult(); + } + } + + return result == InteractionResult.SUCCESS; + } + + @Override + public boolean canPlaceAt(org.bukkit.World world, BlockVector3 position, BlockState blockState) { + int internalId = BlockStateIdAccess.getBlockStateId(blockState); + net.minecraft.world.level.block.state.BlockState blockData = Block.stateById(internalId); + return blockData.canSurvive(((CraftWorld) world).getHandle(), new BlockPos(position.getX(), position.getY(), position.getZ())); + } + + @Override + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) { + try { + doRegen(bukkitWorld, region, extent, options); + } catch (Exception e) { + throw new IllegalStateException("Regen failed.", e); + } + + return true; + } + + private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) throws Exception { + Environment env = bukkitWorld.getEnvironment(); + ChunkGenerator gen = bukkitWorld.getGenerator(); + + Path tempDir = Files.createTempDirectory("WorldEditWorldGen"); + LevelStorageSource levelStorage = LevelStorageSource.createDefault(tempDir); + ResourceKey worldDimKey = getWorldDimKey(env); + try (LevelStorageSource.LevelStorageAccess session = levelStorage.createAccess("faweregentempworld", worldDimKey)) { + ServerLevel originalWorld = ((CraftWorld) bukkitWorld).getHandle(); + PrimaryLevelData levelProperties = (PrimaryLevelData) originalWorld.getServer() + .getWorldData().overworldData(); + WorldOptions originalOpts = levelProperties.worldGenOptions(); + + long seed = options.getSeed().orElse(originalWorld.getSeed()); + WorldOptions newOpts = options.getSeed().isPresent() + ? originalOpts.withSeed(OptionalLong.of(seed)) + : originalOpts; + + LevelSettings newWorldSettings = new LevelSettings( + "faweregentempworld", + levelProperties.settings.gameType(), + levelProperties.settings.hardcore(), + levelProperties.settings.difficulty(), + levelProperties.settings.allowCommands(), + levelProperties.settings.gameRules(), + levelProperties.settings.getDataConfiguration() + ); + + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = + levelProperties.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : levelProperties.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + + ServerLevel freshWorld = new ServerLevel( + originalWorld.getServer(), + originalWorld.getServer().executor, + session, newWorldData, + originalWorld.dimension(), + new LevelStem( + originalWorld.dimensionTypeRegistration(), + originalWorld.getChunkSource().getGenerator() + ), + new NoOpWorldLoadListener(), + originalWorld.isDebug(), + seed, + ImmutableList.of(), + false, + originalWorld.getRandomSequences(), + env, + gen, + bukkitWorld.getBiomeProvider() + ); + try { + regenForWorld(region, extent, freshWorld, options); + } finally { + freshWorld.getChunkSource().close(false); + } + } finally { + try { + @SuppressWarnings("unchecked") + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("faweregentempworld"); + } catch (IllegalAccessException ignored) { + } + SafeFiles.tryHardToDeleteDir(tempDir); + } + } + + private BiomeType adapt(ServerLevel serverWorld, Biome origBiome) { + ResourceLocation key = serverWorld.registryAccess().registryOrThrow(Registries.BIOME).getKey(origBiome); + if (key == null) { + return null; + } + return BiomeTypes.get(key.toString()); + } + + @SuppressWarnings("unchecked") + private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld, RegenOptions options) throws WorldEditException { + List> chunkLoadings = submitChunkLoadTasks(region, serverWorld); + BlockableEventLoop executor; + try { + executor = (BlockableEventLoop) chunkProviderExecutorField.get(serverWorld.getChunkSource()); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Couldn't get executor for chunk loading.", e); + } + executor.managedBlock(() -> { + // bail out early if a future fails + if (chunkLoadings.stream().anyMatch(ftr -> + ftr.isDone() && Futures.getUnchecked(ftr) == null + )) { + return false; + } + return chunkLoadings.stream().allMatch(CompletableFuture::isDone); + }); + Map chunks = new HashMap<>(); + for (CompletableFuture future : chunkLoadings) { + @Nullable + ChunkAccess chunk = future.getNow(null); + checkState(chunk != null, "Failed to generate a chunk, regen failed."); + chunks.put(chunk.getPos(), chunk); + } + + for (BlockVector3 vec : region) { + BlockPos pos = new BlockPos(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()); + ChunkAccess chunk = chunks.get(new ChunkPos(pos)); + final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(pos); + int internalId = Block.getId(blockData); + BlockStateHolder state = BlockStateIdAccess.getBlockStateById(internalId); + Objects.requireNonNull(state); + BlockEntity blockEntity = chunk.getBlockEntity(pos); + if (blockEntity != null) { + net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(); + state = state.toBaseBlock(((CompoundBinaryTag) toNativeBinary(tag))); + } + extent.setBlock(vec, state.toBaseBlock()); + if (options.shouldRegenBiomes()) { + Biome origBiome = chunk.getNoiseBiome(vec.getX(), vec.getY(), vec.getZ()).value(); + BiomeType adaptedBiome = adapt(serverWorld, origBiome); + if (adaptedBiome != null) { + extent.setBiome(vec, adaptedBiome); + } + } + } + } + + @SuppressWarnings("unchecked") + private List> submitChunkLoadTasks(Region region, ServerLevel serverWorld) { + ServerChunkCache chunkManager = serverWorld.getChunkSource(); + List> chunkLoadings = new ArrayList<>(); + // Pre-gen all the chunks + for (BlockVector2 chunk : region.getChunks()) { + try { + //noinspection unchecked + chunkLoadings.add( + ((CompletableFuture>) + getChunkFutureMethod.invoke(chunkManager, chunk.getX(), chunk.getZ(), ChunkStatus.FEATURES, true)) + .thenApply(either -> either.left().orElse(null)) + ); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException("Couldn't load chunk for regen.", e); + } + } + return chunkLoadings; + } + + private ResourceKey getWorldDimKey(Environment env) { + switch (env) { + case NETHER: + return LevelStem.NETHER; + case THE_END: + return LevelStem.END; + case NORMAL: + default: + return LevelStem.OVERWORLD; + } + } + + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.NEIGHBORS, + SideEffect.LIGHTING, + SideEffect.VALIDATION, + SideEffect.ENTITY_AI, + SideEffect.EVENTS, + SideEffect.UPDATE + ); + + @Override + public Set getSupportedSideEffects() { + return SUPPORTED_SIDE_EFFECTS; + } + + @Override + public boolean clearContainerBlockContents(org.bukkit.World world, BlockVector3 pt) { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + + BlockEntity entity = originalWorld.getBlockEntity(new BlockPos(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ())); + if (entity instanceof Clearable) { + ((Clearable) entity).clearContent(); + return true; + } + return false; + } + + // ------------------------------------------------------------------------ + // Code that is less likely to break + // ------------------------------------------------------------------------ + + /** + * Converts from a non-native NMS NBT structure to a native WorldEdit NBT + * structure. + * + * @param foreign non-native NMS NBT structure + * @return native WorldEdit NBT structure + */ + @Override + public BinaryTag toNativeBinary(net.minecraft.nbt.Tag foreign) { + if (foreign == null) { + return null; + } + if (foreign instanceof net.minecraft.nbt.CompoundTag) { + Map values = new HashMap<>(); + Set foreignKeys = ((net.minecraft.nbt.CompoundTag) foreign).getAllKeys(); + + for (String str : foreignKeys) { + net.minecraft.nbt.Tag base = ((net.minecraft.nbt.CompoundTag) foreign).get(str); + values.put(str, toNativeBinary(base)); + } + return CompoundBinaryTag.from(values); + } else if (foreign instanceof net.minecraft.nbt.ByteTag) { + return ByteBinaryTag.of(((net.minecraft.nbt.ByteTag) foreign).getAsByte()); + } else if (foreign instanceof net.minecraft.nbt.ByteArrayTag) { + return ByteArrayBinaryTag.of(((net.minecraft.nbt.ByteArrayTag) foreign).getAsByteArray()); + } else if (foreign instanceof net.minecraft.nbt.DoubleTag) { + return DoubleBinaryTag.of(((net.minecraft.nbt.DoubleTag) foreign).getAsDouble()); + } else if (foreign instanceof net.minecraft.nbt.FloatTag) { + return FloatBinaryTag.of(((net.minecraft.nbt.FloatTag) foreign).getAsFloat()); + } else if (foreign instanceof net.minecraft.nbt.IntTag) { + return IntBinaryTag.of(((net.minecraft.nbt.IntTag) foreign).getAsInt()); + } else if (foreign instanceof net.minecraft.nbt.IntArrayTag) { + return IntArrayBinaryTag.of(((net.minecraft.nbt.IntArrayTag) foreign).getAsIntArray()); + } else if (foreign instanceof net.minecraft.nbt.LongArrayTag) { + return LongArrayBinaryTag.of(((net.minecraft.nbt.LongArrayTag) foreign).getAsLongArray()); + } else if (foreign instanceof net.minecraft.nbt.ListTag) { + try { + return toNativeList((net.minecraft.nbt.ListTag) foreign); + } catch (Throwable e) { + logger.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e); + return ListBinaryTag.empty(); + } + } else if (foreign instanceof net.minecraft.nbt.LongTag) { + return LongBinaryTag.of(((net.minecraft.nbt.LongTag) foreign).getAsLong()); + } else if (foreign instanceof net.minecraft.nbt.ShortTag) { + return ShortBinaryTag.of(((net.minecraft.nbt.ShortTag) foreign).getAsShort()); + } else if (foreign instanceof net.minecraft.nbt.StringTag) { + return StringBinaryTag.of(foreign.getAsString()); + } else if (foreign instanceof net.minecraft.nbt.EndTag) { + return EndBinaryTag.get(); + } else { + throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); + } + } + + /** + * Convert a foreign NBT list tag into a native WorldEdit one. + * + * @param foreign the foreign tag + * @return the converted tag + * @throws SecurityException on error + * @throws IllegalArgumentException on error + */ + private ListBinaryTag toNativeList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException { + ListBinaryTag.Builder values = ListBinaryTag.builder(); + + for (net.minecraft.nbt.Tag tag : foreign) { + values.add(toNativeBinary(tag)); + } + + return values.build(); + } + + /** + * Converts a WorldEdit-native NBT structure to a NMS structure. + * + * @param foreign structure to convert + * @return non-native structure + */ + @Override + public net.minecraft.nbt.Tag fromNativeBinary(BinaryTag foreign) { + if (foreign == null) { + return null; + } + if (foreign instanceof CompoundBinaryTag) { + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + for (String key : ((CompoundBinaryTag) foreign).keySet()) { + tag.put(key, fromNativeBinary(((CompoundBinaryTag) foreign).get(key))); + } + return tag; + } else if (foreign instanceof ByteBinaryTag) { + return net.minecraft.nbt.ByteTag.valueOf(((ByteBinaryTag) foreign).value()); + } else if (foreign instanceof ByteArrayBinaryTag) { + return new net.minecraft.nbt.ByteArrayTag(((ByteArrayBinaryTag) foreign).value()); + } else if (foreign instanceof DoubleBinaryTag) { + return net.minecraft.nbt.DoubleTag.valueOf(((DoubleBinaryTag) foreign).value()); + } else if (foreign instanceof FloatBinaryTag) { + return net.minecraft.nbt.FloatTag.valueOf(((FloatBinaryTag) foreign).value()); + } else if (foreign instanceof IntBinaryTag) { + return net.minecraft.nbt.IntTag.valueOf(((IntBinaryTag) foreign).value()); + } else if (foreign instanceof IntArrayBinaryTag) { + return new net.minecraft.nbt.IntArrayTag(((IntArrayBinaryTag) foreign).value()); + } else if (foreign instanceof LongArrayBinaryTag) { + return new net.minecraft.nbt.LongArrayTag(((LongArrayBinaryTag) foreign).value()); + } else if (foreign instanceof ListBinaryTag) { + net.minecraft.nbt.ListTag tag = new net.minecraft.nbt.ListTag(); + ListBinaryTag foreignList = (ListBinaryTag) foreign; + for (BinaryTag t : foreignList) { + tag.add(fromNativeBinary(t)); + } + return tag; + } else if (foreign instanceof LongBinaryTag) { + return net.minecraft.nbt.LongTag.valueOf(((LongBinaryTag) foreign).value()); + } else if (foreign instanceof ShortBinaryTag) { + return net.minecraft.nbt.ShortTag.valueOf(((ShortBinaryTag) foreign).value()); + } else if (foreign instanceof StringBinaryTag) { + return net.minecraft.nbt.StringTag.valueOf(((StringBinaryTag) foreign).value()); + } else if (foreign instanceof EndBinaryTag) { + return net.minecraft.nbt.EndTag.INSTANCE; + } else { + throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName()); + } + } + + @Override + public boolean supportsWatchdog() { + return watchdog != null; + } + + @Override + public void tickWatchdog() { + watchdog.tick(); + } + + private class SpigotWatchdog implements Watchdog { + private final Field instanceField; + private final Field lastTickField; + + SpigotWatchdog() throws NoSuchFieldException { + Field instanceField = WatchdogThread.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + this.instanceField = instanceField; + + Field lastTickField = WatchdogThread.class.getDeclaredField("lastTick"); + lastTickField.setAccessible(true); + this.lastTickField = lastTickField; + } + + @Override + public void tick() { + try { + WatchdogThread instance = (WatchdogThread) this.instanceField.get(null); + if ((long) lastTickField.get(instance) != 0) { + WatchdogThread.tick(); + } + } catch (IllegalAccessException e) { + logger.log(Level.WARNING, "Failed to tick watchdog", e); + } + } + } + + private static class MojangWatchdog implements Watchdog { + private final DedicatedServer server; + private final Field tickField; + + MojangWatchdog(DedicatedServer server) throws NoSuchFieldException { + this.server = server; + Field tickField = MinecraftServer.class.getDeclaredField( + Refraction.pickName("nextTickTime", "ah") + ); + if (tickField.getType() != long.class) { + throw new IllegalStateException("nextTickTime is not a long field, mapping is likely incorrect"); + } + tickField.setAccessible(true); + this.tickField = tickField; + } + + @Override + public void tick() { + try { + tickField.set(server, Util.getMillis()); + } catch (IllegalAccessException ignored) { + } + } + } + + private static class NoOpWorldLoadListener implements ChunkProgressListener { + @Override + public void updateSpawnPos(ChunkPos spawnPos) { + } + + @Override + public void onStatusChange(ChunkPos pos, @org.jetbrains.annotations.Nullable ChunkStatus status) { + } + + @Override + public void start() { + } + + @Override + public void stop() { + } + + @Override + public void setChunkRadius(int radius) { + } + } +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightDataConverters.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightDataConverters.java new file mode 100644 index 000000000..175174b75 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightDataConverters.java @@ -0,0 +1,2801 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DSL.TypeReference; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import net.minecraft.core.Direction; +import net.minecraft.nbt.NbtOps; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.GsonHelper; +import net.minecraft.util.StringUtil; +import net.minecraft.util.datafix.DataFixers; +import net.minecraft.util.datafix.fixes.References; +import net.minecraft.world.item.DyeColor; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +/** + * Handles converting all Pre 1.13.2 data using the Legacy DataFix System (ported to 1.13.2) + * + * We register a DFU Fixer per Legacy Data Version and apply the fixes using legacy strategy + * which is safer, faster and cleaner code. + * + * The pre DFU code did not fail when the Source version was unknown. + * + * This class also provides util methods for converting compounds to wrap the update call to + * receive the source version in the compound + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer { + + //FAWE start - BinaryTag + @SuppressWarnings("unchecked") + @Override + public T fixUp(FixType type, T original, int srcVer) { + if (type == FixTypes.CHUNK) { + return (T) fixChunk((CompoundBinaryTag) original, srcVer); + } else if (type == FixTypes.BLOCK_ENTITY) { + return (T) fixBlockEntity((CompoundBinaryTag) original, srcVer); + } else if (type == FixTypes.ENTITY) { + return (T) fixEntity((CompoundBinaryTag) original, srcVer); + } else if (type == FixTypes.BLOCK_STATE) { + return (T) fixBlockState((String) original, srcVer); + } else if (type == FixTypes.ITEM_TYPE) { + return (T) fixItemType((String) original, srcVer); + } else if (type == FixTypes.BIOME) { + return (T) fixBiome((String) original, srcVer); + } + return original; + } + + private CompoundBinaryTag fixChunk(CompoundBinaryTag originalChunk, int srcVer) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(originalChunk); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.CHUNK, tag, srcVer); + return (CompoundBinaryTag) adapter.toNativeBinary(fixed); + } + + private CompoundBinaryTag fixBlockEntity(CompoundBinaryTag origTileEnt, int srcVer) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origTileEnt); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer); + return (CompoundBinaryTag) adapter.toNativeBinary(fixed); + } + + private CompoundBinaryTag fixEntity(CompoundBinaryTag origEnt, int srcVer) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origEnt); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.ENTITY, tag, srcVer); + return (CompoundBinaryTag) adapter.toNativeBinary(fixed); + } + //FAWE end + + private String fixBlockState(String blockState, int srcVer) { + net.minecraft.nbt.CompoundTag stateNBT = stateToNBT(blockState); + Dynamic dynamic = new Dynamic<>(OPS_NBT, stateNBT); + net.minecraft.nbt.CompoundTag fixed = (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update(References.BLOCK_STATE, dynamic, srcVer, DATA_VERSION).getValue(); + return nbtToState(fixed); + } + + private String nbtToState(net.minecraft.nbt.CompoundTag tagCompound) { + StringBuilder sb = new StringBuilder(); + sb.append(tagCompound.getString("Name")); + if (tagCompound.contains("Properties", 10)) { + sb.append('['); + net.minecraft.nbt.CompoundTag props = tagCompound.getCompound("Properties"); + sb.append(props.getAllKeys().stream().map(k -> k + "=" + props.getString(k).replace("\"", "")).collect(Collectors.joining(","))); + sb.append(']'); + } + return sb.toString(); + } + + private static net.minecraft.nbt.CompoundTag stateToNBT(String blockState) { + int propIdx = blockState.indexOf('['); + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + if (propIdx < 0) { + tag.putString("Name", blockState); + } else { + tag.putString("Name", blockState.substring(0, propIdx)); + net.minecraft.nbt.CompoundTag propTag = new net.minecraft.nbt.CompoundTag(); + String props = blockState.substring(propIdx + 1, blockState.length() - 1); + String[] propArr = props.split(","); + for (String pair : propArr) { + final String[] split = pair.split("="); + propTag.putString(split[0], split[1]); + } + tag.put("Properties", propTag); + } + return tag; + } + + private String fixBiome(String key, int srcVer) { + return fixName(key, srcVer, References.BIOME); + } + + private String fixItemType(String key, int srcVer) { + return fixName(key, srcVer, References.ITEM_NAME); + } + + private static String fixName(String key, int srcVer, TypeReference type) { + return INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, net.minecraft.nbt.StringTag.valueOf(key)), srcVer, DATA_VERSION) + .getValue().getAsString(); + } + + private final PaperweightAdapter adapter; + + private static final NbtOps OPS_NBT = NbtOps.INSTANCE; + private static final int LEGACY_VERSION = 1343; + private static int DATA_VERSION; + public static PaperweightDataConverters INSTANCE; + + private final Map> converters = new EnumMap<>(LegacyType.class); + private final Map> inspectors = new EnumMap<>(LegacyType.class); + + // Set on build + private DataFixer fixer; + private static final Map DFU_TO_LEGACY = new HashMap<>(); + + public enum LegacyType { + LEVEL(References.LEVEL), + PLAYER(References.PLAYER), + CHUNK(References.CHUNK), + BLOCK_ENTITY(References.BLOCK_ENTITY), + ENTITY(References.ENTITY), + ITEM_INSTANCE(References.ITEM_STACK), + OPTIONS(References.OPTIONS), + STRUCTURE(References.STRUCTURE); + + private final TypeReference type; + + LegacyType(TypeReference type) { + this.type = type; + DFU_TO_LEGACY.put(type.typeName(), this); + } + + public TypeReference getDFUType() { + return type; + } + } + + public PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) { + super(dataVersion); + DATA_VERSION = dataVersion; + INSTANCE = this; + this.adapter = adapter; + registerConverters(); + registerInspectors(); + } + + + // Called after fixers are built and ready for FIXING + @Override + public DataFixer buildUnoptimized() { + return this.fixer = new WrappedDataFixer(DataFixers.getDataFixer()); + } + + @Override + public DataFixer buildOptimized(final Set requiredTypes, Executor executor) { + return buildUnoptimized(); + } + + @SuppressWarnings("unchecked") + private class WrappedDataFixer implements DataFixer { + private final DataFixer realFixer; + + WrappedDataFixer(DataFixer realFixer) { + this.realFixer = realFixer; + } + + @Override + public Dynamic update(TypeReference type, Dynamic dynamic, int sourceVer, int targetVer) { + LegacyType legacyType = DFU_TO_LEGACY.get(type.typeName()); + if (sourceVer < LEGACY_VERSION && legacyType != null) { + net.minecraft.nbt.CompoundTag cmp = (net.minecraft.nbt.CompoundTag) dynamic.getValue(); + int desiredVersion = Math.min(targetVer, LEGACY_VERSION); + + cmp = convert(legacyType, cmp, sourceVer, desiredVersion); + sourceVer = desiredVersion; + dynamic = new Dynamic(OPS_NBT, cmp); + } + return realFixer.update(type, dynamic, sourceVer, targetVer); + } + + private net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int desiredVersion) { + List converters = PaperweightDataConverters.this.converters.get(type); + if (converters != null && !converters.isEmpty()) { + for (DataConverter converter : converters) { + int dataVersion = converter.getDataVersion(); + if (dataVersion > sourceVer && dataVersion <= desiredVersion) { + cmp = converter.convert(cmp); + } + } + } + + List inspectors = PaperweightDataConverters.this.inspectors.get(type); + if (inspectors != null && !inspectors.isEmpty()) { + for (DataInspector inspector : inspectors) { + cmp = inspector.inspect(cmp, sourceVer, desiredVersion); + } + } + + return cmp; + } + + @Override + public Schema getSchema(int i) { + return realFixer.getSchema(i); + } + } + + public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp) { + return convert(type.getDFUType(), cmp); + } + + public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) { + return convert(type.getDFUType(), cmp, sourceVer); + } + + public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + return convert(type.getDFUType(), cmp, sourceVer, targetVer); + } + + public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp) { + int i = cmp.contains("DataVersion", 99) ? cmp.getInt("DataVersion") : -1; + return convert(type, cmp, i); + } + + public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) { + return convert(type, cmp, sourceVer, DATA_VERSION); + } + + public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (sourceVer >= targetVer) { + return cmp; + } + return (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, cmp), sourceVer, targetVer).getValue(); + } + + + public interface DataInspector { + net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer); + } + + public interface DataConverter { + + int getDataVersion(); + + net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp); + } + + + private void registerInspector(LegacyType type, DataInspector inspector) { + this.inspectors.computeIfAbsent(type, k -> new ArrayList<>()).add(inspector); + } + + private void registerConverter(LegacyType type, DataConverter converter) { + int version = converter.getDataVersion(); + + List list = this.converters.computeIfAbsent(type, k -> new ArrayList<>()); + if (!list.isEmpty() && list.get(list.size() - 1).getDataVersion() > version) { + for (int j = 0; j < list.size(); ++j) { + if (list.get(j).getDataVersion() > version) { + list.add(j, converter); + break; + } + } + } else { + list.add(converter); + } + } + + private void registerInspectors() { + registerEntityItemList("EntityHorseDonkey", "SaddleItem", "Items"); + registerEntityItemList("EntityHorseMule", "Items"); + registerEntityItemList("EntityMinecartChest", "Items"); + registerEntityItemList("EntityMinecartHopper", "Items"); + registerEntityItemList("EntityVillager", "Inventory"); + registerEntityItemListEquipment("EntityArmorStand"); + registerEntityItemListEquipment("EntityBat"); + registerEntityItemListEquipment("EntityBlaze"); + registerEntityItemListEquipment("EntityCaveSpider"); + registerEntityItemListEquipment("EntityChicken"); + registerEntityItemListEquipment("EntityCow"); + registerEntityItemListEquipment("EntityCreeper"); + registerEntityItemListEquipment("EntityEnderDragon"); + registerEntityItemListEquipment("EntityEnderman"); + registerEntityItemListEquipment("EntityEndermite"); + registerEntityItemListEquipment("EntityEvoker"); + registerEntityItemListEquipment("EntityGhast"); + registerEntityItemListEquipment("EntityGiantZombie"); + registerEntityItemListEquipment("EntityGuardian"); + registerEntityItemListEquipment("EntityGuardianElder"); + registerEntityItemListEquipment("EntityHorse"); + registerEntityItemListEquipment("EntityHorseDonkey"); + registerEntityItemListEquipment("EntityHorseMule"); + registerEntityItemListEquipment("EntityHorseSkeleton"); + registerEntityItemListEquipment("EntityHorseZombie"); + registerEntityItemListEquipment("EntityIronGolem"); + registerEntityItemListEquipment("EntityMagmaCube"); + registerEntityItemListEquipment("EntityMushroomCow"); + registerEntityItemListEquipment("EntityOcelot"); + registerEntityItemListEquipment("EntityPig"); + registerEntityItemListEquipment("EntityPigZombie"); + registerEntityItemListEquipment("EntityRabbit"); + registerEntityItemListEquipment("EntitySheep"); + registerEntityItemListEquipment("EntityShulker"); + registerEntityItemListEquipment("EntitySilverfish"); + registerEntityItemListEquipment("EntitySkeleton"); + registerEntityItemListEquipment("EntitySkeletonStray"); + registerEntityItemListEquipment("EntitySkeletonWither"); + registerEntityItemListEquipment("EntitySlime"); + registerEntityItemListEquipment("EntitySnowman"); + registerEntityItemListEquipment("EntitySpider"); + registerEntityItemListEquipment("EntitySquid"); + registerEntityItemListEquipment("EntityVex"); + registerEntityItemListEquipment("EntityVillager"); + registerEntityItemListEquipment("EntityVindicator"); + registerEntityItemListEquipment("EntityWitch"); + registerEntityItemListEquipment("EntityWither"); + registerEntityItemListEquipment("EntityWolf"); + registerEntityItemListEquipment("EntityZombie"); + registerEntityItemListEquipment("EntityZombieHusk"); + registerEntityItemListEquipment("EntityZombieVillager"); + registerEntityItemSingle("EntityFireworks", "FireworksItem"); + registerEntityItemSingle("EntityHorse", "ArmorItem"); + registerEntityItemSingle("EntityHorse", "SaddleItem"); + registerEntityItemSingle("EntityHorseMule", "SaddleItem"); + registerEntityItemSingle("EntityHorseSkeleton", "SaddleItem"); + registerEntityItemSingle("EntityHorseZombie", "SaddleItem"); + registerEntityItemSingle("EntityItem", "Item"); + registerEntityItemSingle("EntityItemFrame", "Item"); + registerEntityItemSingle("EntityPotion", "Potion"); + + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItem("TileEntityRecordPlayer", "RecordItem")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityBrewingStand", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityChest", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDispenser", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDropper", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityFurnace", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityHopper", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityShulkerBox", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorMobSpawnerMobs()); + registerInspector(LegacyType.CHUNK, new DataInspectorChunks()); + registerInspector(LegacyType.ENTITY, new DataInspectorCommandBlock()); + registerInspector(LegacyType.ENTITY, new DataInspectorEntityPassengers()); + registerInspector(LegacyType.ENTITY, new DataInspectorMobSpawnerMinecart()); + registerInspector(LegacyType.ENTITY, new DataInspectorVillagers()); + registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorBlockEntity()); + registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorEntity()); + registerInspector(LegacyType.LEVEL, new DataInspectorLevelPlayer()); + registerInspector(LegacyType.PLAYER, new DataInspectorPlayer()); + registerInspector(LegacyType.PLAYER, new DataInspectorPlayerVehicle()); + registerInspector(LegacyType.STRUCTURE, new DataInspectorStructure()); + } + + private void registerConverters() { + registerConverter(LegacyType.ENTITY, new DataConverterEquipment()); + registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterSignText()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterMaterialId()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionId()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterSpawnEgg()); + registerConverter(LegacyType.ENTITY, new DataConverterMinecart()); + registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterMobSpawner()); + registerConverter(LegacyType.ENTITY, new DataConverterUUID()); + registerConverter(LegacyType.ENTITY, new DataConverterHealth()); + registerConverter(LegacyType.ENTITY, new DataConverterSaddle()); + registerConverter(LegacyType.ENTITY, new DataConverterHanging()); + registerConverter(LegacyType.ENTITY, new DataConverterDropChances()); + registerConverter(LegacyType.ENTITY, new DataConverterRiding()); + registerConverter(LegacyType.ENTITY, new DataConverterArmorStand()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBook()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterCookedFish()); + registerConverter(LegacyType.ENTITY, new DataConverterZombie()); + registerConverter(LegacyType.OPTIONS, new DataConverterVBO()); + registerConverter(LegacyType.ENTITY, new DataConverterGuardian()); + registerConverter(LegacyType.ENTITY, new DataConverterSkeleton()); + registerConverter(LegacyType.ENTITY, new DataConverterZombieType()); + registerConverter(LegacyType.ENTITY, new DataConverterHorse()); + registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterTileEntity()); + registerConverter(LegacyType.ENTITY, new DataConverterEntity()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBanner()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionWater()); + registerConverter(LegacyType.ENTITY, new DataConverterShulker()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterShulkerBoxItem()); + registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterShulkerBoxBlock()); + registerConverter(LegacyType.OPTIONS, new DataConverterLang()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterTotem()); + registerConverter(LegacyType.CHUNK, new DataConverterBedBlock()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBedItem()); + } + + private void registerEntityItemList(String type, String... keys) { + registerInspector(LegacyType.ENTITY, new DataInspectorItemList(type, keys)); + } + + private void registerEntityItemSingle(String type, String key) { + registerInspector(LegacyType.ENTITY, new DataInspectorItem(type, key)); + } + + private void registerEntityItemListEquipment(String type) { + registerEntityItemList(type, "ArmorItems", "HandItems"); + } + + private static final Map OLD_ID_TO_KEY_MAP = new HashMap<>(); + + static { + final Map map = OLD_ID_TO_KEY_MAP; + map.put("EntityItem", new ResourceLocation("item")); + map.put("EntityExperienceOrb", new ResourceLocation("xp_orb")); + map.put("EntityAreaEffectCloud", new ResourceLocation("area_effect_cloud")); + map.put("EntityGuardianElder", new ResourceLocation("elder_guardian")); + map.put("EntitySkeletonWither", new ResourceLocation("wither_skeleton")); + map.put("EntitySkeletonStray", new ResourceLocation("stray")); + map.put("EntityEgg", new ResourceLocation("egg")); + map.put("EntityLeash", new ResourceLocation("leash_knot")); + map.put("EntityPainting", new ResourceLocation("painting")); + map.put("EntityTippedArrow", new ResourceLocation("arrow")); + map.put("EntitySnowball", new ResourceLocation("snowball")); + map.put("EntityLargeFireball", new ResourceLocation("fireball")); + map.put("EntitySmallFireball", new ResourceLocation("small_fireball")); + map.put("EntityEnderPearl", new ResourceLocation("ender_pearl")); + map.put("EntityEnderSignal", new ResourceLocation("eye_of_ender_signal")); + map.put("EntityPotion", new ResourceLocation("potion")); + map.put("EntityThrownExpBottle", new ResourceLocation("xp_bottle")); + map.put("EntityItemFrame", new ResourceLocation("item_frame")); + map.put("EntityWitherSkull", new ResourceLocation("wither_skull")); + map.put("EntityTNTPrimed", new ResourceLocation("tnt")); + map.put("EntityFallingBlock", new ResourceLocation("falling_block")); + map.put("EntityFireworks", new ResourceLocation("fireworks_rocket")); + map.put("EntityZombieHusk", new ResourceLocation("husk")); + map.put("EntitySpectralArrow", new ResourceLocation("spectral_arrow")); + map.put("EntityShulkerBullet", new ResourceLocation("shulker_bullet")); + map.put("EntityDragonFireball", new ResourceLocation("dragon_fireball")); + map.put("EntityZombieVillager", new ResourceLocation("zombie_villager")); + map.put("EntityHorseSkeleton", new ResourceLocation("skeleton_horse")); + map.put("EntityHorseZombie", new ResourceLocation("zombie_horse")); + map.put("EntityArmorStand", new ResourceLocation("armor_stand")); + map.put("EntityHorseDonkey", new ResourceLocation("donkey")); + map.put("EntityHorseMule", new ResourceLocation("mule")); + map.put("EntityEvokerFangs", new ResourceLocation("evocation_fangs")); + map.put("EntityEvoker", new ResourceLocation("evocation_illager")); + map.put("EntityVex", new ResourceLocation("vex")); + map.put("EntityVindicator", new ResourceLocation("vindication_illager")); + map.put("EntityIllagerIllusioner", new ResourceLocation("illusion_illager")); + map.put("EntityMinecartCommandBlock", new ResourceLocation("commandblock_minecart")); + map.put("EntityBoat", new ResourceLocation("boat")); + map.put("EntityMinecartRideable", new ResourceLocation("minecart")); + map.put("EntityMinecartChest", new ResourceLocation("chest_minecart")); + map.put("EntityMinecartFurnace", new ResourceLocation("furnace_minecart")); + map.put("EntityMinecartTNT", new ResourceLocation("tnt_minecart")); + map.put("EntityMinecartHopper", new ResourceLocation("hopper_minecart")); + map.put("EntityMinecartMobSpawner", new ResourceLocation("spawner_minecart")); + map.put("EntityCreeper", new ResourceLocation("creeper")); + map.put("EntitySkeleton", new ResourceLocation("skeleton")); + map.put("EntitySpider", new ResourceLocation("spider")); + map.put("EntityGiantZombie", new ResourceLocation("giant")); + map.put("EntityZombie", new ResourceLocation("zombie")); + map.put("EntitySlime", new ResourceLocation("slime")); + map.put("EntityGhast", new ResourceLocation("ghast")); + map.put("EntityPigZombie", new ResourceLocation("zombie_pigman")); + map.put("EntityEnderman", new ResourceLocation("enderman")); + map.put("EntityCaveSpider", new ResourceLocation("cave_spider")); + map.put("EntitySilverfish", new ResourceLocation("silverfish")); + map.put("EntityBlaze", new ResourceLocation("blaze")); + map.put("EntityMagmaCube", new ResourceLocation("magma_cube")); + map.put("EntityEnderDragon", new ResourceLocation("ender_dragon")); + map.put("EntityWither", new ResourceLocation("wither")); + map.put("EntityBat", new ResourceLocation("bat")); + map.put("EntityWitch", new ResourceLocation("witch")); + map.put("EntityEndermite", new ResourceLocation("endermite")); + map.put("EntityGuardian", new ResourceLocation("guardian")); + map.put("EntityShulker", new ResourceLocation("shulker")); + map.put("EntityPig", new ResourceLocation("pig")); + map.put("EntitySheep", new ResourceLocation("sheep")); + map.put("EntityCow", new ResourceLocation("cow")); + map.put("EntityChicken", new ResourceLocation("chicken")); + map.put("EntitySquid", new ResourceLocation("squid")); + map.put("EntityWolf", new ResourceLocation("wolf")); + map.put("EntityMushroomCow", new ResourceLocation("mooshroom")); + map.put("EntitySnowman", new ResourceLocation("snowman")); + map.put("EntityOcelot", new ResourceLocation("ocelot")); + map.put("EntityIronGolem", new ResourceLocation("villager_golem")); + map.put("EntityHorse", new ResourceLocation("horse")); + map.put("EntityRabbit", new ResourceLocation("rabbit")); + map.put("EntityPolarBear", new ResourceLocation("polar_bear")); + map.put("EntityLlama", new ResourceLocation("llama")); + map.put("EntityLlamaSpit", new ResourceLocation("llama_spit")); + map.put("EntityParrot", new ResourceLocation("parrot")); + map.put("EntityVillager", new ResourceLocation("villager")); + map.put("EntityEnderCrystal", new ResourceLocation("ender_crystal")); + map.put("TileEntityFurnace", new ResourceLocation("furnace")); + map.put("TileEntityChest", new ResourceLocation("chest")); + map.put("TileEntityEnderChest", new ResourceLocation("ender_chest")); + map.put("TileEntityRecordPlayer", new ResourceLocation("jukebox")); + map.put("TileEntityDispenser", new ResourceLocation("dispenser")); + map.put("TileEntityDropper", new ResourceLocation("dropper")); + map.put("TileEntitySign", new ResourceLocation("sign")); + map.put("TileEntityMobSpawner", new ResourceLocation("mob_spawner")); + map.put("TileEntityNote", new ResourceLocation("noteblock")); + map.put("TileEntityPiston", new ResourceLocation("piston")); + map.put("TileEntityBrewingStand", new ResourceLocation("brewing_stand")); + map.put("TileEntityEnchantTable", new ResourceLocation("enchanting_table")); + map.put("TileEntityEnderPortal", new ResourceLocation("end_portal")); + map.put("TileEntityBeacon", new ResourceLocation("beacon")); + map.put("TileEntitySkull", new ResourceLocation("skull")); + map.put("TileEntityLightDetector", new ResourceLocation("daylight_detector")); + map.put("TileEntityHopper", new ResourceLocation("hopper")); + map.put("TileEntityComparator", new ResourceLocation("comparator")); + map.put("TileEntityFlowerPot", new ResourceLocation("flower_pot")); + map.put("TileEntityBanner", new ResourceLocation("banner")); + map.put("TileEntityStructure", new ResourceLocation("structure_block")); + map.put("TileEntityEndGateway", new ResourceLocation("end_gateway")); + map.put("TileEntityCommand", new ResourceLocation("command_block")); + map.put("TileEntityShulkerBox", new ResourceLocation("shulker_box")); + map.put("TileEntityBed", new ResourceLocation("bed")); + } + + private static ResourceLocation getKey(String type) { + final ResourceLocation key = OLD_ID_TO_KEY_MAP.get(type); + if (key == null) { + throw new IllegalArgumentException("Unknown mapping for " + type); + } + return key; + } + + private static void convertCompound(LegacyType type, net.minecraft.nbt.CompoundTag cmp, String key, int sourceVer, int targetVer) { + cmp.put(key, convert(type, cmp.getCompound(key), sourceVer, targetVer)); + } + + private static void convertItem(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) { + if (nbttagcompound.contains(key, 10)) { + convertCompound(LegacyType.ITEM_INSTANCE, nbttagcompound, key, sourceVer, targetVer); + } + } + + private static void convertItems(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) { + if (nbttagcompound.contains(key, 9)) { + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound.getList(key, 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ITEM_INSTANCE, nbttaglist.getCompound(j), sourceVer, targetVer)); + } + } + + } + + private static class DataConverterEquipment implements DataConverter { + + DataConverterEquipment() { + } + + public int getDataVersion() { + return 100; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + net.minecraft.nbt.ListTag nbttaglist = cmp.getList("Equipment", 10); + net.minecraft.nbt.ListTag nbttaglist1; + + if (!nbttaglist.isEmpty() && !cmp.contains("HandItems", 10)) { + nbttaglist1 = new net.minecraft.nbt.ListTag(); + nbttaglist1.add(nbttaglist.get(0)); + nbttaglist1.add(new net.minecraft.nbt.CompoundTag()); + cmp.put("HandItems", nbttaglist1); + } + + if (nbttaglist.size() > 1 && !cmp.contains("ArmorItem", 10)) { + nbttaglist1 = new net.minecraft.nbt.ListTag(); + nbttaglist1.add(nbttaglist.get(1)); + nbttaglist1.add(nbttaglist.get(2)); + nbttaglist1.add(nbttaglist.get(3)); + nbttaglist1.add(nbttaglist.get(4)); + cmp.put("ArmorItems", nbttaglist1); + } + + cmp.remove("Equipment"); + if (cmp.contains("DropChances", 9)) { + nbttaglist1 = cmp.getList("DropChances", 5); + net.minecraft.nbt.ListTag nbttaglist2; + + if (!cmp.contains("HandDropChances", 10)) { + nbttaglist2 = new net.minecraft.nbt.ListTag(); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(0))); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(0.0F)); + cmp.put("HandDropChances", nbttaglist2); + } + + if (!cmp.contains("ArmorDropChances", 10)) { + nbttaglist2 = new net.minecraft.nbt.ListTag(); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(1))); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(2))); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(3))); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(4))); + cmp.put("ArmorDropChances", nbttaglist2); + } + + cmp.remove("DropChances"); + } + + return cmp; + } + } + + private static class DataInspectorBlockEntity implements DataInspector { + + private static final Map b = Maps.newHashMap(); + private static final Map c = Maps.newHashMap(); + + DataInspectorBlockEntity() { + } + + @Nullable + private static String convertEntityId(int i, String s) { + String key = new ResourceLocation(s).toString(); + if (i < 515 && DataInspectorBlockEntity.b.containsKey(key)) { + return DataInspectorBlockEntity.b.get(key); + } else { + return DataInspectorBlockEntity.c.get(key); + } + } + + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (!cmp.contains("tag", 10)) { + return cmp; + } else { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); + String s = cmp.getString("id"); + String s1 = convertEntityId(sourceVer, s); + boolean flag; + + if (s1 == null) { + // CraftBukkit - Remove unnecessary warning (occurs when deserializing a Shulker Box item) + // DataInspectorBlockEntity.a.warn("Unable to resolve BlockEntity for ItemInstance: {}", s); + flag = false; + } else { + flag = !nbttagcompound2.contains("id"); + nbttagcompound2.putString("id", s1); + } + + convert(LegacyType.BLOCK_ENTITY, nbttagcompound2, sourceVer, targetVer); + if (flag) { + nbttagcompound2.remove("id"); + } + } + + return cmp; + } + } + + static { + Map map = DataInspectorBlockEntity.b; + + map.put("minecraft:furnace", "Furnace"); + map.put("minecraft:lit_furnace", "Furnace"); + map.put("minecraft:chest", "Chest"); + map.put("minecraft:trapped_chest", "Chest"); + map.put("minecraft:ender_chest", "EnderChest"); + map.put("minecraft:jukebox", "RecordPlayer"); + map.put("minecraft:dispenser", "Trap"); + map.put("minecraft:dropper", "Dropper"); + map.put("minecraft:sign", "Sign"); + map.put("minecraft:mob_spawner", "MobSpawner"); + map.put("minecraft:noteblock", "Music"); + map.put("minecraft:brewing_stand", "Cauldron"); + map.put("minecraft:enhanting_table", "EnchantTable"); + map.put("minecraft:command_block", "CommandBlock"); + map.put("minecraft:beacon", "Beacon"); + map.put("minecraft:skull", "Skull"); + map.put("minecraft:daylight_detector", "DLDetector"); + map.put("minecraft:hopper", "Hopper"); + map.put("minecraft:banner", "Banner"); + map.put("minecraft:flower_pot", "FlowerPot"); + map.put("minecraft:repeating_command_block", "CommandBlock"); + map.put("minecraft:chain_command_block", "CommandBlock"); + map.put("minecraft:standing_sign", "Sign"); + map.put("minecraft:wall_sign", "Sign"); + map.put("minecraft:piston_head", "Piston"); + map.put("minecraft:daylight_detector_inverted", "DLDetector"); + map.put("minecraft:unpowered_comparator", "Comparator"); + map.put("minecraft:powered_comparator", "Comparator"); + map.put("minecraft:wall_banner", "Banner"); + map.put("minecraft:standing_banner", "Banner"); + map.put("minecraft:structure_block", "Structure"); + map.put("minecraft:end_portal", "Airportal"); + map.put("minecraft:end_gateway", "EndGateway"); + map.put("minecraft:shield", "Shield"); + map = DataInspectorBlockEntity.c; + map.put("minecraft:furnace", "minecraft:furnace"); + map.put("minecraft:lit_furnace", "minecraft:furnace"); + map.put("minecraft:chest", "minecraft:chest"); + map.put("minecraft:trapped_chest", "minecraft:chest"); + map.put("minecraft:ender_chest", "minecraft:enderchest"); + map.put("minecraft:jukebox", "minecraft:jukebox"); + map.put("minecraft:dispenser", "minecraft:dispenser"); + map.put("minecraft:dropper", "minecraft:dropper"); + map.put("minecraft:sign", "minecraft:sign"); + map.put("minecraft:mob_spawner", "minecraft:mob_spawner"); + map.put("minecraft:noteblock", "minecraft:noteblock"); + map.put("minecraft:brewing_stand", "minecraft:brewing_stand"); + map.put("minecraft:enhanting_table", "minecraft:enchanting_table"); + map.put("minecraft:command_block", "minecraft:command_block"); + map.put("minecraft:beacon", "minecraft:beacon"); + map.put("minecraft:skull", "minecraft:skull"); + map.put("minecraft:daylight_detector", "minecraft:daylight_detector"); + map.put("minecraft:hopper", "minecraft:hopper"); + map.put("minecraft:banner", "minecraft:banner"); + map.put("minecraft:flower_pot", "minecraft:flower_pot"); + map.put("minecraft:repeating_command_block", "minecraft:command_block"); + map.put("minecraft:chain_command_block", "minecraft:command_block"); + map.put("minecraft:shulker_box", "minecraft:shulker_box"); + map.put("minecraft:white_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:orange_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:magenta_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:light_blue_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:yellow_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:lime_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:pink_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:gray_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:silver_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:cyan_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:purple_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:blue_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:brown_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:green_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:red_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:black_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:bed", "minecraft:bed"); + map.put("minecraft:standing_sign", "minecraft:sign"); + map.put("minecraft:wall_sign", "minecraft:sign"); + map.put("minecraft:piston_head", "minecraft:piston"); + map.put("minecraft:daylight_detector_inverted", "minecraft:daylight_detector"); + map.put("minecraft:unpowered_comparator", "minecraft:comparator"); + map.put("minecraft:powered_comparator", "minecraft:comparator"); + map.put("minecraft:wall_banner", "minecraft:banner"); + map.put("minecraft:standing_banner", "minecraft:banner"); + map.put("minecraft:structure_block", "minecraft:structure_block"); + map.put("minecraft:end_portal", "minecraft:end_portal"); + map.put("minecraft:end_gateway", "minecraft:end_gateway"); + map.put("minecraft:shield", "minecraft:shield"); + } + } + + private static class DataInspectorEntity implements DataInspector { + + DataInspectorEntity() { + } + + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("EntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag"); + String s = cmp.getString("id"); + String s1; + + if ("minecraft:armor_stand".equals(s)) { + s1 = sourceVer < 515 ? "ArmorStand" : "minecraft:armor_stand"; + } else { + if (!"minecraft:spawn_egg".equals(s)) { + return cmp; + } + + s1 = nbttagcompound2.getString("id"); + } + + boolean flag; + + flag = !nbttagcompound2.contains("id", 8); + nbttagcompound2.putString("id", s1); + + convert(LegacyType.ENTITY, nbttagcompound2, sourceVer, targetVer); + if (flag) { + nbttagcompound2.remove("id"); + } + } + + return cmp; + } + } + + + private abstract static class DataInspectorTagged implements DataInspector { + + private final ResourceLocation key; + + DataInspectorTagged(String type) { + this.key = getKey(type); + } + + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (this.key.equals(new ResourceLocation(cmp.getString("id")))) { + cmp = this.inspectChecked(cmp, sourceVer, targetVer); + } + + return cmp; + } + + abstract net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer); + } + + private static class DataInspectorItemList extends DataInspectorTagged { + + private final String[] keys; + + DataInspectorItemList(String oclass, String... astring) { + super(oclass); + this.keys = astring; + } + + net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) { + for (String s : this.keys) { + PaperweightDataConverters.convertItems(nbttagcompound, s, sourceVer, targetVer); + } + + return nbttagcompound; + } + } + + private static class DataInspectorItem extends DataInspectorTagged { + + private final String[] keys; + + DataInspectorItem(String oclass, String... astring) { + super(oclass); + this.keys = astring; + } + + net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) { + for (String key : this.keys) { + PaperweightDataConverters.convertItem(nbttagcompound, key, sourceVer, targetVer); + } + + return nbttagcompound; + } + } + + private static class DataConverterMaterialId implements DataConverter { + + private static final String[] materials = new String[2268]; + + DataConverterMaterialId() { + } + + public int getDataVersion() { + return 102; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.contains("id", 99)) { + short short0 = cmp.getShort("id"); + + if (short0 > 0 && short0 < materials.length && materials[short0] != null) { + cmp.putString("id", materials[short0]); + } + } + + return cmp; + } + + static { + materials[1] = "minecraft:stone"; + materials[2] = "minecraft:grass"; + materials[3] = "minecraft:dirt"; + materials[4] = "minecraft:cobblestone"; + materials[5] = "minecraft:planks"; + materials[6] = "minecraft:sapling"; + materials[7] = "minecraft:bedrock"; + materials[8] = "minecraft:flowing_water"; + materials[9] = "minecraft:water"; + materials[10] = "minecraft:flowing_lava"; + materials[11] = "minecraft:lava"; + materials[12] = "minecraft:sand"; + materials[13] = "minecraft:gravel"; + materials[14] = "minecraft:gold_ore"; + materials[15] = "minecraft:iron_ore"; + materials[16] = "minecraft:coal_ore"; + materials[17] = "minecraft:log"; + materials[18] = "minecraft:leaves"; + materials[19] = "minecraft:sponge"; + materials[20] = "minecraft:glass"; + materials[21] = "minecraft:lapis_ore"; + materials[22] = "minecraft:lapis_block"; + materials[23] = "minecraft:dispenser"; + materials[24] = "minecraft:sandstone"; + materials[25] = "minecraft:noteblock"; + materials[27] = "minecraft:golden_rail"; + materials[28] = "minecraft:detector_rail"; + materials[29] = "minecraft:sticky_piston"; + materials[30] = "minecraft:web"; + materials[31] = "minecraft:tallgrass"; + materials[32] = "minecraft:deadbush"; + materials[33] = "minecraft:piston"; + materials[35] = "minecraft:wool"; + materials[37] = "minecraft:yellow_flower"; + materials[38] = "minecraft:red_flower"; + materials[39] = "minecraft:brown_mushroom"; + materials[40] = "minecraft:red_mushroom"; + materials[41] = "minecraft:gold_block"; + materials[42] = "minecraft:iron_block"; + materials[43] = "minecraft:double_stone_slab"; + materials[44] = "minecraft:stone_slab"; + materials[45] = "minecraft:brick_block"; + materials[46] = "minecraft:tnt"; + materials[47] = "minecraft:bookshelf"; + materials[48] = "minecraft:mossy_cobblestone"; + materials[49] = "minecraft:obsidian"; + materials[50] = "minecraft:torch"; + materials[51] = "minecraft:fire"; + materials[52] = "minecraft:mob_spawner"; + materials[53] = "minecraft:oak_stairs"; + materials[54] = "minecraft:chest"; + materials[56] = "minecraft:diamond_ore"; + materials[57] = "minecraft:diamond_block"; + materials[58] = "minecraft:crafting_table"; + materials[60] = "minecraft:farmland"; + materials[61] = "minecraft:furnace"; + materials[62] = "minecraft:lit_furnace"; + materials[65] = "minecraft:ladder"; + materials[66] = "minecraft:rail"; + materials[67] = "minecraft:stone_stairs"; + materials[69] = "minecraft:lever"; + materials[70] = "minecraft:stone_pressure_plate"; + materials[72] = "minecraft:wooden_pressure_plate"; + materials[73] = "minecraft:redstone_ore"; + materials[76] = "minecraft:redstone_torch"; + materials[77] = "minecraft:stone_button"; + materials[78] = "minecraft:snow_layer"; + materials[79] = "minecraft:ice"; + materials[80] = "minecraft:snow"; + materials[81] = "minecraft:cactus"; + materials[82] = "minecraft:clay"; + materials[84] = "minecraft:jukebox"; + materials[85] = "minecraft:fence"; + materials[86] = "minecraft:pumpkin"; + materials[87] = "minecraft:netherrack"; + materials[88] = "minecraft:soul_sand"; + materials[89] = "minecraft:glowstone"; + materials[90] = "minecraft:portal"; + materials[91] = "minecraft:lit_pumpkin"; + materials[95] = "minecraft:stained_glass"; + materials[96] = "minecraft:trapdoor"; + materials[97] = "minecraft:monster_egg"; + materials[98] = "minecraft:stonebrick"; + materials[99] = "minecraft:brown_mushroom_block"; + materials[100] = "minecraft:red_mushroom_block"; + materials[101] = "minecraft:iron_bars"; + materials[102] = "minecraft:glass_pane"; + materials[103] = "minecraft:melon_block"; + materials[106] = "minecraft:vine"; + materials[107] = "minecraft:fence_gate"; + materials[108] = "minecraft:brick_stairs"; + materials[109] = "minecraft:stone_brick_stairs"; + materials[110] = "minecraft:mycelium"; + materials[111] = "minecraft:waterlily"; + materials[112] = "minecraft:nether_brick"; + materials[113] = "minecraft:nether_brick_fence"; + materials[114] = "minecraft:nether_brick_stairs"; + materials[116] = "minecraft:enchanting_table"; + materials[119] = "minecraft:end_portal"; + materials[120] = "minecraft:end_portal_frame"; + materials[121] = "minecraft:end_stone"; + materials[122] = "minecraft:dragon_egg"; + materials[123] = "minecraft:redstone_lamp"; + materials[125] = "minecraft:double_wooden_slab"; + materials[126] = "minecraft:wooden_slab"; + materials[127] = "minecraft:cocoa"; + materials[128] = "minecraft:sandstone_stairs"; + materials[129] = "minecraft:emerald_ore"; + materials[130] = "minecraft:ender_chest"; + materials[131] = "minecraft:tripwire_hook"; + materials[133] = "minecraft:emerald_block"; + materials[134] = "minecraft:spruce_stairs"; + materials[135] = "minecraft:birch_stairs"; + materials[136] = "minecraft:jungle_stairs"; + materials[137] = "minecraft:command_block"; + materials[138] = "minecraft:beacon"; + materials[139] = "minecraft:cobblestone_wall"; + materials[141] = "minecraft:carrots"; + materials[142] = "minecraft:potatoes"; + materials[143] = "minecraft:wooden_button"; + materials[145] = "minecraft:anvil"; + materials[146] = "minecraft:trapped_chest"; + materials[147] = "minecraft:light_weighted_pressure_plate"; + materials[148] = "minecraft:heavy_weighted_pressure_plate"; + materials[151] = "minecraft:daylight_detector"; + materials[152] = "minecraft:redstone_block"; + materials[153] = "minecraft:quartz_ore"; + materials[154] = "minecraft:hopper"; + materials[155] = "minecraft:quartz_block"; + materials[156] = "minecraft:quartz_stairs"; + materials[157] = "minecraft:activator_rail"; + materials[158] = "minecraft:dropper"; + materials[159] = "minecraft:stained_hardened_clay"; + materials[160] = "minecraft:stained_glass_pane"; + materials[161] = "minecraft:leaves2"; + materials[162] = "minecraft:log2"; + materials[163] = "minecraft:acacia_stairs"; + materials[164] = "minecraft:dark_oak_stairs"; + materials[170] = "minecraft:hay_block"; + materials[171] = "minecraft:carpet"; + materials[172] = "minecraft:hardened_clay"; + materials[173] = "minecraft:coal_block"; + materials[174] = "minecraft:packed_ice"; + materials[175] = "minecraft:double_plant"; + materials[256] = "minecraft:iron_shovel"; + materials[257] = "minecraft:iron_pickaxe"; + materials[258] = "minecraft:iron_axe"; + materials[259] = "minecraft:flint_and_steel"; + materials[260] = "minecraft:apple"; + materials[261] = "minecraft:bow"; + materials[262] = "minecraft:arrow"; + materials[263] = "minecraft:coal"; + materials[264] = "minecraft:diamond"; + materials[265] = "minecraft:iron_ingot"; + materials[266] = "minecraft:gold_ingot"; + materials[267] = "minecraft:iron_sword"; + materials[268] = "minecraft:wooden_sword"; + materials[269] = "minecraft:wooden_shovel"; + materials[270] = "minecraft:wooden_pickaxe"; + materials[271] = "minecraft:wooden_axe"; + materials[272] = "minecraft:stone_sword"; + materials[273] = "minecraft:stone_shovel"; + materials[274] = "minecraft:stone_pickaxe"; + materials[275] = "minecraft:stone_axe"; + materials[276] = "minecraft:diamond_sword"; + materials[277] = "minecraft:diamond_shovel"; + materials[278] = "minecraft:diamond_pickaxe"; + materials[279] = "minecraft:diamond_axe"; + materials[280] = "minecraft:stick"; + materials[281] = "minecraft:bowl"; + materials[282] = "minecraft:mushroom_stew"; + materials[283] = "minecraft:golden_sword"; + materials[284] = "minecraft:golden_shovel"; + materials[285] = "minecraft:golden_pickaxe"; + materials[286] = "minecraft:golden_axe"; + materials[287] = "minecraft:string"; + materials[288] = "minecraft:feather"; + materials[289] = "minecraft:gunpowder"; + materials[290] = "minecraft:wooden_hoe"; + materials[291] = "minecraft:stone_hoe"; + materials[292] = "minecraft:iron_hoe"; + materials[293] = "minecraft:diamond_hoe"; + materials[294] = "minecraft:golden_hoe"; + materials[295] = "minecraft:wheat_seeds"; + materials[296] = "minecraft:wheat"; + materials[297] = "minecraft:bread"; + materials[298] = "minecraft:leather_helmet"; + materials[299] = "minecraft:leather_chestplate"; + materials[300] = "minecraft:leather_leggings"; + materials[301] = "minecraft:leather_boots"; + materials[302] = "minecraft:chainmail_helmet"; + materials[303] = "minecraft:chainmail_chestplate"; + materials[304] = "minecraft:chainmail_leggings"; + materials[305] = "minecraft:chainmail_boots"; + materials[306] = "minecraft:iron_helmet"; + materials[307] = "minecraft:iron_chestplate"; + materials[308] = "minecraft:iron_leggings"; + materials[309] = "minecraft:iron_boots"; + materials[310] = "minecraft:diamond_helmet"; + materials[311] = "minecraft:diamond_chestplate"; + materials[312] = "minecraft:diamond_leggings"; + materials[313] = "minecraft:diamond_boots"; + materials[314] = "minecraft:golden_helmet"; + materials[315] = "minecraft:golden_chestplate"; + materials[316] = "minecraft:golden_leggings"; + materials[317] = "minecraft:golden_boots"; + materials[318] = "minecraft:flint"; + materials[319] = "minecraft:porkchop"; + materials[320] = "minecraft:cooked_porkchop"; + materials[321] = "minecraft:painting"; + materials[322] = "minecraft:golden_apple"; + materials[323] = "minecraft:sign"; + materials[324] = "minecraft:wooden_door"; + materials[325] = "minecraft:bucket"; + materials[326] = "minecraft:water_bucket"; + materials[327] = "minecraft:lava_bucket"; + materials[328] = "minecraft:minecart"; + materials[329] = "minecraft:saddle"; + materials[330] = "minecraft:iron_door"; + materials[331] = "minecraft:redstone"; + materials[332] = "minecraft:snowball"; + materials[333] = "minecraft:boat"; + materials[334] = "minecraft:leather"; + materials[335] = "minecraft:milk_bucket"; + materials[336] = "minecraft:brick"; + materials[337] = "minecraft:clay_ball"; + materials[338] = "minecraft:reeds"; + materials[339] = "minecraft:paper"; + materials[340] = "minecraft:book"; + materials[341] = "minecraft:slime_ball"; + materials[342] = "minecraft:chest_minecart"; + materials[343] = "minecraft:furnace_minecart"; + materials[344] = "minecraft:egg"; + materials[345] = "minecraft:compass"; + materials[346] = "minecraft:fishing_rod"; + materials[347] = "minecraft:clock"; + materials[348] = "minecraft:glowstone_dust"; + materials[349] = "minecraft:fish"; + materials[350] = "minecraft:cooked_fish"; // Paper - cooked_fished -> cooked_fish + materials[351] = "minecraft:dye"; + materials[352] = "minecraft:bone"; + materials[353] = "minecraft:sugar"; + materials[354] = "minecraft:cake"; + materials[355] = "minecraft:bed"; + materials[356] = "minecraft:repeater"; + materials[357] = "minecraft:cookie"; + materials[358] = "minecraft:filled_map"; + materials[359] = "minecraft:shears"; + materials[360] = "minecraft:melon"; + materials[361] = "minecraft:pumpkin_seeds"; + materials[362] = "minecraft:melon_seeds"; + materials[363] = "minecraft:beef"; + materials[364] = "minecraft:cooked_beef"; + materials[365] = "minecraft:chicken"; + materials[366] = "minecraft:cooked_chicken"; + materials[367] = "minecraft:rotten_flesh"; + materials[368] = "minecraft:ender_pearl"; + materials[369] = "minecraft:blaze_rod"; + materials[370] = "minecraft:ghast_tear"; + materials[371] = "minecraft:gold_nugget"; + materials[372] = "minecraft:nether_wart"; + materials[373] = "minecraft:potion"; + materials[374] = "minecraft:glass_bottle"; + materials[375] = "minecraft:spider_eye"; + materials[376] = "minecraft:fermented_spider_eye"; + materials[377] = "minecraft:blaze_powder"; + materials[378] = "minecraft:magma_cream"; + materials[379] = "minecraft:brewing_stand"; + materials[380] = "minecraft:cauldron"; + materials[381] = "minecraft:ender_eye"; + materials[382] = "minecraft:speckled_melon"; + materials[383] = "minecraft:spawn_egg"; + materials[384] = "minecraft:experience_bottle"; + materials[385] = "minecraft:fire_charge"; + materials[386] = "minecraft:writable_book"; + materials[387] = "minecraft:written_book"; + materials[388] = "minecraft:emerald"; + materials[389] = "minecraft:item_frame"; + materials[390] = "minecraft:flower_pot"; + materials[391] = "minecraft:carrot"; + materials[392] = "minecraft:potato"; + materials[393] = "minecraft:baked_potato"; + materials[394] = "minecraft:poisonous_potato"; + materials[395] = "minecraft:map"; + materials[396] = "minecraft:golden_carrot"; + materials[397] = "minecraft:skull"; + materials[398] = "minecraft:carrot_on_a_stick"; + materials[399] = "minecraft:nether_star"; + materials[400] = "minecraft:pumpkin_pie"; + materials[401] = "minecraft:fireworks"; + materials[402] = "minecraft:firework_charge"; + materials[403] = "minecraft:enchanted_book"; + materials[404] = "minecraft:comparator"; + materials[405] = "minecraft:netherbrick"; + materials[406] = "minecraft:quartz"; + materials[407] = "minecraft:tnt_minecart"; + materials[408] = "minecraft:hopper_minecart"; + materials[417] = "minecraft:iron_horse_armor"; + materials[418] = "minecraft:golden_horse_armor"; + materials[419] = "minecraft:diamond_horse_armor"; + materials[420] = "minecraft:lead"; + materials[421] = "minecraft:name_tag"; + materials[422] = "minecraft:command_block_minecart"; + materials[2256] = "minecraft:record_13"; + materials[2257] = "minecraft:record_cat"; + materials[2258] = "minecraft:record_blocks"; + materials[2259] = "minecraft:record_chirp"; + materials[2260] = "minecraft:record_far"; + materials[2261] = "minecraft:record_mall"; + materials[2262] = "minecraft:record_mellohi"; + materials[2263] = "minecraft:record_stal"; + materials[2264] = "minecraft:record_strad"; + materials[2265] = "minecraft:record_ward"; + materials[2266] = "minecraft:record_11"; + materials[2267] = "minecraft:record_wait"; + // Paper start + materials[409] = "minecraft:prismarine_shard"; + materials[410] = "minecraft:prismarine_crystals"; + materials[411] = "minecraft:rabbit"; + materials[412] = "minecraft:cooked_rabbit"; + materials[413] = "minecraft:rabbit_stew"; + materials[414] = "minecraft:rabbit_foot"; + materials[415] = "minecraft:rabbit_hide"; + materials[416] = "minecraft:armor_stand"; + materials[423] = "minecraft:mutton"; + materials[424] = "minecraft:cooked_mutton"; + materials[425] = "minecraft:banner"; + materials[426] = "minecraft:end_crystal"; + materials[427] = "minecraft:spruce_door"; + materials[428] = "minecraft:birch_door"; + materials[429] = "minecraft:jungle_door"; + materials[430] = "minecraft:acacia_door"; + materials[431] = "minecraft:dark_oak_door"; + materials[432] = "minecraft:chorus_fruit"; + materials[433] = "minecraft:chorus_fruit_popped"; + materials[434] = "minecraft:beetroot"; + materials[435] = "minecraft:beetroot_seeds"; + materials[436] = "minecraft:beetroot_soup"; + materials[437] = "minecraft:dragon_breath"; + materials[438] = "minecraft:splash_potion"; + materials[439] = "minecraft:spectral_arrow"; + materials[440] = "minecraft:tipped_arrow"; + materials[441] = "minecraft:lingering_potion"; + materials[442] = "minecraft:shield"; + materials[443] = "minecraft:elytra"; + materials[444] = "minecraft:spruce_boat"; + materials[445] = "minecraft:birch_boat"; + materials[446] = "minecraft:jungle_boat"; + materials[447] = "minecraft:acacia_boat"; + materials[448] = "minecraft:dark_oak_boat"; + materials[449] = "minecraft:totem_of_undying"; + materials[450] = "minecraft:shulker_shell"; + materials[452] = "minecraft:iron_nugget"; + materials[453] = "minecraft:knowledge_book"; + // Paper end + } + } + + private static class DataConverterArmorStand implements DataConverter { + + DataConverterArmorStand() { + } + + public int getDataVersion() { + return 147; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("ArmorStand".equals(cmp.getString("id")) && cmp.getBoolean("Silent") && !cmp.getBoolean("Marker")) { + cmp.remove("Silent"); + } + + return cmp; + } + } + + private static class DataConverterBanner implements DataConverter { + + DataConverterBanner() { + } + + public int getDataVersion() { + return 804; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:banner".equals(cmp.getString("id")) && cmp.contains("tag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); + + if (nbttagcompound2.contains("Base", 99)) { + cmp.putShort("Damage", (short) (nbttagcompound2.getShort("Base") & 15)); + if (nbttagcompound1.contains("display", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound1.getCompound("display"); + + if (nbttagcompound3.contains("Lore", 9)) { + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound3.getList("Lore", 8); + + if (nbttaglist.size() == 1 && "(+NBT)".equals(nbttaglist.getString(0))) { + return cmp; + } + } + } + + nbttagcompound2.remove("Base"); + if (nbttagcompound2.isEmpty()) { + nbttagcompound1.remove("BlockEntityTag"); + } + + if (nbttagcompound1.isEmpty()) { + cmp.remove("tag"); + } + } + } + } + + return cmp; + } + } + + private static class DataConverterPotionId implements DataConverter { + + private static final String[] potions = new String[128]; + + DataConverterPotionId() { + } + + public int getDataVersion() { + return 102; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:potion".equals(cmp.getString("id"))) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + short short0 = cmp.getShort("Damage"); + + if (!nbttagcompound1.contains("Potion", 8)) { + String s = DataConverterPotionId.potions[short0 & 127]; + + nbttagcompound1.putString("Potion", s == null ? "minecraft:water" : s); + cmp.put("tag", nbttagcompound1); + if ((short0 & 16384) == 16384) { + cmp.putString("id", "minecraft:splash_potion"); + } + } + + if (short0 != 0) { + cmp.putShort("Damage", (short) 0); + } + } + + return cmp; + } + + static { + DataConverterPotionId.potions[0] = "minecraft:water"; + DataConverterPotionId.potions[1] = "minecraft:regeneration"; + DataConverterPotionId.potions[2] = "minecraft:swiftness"; + DataConverterPotionId.potions[3] = "minecraft:fire_resistance"; + DataConverterPotionId.potions[4] = "minecraft:poison"; + DataConverterPotionId.potions[5] = "minecraft:healing"; + DataConverterPotionId.potions[6] = "minecraft:night_vision"; + DataConverterPotionId.potions[7] = null; + DataConverterPotionId.potions[8] = "minecraft:weakness"; + DataConverterPotionId.potions[9] = "minecraft:strength"; + DataConverterPotionId.potions[10] = "minecraft:slowness"; + DataConverterPotionId.potions[11] = "minecraft:leaping"; + DataConverterPotionId.potions[12] = "minecraft:harming"; + DataConverterPotionId.potions[13] = "minecraft:water_breathing"; + DataConverterPotionId.potions[14] = "minecraft:invisibility"; + DataConverterPotionId.potions[15] = null; + DataConverterPotionId.potions[16] = "minecraft:awkward"; + DataConverterPotionId.potions[17] = "minecraft:regeneration"; + DataConverterPotionId.potions[18] = "minecraft:swiftness"; + DataConverterPotionId.potions[19] = "minecraft:fire_resistance"; + DataConverterPotionId.potions[20] = "minecraft:poison"; + DataConverterPotionId.potions[21] = "minecraft:healing"; + DataConverterPotionId.potions[22] = "minecraft:night_vision"; + DataConverterPotionId.potions[23] = null; + DataConverterPotionId.potions[24] = "minecraft:weakness"; + DataConverterPotionId.potions[25] = "minecraft:strength"; + DataConverterPotionId.potions[26] = "minecraft:slowness"; + DataConverterPotionId.potions[27] = "minecraft:leaping"; + DataConverterPotionId.potions[28] = "minecraft:harming"; + DataConverterPotionId.potions[29] = "minecraft:water_breathing"; + DataConverterPotionId.potions[30] = "minecraft:invisibility"; + DataConverterPotionId.potions[31] = null; + DataConverterPotionId.potions[32] = "minecraft:thick"; + DataConverterPotionId.potions[33] = "minecraft:strong_regeneration"; + DataConverterPotionId.potions[34] = "minecraft:strong_swiftness"; + DataConverterPotionId.potions[35] = "minecraft:fire_resistance"; + DataConverterPotionId.potions[36] = "minecraft:strong_poison"; + DataConverterPotionId.potions[37] = "minecraft:strong_healing"; + DataConverterPotionId.potions[38] = "minecraft:night_vision"; + DataConverterPotionId.potions[39] = null; + DataConverterPotionId.potions[40] = "minecraft:weakness"; + DataConverterPotionId.potions[41] = "minecraft:strong_strength"; + DataConverterPotionId.potions[42] = "minecraft:slowness"; + DataConverterPotionId.potions[43] = "minecraft:strong_leaping"; + DataConverterPotionId.potions[44] = "minecraft:strong_harming"; + DataConverterPotionId.potions[45] = "minecraft:water_breathing"; + DataConverterPotionId.potions[46] = "minecraft:invisibility"; + DataConverterPotionId.potions[47] = null; + DataConverterPotionId.potions[48] = null; + DataConverterPotionId.potions[49] = "minecraft:strong_regeneration"; + DataConverterPotionId.potions[50] = "minecraft:strong_swiftness"; + DataConverterPotionId.potions[51] = "minecraft:fire_resistance"; + DataConverterPotionId.potions[52] = "minecraft:strong_poison"; + DataConverterPotionId.potions[53] = "minecraft:strong_healing"; + DataConverterPotionId.potions[54] = "minecraft:night_vision"; + DataConverterPotionId.potions[55] = null; + DataConverterPotionId.potions[56] = "minecraft:weakness"; + DataConverterPotionId.potions[57] = "minecraft:strong_strength"; + DataConverterPotionId.potions[58] = "minecraft:slowness"; + DataConverterPotionId.potions[59] = "minecraft:strong_leaping"; + DataConverterPotionId.potions[60] = "minecraft:strong_harming"; + DataConverterPotionId.potions[61] = "minecraft:water_breathing"; + DataConverterPotionId.potions[62] = "minecraft:invisibility"; + DataConverterPotionId.potions[63] = null; + DataConverterPotionId.potions[64] = "minecraft:mundane"; + DataConverterPotionId.potions[65] = "minecraft:long_regeneration"; + DataConverterPotionId.potions[66] = "minecraft:long_swiftness"; + DataConverterPotionId.potions[67] = "minecraft:long_fire_resistance"; + DataConverterPotionId.potions[68] = "minecraft:long_poison"; + DataConverterPotionId.potions[69] = "minecraft:healing"; + DataConverterPotionId.potions[70] = "minecraft:long_night_vision"; + DataConverterPotionId.potions[71] = null; + DataConverterPotionId.potions[72] = "minecraft:long_weakness"; + DataConverterPotionId.potions[73] = "minecraft:long_strength"; + DataConverterPotionId.potions[74] = "minecraft:long_slowness"; + DataConverterPotionId.potions[75] = "minecraft:long_leaping"; + DataConverterPotionId.potions[76] = "minecraft:harming"; + DataConverterPotionId.potions[77] = "minecraft:long_water_breathing"; + DataConverterPotionId.potions[78] = "minecraft:long_invisibility"; + DataConverterPotionId.potions[79] = null; + DataConverterPotionId.potions[80] = "minecraft:awkward"; + DataConverterPotionId.potions[81] = "minecraft:long_regeneration"; + DataConverterPotionId.potions[82] = "minecraft:long_swiftness"; + DataConverterPotionId.potions[83] = "minecraft:long_fire_resistance"; + DataConverterPotionId.potions[84] = "minecraft:long_poison"; + DataConverterPotionId.potions[85] = "minecraft:healing"; + DataConverterPotionId.potions[86] = "minecraft:long_night_vision"; + DataConverterPotionId.potions[87] = null; + DataConverterPotionId.potions[88] = "minecraft:long_weakness"; + DataConverterPotionId.potions[89] = "minecraft:long_strength"; + DataConverterPotionId.potions[90] = "minecraft:long_slowness"; + DataConverterPotionId.potions[91] = "minecraft:long_leaping"; + DataConverterPotionId.potions[92] = "minecraft:harming"; + DataConverterPotionId.potions[93] = "minecraft:long_water_breathing"; + DataConverterPotionId.potions[94] = "minecraft:long_invisibility"; + DataConverterPotionId.potions[95] = null; + DataConverterPotionId.potions[96] = "minecraft:thick"; + DataConverterPotionId.potions[97] = "minecraft:regeneration"; + DataConverterPotionId.potions[98] = "minecraft:swiftness"; + DataConverterPotionId.potions[99] = "minecraft:long_fire_resistance"; + DataConverterPotionId.potions[100] = "minecraft:poison"; + DataConverterPotionId.potions[101] = "minecraft:strong_healing"; + DataConverterPotionId.potions[102] = "minecraft:long_night_vision"; + DataConverterPotionId.potions[103] = null; + DataConverterPotionId.potions[104] = "minecraft:long_weakness"; + DataConverterPotionId.potions[105] = "minecraft:strength"; + DataConverterPotionId.potions[106] = "minecraft:long_slowness"; + DataConverterPotionId.potions[107] = "minecraft:leaping"; + DataConverterPotionId.potions[108] = "minecraft:strong_harming"; + DataConverterPotionId.potions[109] = "minecraft:long_water_breathing"; + DataConverterPotionId.potions[110] = "minecraft:long_invisibility"; + DataConverterPotionId.potions[111] = null; + DataConverterPotionId.potions[112] = null; + DataConverterPotionId.potions[113] = "minecraft:regeneration"; + DataConverterPotionId.potions[114] = "minecraft:swiftness"; + DataConverterPotionId.potions[115] = "minecraft:long_fire_resistance"; + DataConverterPotionId.potions[116] = "minecraft:poison"; + DataConverterPotionId.potions[117] = "minecraft:strong_healing"; + DataConverterPotionId.potions[118] = "minecraft:long_night_vision"; + DataConverterPotionId.potions[119] = null; + DataConverterPotionId.potions[120] = "minecraft:long_weakness"; + DataConverterPotionId.potions[121] = "minecraft:strength"; + DataConverterPotionId.potions[122] = "minecraft:long_slowness"; + DataConverterPotionId.potions[123] = "minecraft:leaping"; + DataConverterPotionId.potions[124] = "minecraft:strong_harming"; + DataConverterPotionId.potions[125] = "minecraft:long_water_breathing"; + DataConverterPotionId.potions[126] = "minecraft:long_invisibility"; + DataConverterPotionId.potions[127] = null; + } + } + + private static class DataConverterSpawnEgg implements DataConverter { + + private static final String[] eggs = new String[256]; + + DataConverterSpawnEgg() { + } + + public int getDataVersion() { + return 105; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:spawn_egg".equals(cmp.getString("id"))) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag"); + short short0 = cmp.getShort("Damage"); + + if (!nbttagcompound2.contains("id", 8)) { + String s = DataConverterSpawnEgg.eggs[short0 & 255]; + + if (s != null) { + nbttagcompound2.putString("id", s); + nbttagcompound1.put("EntityTag", nbttagcompound2); + cmp.put("tag", nbttagcompound1); + } + } + + if (short0 != 0) { + cmp.putShort("Damage", (short) 0); + } + } + + return cmp; + } + + static { + + DataConverterSpawnEgg.eggs[1] = "Item"; + DataConverterSpawnEgg.eggs[2] = "XPOrb"; + DataConverterSpawnEgg.eggs[7] = "ThrownEgg"; + DataConverterSpawnEgg.eggs[8] = "LeashKnot"; + DataConverterSpawnEgg.eggs[9] = "Painting"; + DataConverterSpawnEgg.eggs[10] = "Arrow"; + DataConverterSpawnEgg.eggs[11] = "Snowball"; + DataConverterSpawnEgg.eggs[12] = "Fireball"; + DataConverterSpawnEgg.eggs[13] = "SmallFireball"; + DataConverterSpawnEgg.eggs[14] = "ThrownEnderpearl"; + DataConverterSpawnEgg.eggs[15] = "EyeOfEnderSignal"; + DataConverterSpawnEgg.eggs[16] = "ThrownPotion"; + DataConverterSpawnEgg.eggs[17] = "ThrownExpBottle"; + DataConverterSpawnEgg.eggs[18] = "ItemFrame"; + DataConverterSpawnEgg.eggs[19] = "WitherSkull"; + DataConverterSpawnEgg.eggs[20] = "PrimedTnt"; + DataConverterSpawnEgg.eggs[21] = "FallingSand"; + DataConverterSpawnEgg.eggs[22] = "FireworksRocketEntity"; + DataConverterSpawnEgg.eggs[23] = "TippedArrow"; + DataConverterSpawnEgg.eggs[24] = "SpectralArrow"; + DataConverterSpawnEgg.eggs[25] = "ShulkerBullet"; + DataConverterSpawnEgg.eggs[26] = "DragonFireball"; + DataConverterSpawnEgg.eggs[30] = "ArmorStand"; + DataConverterSpawnEgg.eggs[41] = "Boat"; + DataConverterSpawnEgg.eggs[42] = "MinecartRideable"; + DataConverterSpawnEgg.eggs[43] = "MinecartChest"; + DataConverterSpawnEgg.eggs[44] = "MinecartFurnace"; + DataConverterSpawnEgg.eggs[45] = "MinecartTNT"; + DataConverterSpawnEgg.eggs[46] = "MinecartHopper"; + DataConverterSpawnEgg.eggs[47] = "MinecartSpawner"; + DataConverterSpawnEgg.eggs[40] = "MinecartCommandBlock"; + DataConverterSpawnEgg.eggs[48] = "Mob"; + DataConverterSpawnEgg.eggs[49] = "Monster"; + DataConverterSpawnEgg.eggs[50] = "Creeper"; + DataConverterSpawnEgg.eggs[51] = "Skeleton"; + DataConverterSpawnEgg.eggs[52] = "Spider"; + DataConverterSpawnEgg.eggs[53] = "Giant"; + DataConverterSpawnEgg.eggs[54] = "Zombie"; + DataConverterSpawnEgg.eggs[55] = "Slime"; + DataConverterSpawnEgg.eggs[56] = "Ghast"; + DataConverterSpawnEgg.eggs[57] = "PigZombie"; + DataConverterSpawnEgg.eggs[58] = "Enderman"; + DataConverterSpawnEgg.eggs[59] = "CaveSpider"; + DataConverterSpawnEgg.eggs[60] = "Silverfish"; + DataConverterSpawnEgg.eggs[61] = "Blaze"; + DataConverterSpawnEgg.eggs[62] = "LavaSlime"; + DataConverterSpawnEgg.eggs[63] = "EnderDragon"; + DataConverterSpawnEgg.eggs[64] = "WitherBoss"; + DataConverterSpawnEgg.eggs[65] = "Bat"; + DataConverterSpawnEgg.eggs[66] = "Witch"; + DataConverterSpawnEgg.eggs[67] = "Endermite"; + DataConverterSpawnEgg.eggs[68] = "Guardian"; + DataConverterSpawnEgg.eggs[69] = "Shulker"; + DataConverterSpawnEgg.eggs[90] = "Pig"; + DataConverterSpawnEgg.eggs[91] = "Sheep"; + DataConverterSpawnEgg.eggs[92] = "Cow"; + DataConverterSpawnEgg.eggs[93] = "Chicken"; + DataConverterSpawnEgg.eggs[94] = "Squid"; + DataConverterSpawnEgg.eggs[95] = "Wolf"; + DataConverterSpawnEgg.eggs[96] = "MushroomCow"; + DataConverterSpawnEgg.eggs[97] = "SnowMan"; + DataConverterSpawnEgg.eggs[98] = "Ozelot"; + DataConverterSpawnEgg.eggs[99] = "VillagerGolem"; + DataConverterSpawnEgg.eggs[100] = "EntityHorse"; + DataConverterSpawnEgg.eggs[101] = "Rabbit"; + DataConverterSpawnEgg.eggs[120] = "Villager"; + DataConverterSpawnEgg.eggs[200] = "EnderCrystal"; + } + } + + private static class DataConverterMinecart implements DataConverter { + + private static final List a = Lists.newArrayList("MinecartRideable", "MinecartChest", "MinecartFurnace", "MinecartTNT", "MinecartSpawner", "MinecartHopper", "MinecartCommandBlock"); + + DataConverterMinecart() { + } + + public int getDataVersion() { + return 106; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Minecart".equals(cmp.getString("id"))) { + String s = "MinecartRideable"; + int i = cmp.getInt("Type"); + + if (i > 0 && i < DataConverterMinecart.a.size()) { + s = DataConverterMinecart.a.get(i); + } + + cmp.putString("id", s); + cmp.remove("Type"); + } + + return cmp; + } + } + + private static class DataConverterMobSpawner implements DataConverter { + + DataConverterMobSpawner() { + } + + public int getDataVersion() { + return 107; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (!"MobSpawner".equals(cmp.getString("id"))) { + return cmp; + } else { + if (cmp.contains("EntityId", 8)) { + String s = cmp.getString("EntityId"); + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("SpawnData"); + + nbttagcompound1.putString("id", s.isEmpty() ? "Pig" : s); + cmp.put("SpawnData", nbttagcompound1); + cmp.remove("EntityId"); + } + + if (cmp.contains("SpawnPotentials", 9)) { + net.minecraft.nbt.ListTag nbttaglist = cmp.getList("SpawnPotentials", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompound(i); + + if (nbttagcompound2.contains("Type", 8)) { + net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound2.getCompound("Properties"); + + nbttagcompound3.putString("id", nbttagcompound2.getString("Type")); + nbttagcompound2.put("Entity", nbttagcompound3); + nbttagcompound2.remove("Type"); + nbttagcompound2.remove("Properties"); + } + } + } + + return cmp; + } + } + } + + private static class DataConverterUUID implements DataConverter { + + DataConverterUUID() { + } + + public int getDataVersion() { + return 108; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.contains("UUID", 8)) { + cmp.putUUID("UUID", UUID.fromString(cmp.getString("UUID"))); + } + + return cmp; + } + } + + private static class DataConverterHealth implements DataConverter { + + private static final Set a = Sets.newHashSet("ArmorStand", "Bat", "Blaze", "CaveSpider", "Chicken", "Cow", "Creeper", "EnderDragon", "Enderman", "Endermite", "EntityHorse", "Ghast", "Giant", "Guardian", "LavaSlime", "MushroomCow", "Ozelot", "Pig", "PigZombie", "Rabbit", "Sheep", "Shulker", "Silverfish", "Skeleton", "Slime", "SnowMan", "Spider", "Squid", "Villager", "VillagerGolem", "Witch", "WitherBoss", "Wolf", "Zombie"); + + DataConverterHealth() { + } + + public int getDataVersion() { + return 109; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (DataConverterHealth.a.contains(cmp.getString("id"))) { + float f; + + if (cmp.contains("HealF", 99)) { + f = cmp.getFloat("HealF"); + cmp.remove("HealF"); + } else { + if (!cmp.contains("Health", 99)) { + return cmp; + } + + f = cmp.getFloat("Health"); + } + + cmp.putFloat("Health", f); + } + + return cmp; + } + } + + private static class DataConverterSaddle implements DataConverter { + + DataConverterSaddle() { + } + + public int getDataVersion() { + return 110; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("EntityHorse".equals(cmp.getString("id")) && !cmp.contains("SaddleItem", 10) && cmp.getBoolean("Saddle")) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = new net.minecraft.nbt.CompoundTag(); + + nbttagcompound1.putString("id", "minecraft:saddle"); + nbttagcompound1.putByte("Count", (byte) 1); + nbttagcompound1.putShort("Damage", (short) 0); + cmp.put("SaddleItem", nbttagcompound1); + cmp.remove("Saddle"); + } + + return cmp; + } + } + + private static class DataConverterHanging implements DataConverter { + + DataConverterHanging() { + } + + public int getDataVersion() { + return 111; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = cmp.getString("id"); + boolean flag = "Painting".equals(s); + boolean flag1 = "ItemFrame".equals(s); + + if ((flag || flag1) && !cmp.contains("Facing", 99)) { + Direction enumdirection; + + if (cmp.contains("Direction", 99)) { + enumdirection = Direction.from2DDataValue(cmp.getByte("Direction")); + cmp.putInt("TileX", cmp.getInt("TileX") + enumdirection.getStepX()); + cmp.putInt("TileY", cmp.getInt("TileY") + enumdirection.getStepY()); + cmp.putInt("TileZ", cmp.getInt("TileZ") + enumdirection.getStepZ()); + cmp.remove("Direction"); + if (flag1 && cmp.contains("ItemRotation", 99)) { + cmp.putByte("ItemRotation", (byte) (cmp.getByte("ItemRotation") * 2)); + } + } else { + enumdirection = Direction.from2DDataValue(cmp.getByte("Dir")); + cmp.remove("Dir"); + } + + cmp.putByte("Facing", (byte) enumdirection.get2DDataValue()); + } + + return cmp; + } + } + + private static class DataConverterDropChances implements DataConverter { + + DataConverterDropChances() { + } + + public int getDataVersion() { + return 113; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + net.minecraft.nbt.ListTag nbttaglist; + + if (cmp.contains("HandDropChances", 9)) { + nbttaglist = cmp.getList("HandDropChances", 5); + if (nbttaglist.size() == 2 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F) { + cmp.remove("HandDropChances"); + } + } + + if (cmp.contains("ArmorDropChances", 9)) { + nbttaglist = cmp.getList("ArmorDropChances", 5); + if (nbttaglist.size() == 4 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F && nbttaglist.getFloat(2) == 0.0F && nbttaglist.getFloat(3) == 0.0F) { + cmp.remove("ArmorDropChances"); + } + } + + return cmp; + } + } + + private static class DataConverterRiding implements DataConverter { + + DataConverterRiding() { + } + + public int getDataVersion() { + return 135; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + while (cmp.contains("Riding", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = this.b(cmp); + + this.convert(cmp, nbttagcompound1); + cmp = nbttagcompound1; + } + + return cmp; + } + + protected void convert(net.minecraft.nbt.CompoundTag nbttagcompound, net.minecraft.nbt.CompoundTag nbttagcompound1) { + net.minecraft.nbt.ListTag nbttaglist = new net.minecraft.nbt.ListTag(); + + nbttaglist.add(nbttagcompound); + nbttagcompound1.put("Passengers", nbttaglist); + } + + protected net.minecraft.nbt.CompoundTag b(net.minecraft.nbt.CompoundTag nbttagcompound) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Riding"); + + nbttagcompound.remove("Riding"); + return nbttagcompound1; + } + } + + private static class DataConverterBook implements DataConverter { + + DataConverterBook() { + } + + public int getDataVersion() { + return 165; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:written_book".equals(cmp.getString("id"))) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("pages", 9)) { + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("pages", 8); + + for (int i = 0; i < nbttaglist.size(); ++i) { + String s = nbttaglist.getString(i); + Component object = null; + + if (!"null".equals(s) && !StringUtil.isNullOrEmpty(s)) { + if ((s.charAt(0) != 34 || s.charAt(s.length() - 1) != 34) && (s.charAt(0) != 123 || s.charAt(s.length() - 1) != 125)) { + object = Component.literal(s); + } else { + try { + object = GsonHelper.fromJson(DataConverterSignText.a, s, Component.class, true); + if (object == null) { + object = Component.literal(""); + } + } catch (JsonParseException jsonparseexception) { + ; + } + + if (object == null) { + try { + object = Component.Serializer.fromJson(s); + } catch (JsonParseException jsonparseexception1) { + ; + } + } + + if (object == null) { + try { + object = Component.Serializer.fromJsonLenient(s); + } catch (JsonParseException jsonparseexception2) { + ; + } + } + + if (object == null) { + object = Component.literal(s); + } + } + } else { + object = Component.literal(""); + } + + nbttaglist.set(i, net.minecraft.nbt.StringTag.valueOf(Component.Serializer.toJson(object))); + } + + nbttagcompound1.put("pages", nbttaglist); + } + } + + return cmp; + } + } + + private static class DataConverterCookedFish implements DataConverter { + + private static final ResourceLocation a = new ResourceLocation("cooked_fished"); + + DataConverterCookedFish() { + } + + public int getDataVersion() { + return 502; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.contains("id", 8) && DataConverterCookedFish.a.equals(new ResourceLocation(cmp.getString("id")))) { + cmp.putString("id", "minecraft:cooked_fish"); + } + + return cmp; + } + } + + private static class DataConverterZombie implements DataConverter { + + private static final Random a = new Random(); + + DataConverterZombie() { + } + + public int getDataVersion() { + return 502; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Zombie".equals(cmp.getString("id")) && cmp.getBoolean("IsVillager")) { + if (!cmp.contains("ZombieType", 99)) { + int i = -1; + + if (cmp.contains("VillagerProfession", 99)) { + try { + i = this.convert(cmp.getInt("VillagerProfession")); + } catch (RuntimeException runtimeexception) { + ; + } + } + + if (i == -1) { + i = this.convert(DataConverterZombie.a.nextInt(6)); + } + + cmp.putInt("ZombieType", i); + } + + cmp.remove("IsVillager"); + } + + return cmp; + } + + private int convert(int i) { + return i >= 0 && i < 6 ? i : -1; + } + } + + private static class DataConverterVBO implements DataConverter { + + DataConverterVBO() { + } + + public int getDataVersion() { + return 505; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + cmp.putString("useVbo", "true"); + return cmp; + } + } + + private static class DataConverterGuardian implements DataConverter { + + DataConverterGuardian() { + } + + public int getDataVersion() { + return 700; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Guardian".equals(cmp.getString("id"))) { + if (cmp.getBoolean("Elder")) { + cmp.putString("id", "ElderGuardian"); + } + + cmp.remove("Elder"); + } + + return cmp; + } + } + + private static class DataConverterSkeleton implements DataConverter { + + DataConverterSkeleton() { + } + + public int getDataVersion() { + return 701; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = cmp.getString("id"); + + if ("Skeleton".equals(s)) { + int i = cmp.getInt("SkeletonType"); + + if (i == 1) { + cmp.putString("id", "WitherSkeleton"); + } else if (i == 2) { + cmp.putString("id", "Stray"); + } + + cmp.remove("SkeletonType"); + } + + return cmp; + } + } + + private static class DataConverterZombieType implements DataConverter { + + DataConverterZombieType() { + } + + public int getDataVersion() { + return 702; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Zombie".equals(cmp.getString("id"))) { + int i = cmp.getInt("ZombieType"); + + switch (i) { + case 0: + default: + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + cmp.putString("id", "ZombieVillager"); + cmp.putInt("Profession", i - 1); + break; + + case 6: + cmp.putString("id", "Husk"); + } + + cmp.remove("ZombieType"); + } + + return cmp; + } + } + + private static class DataConverterHorse implements DataConverter { + + DataConverterHorse() { + } + + public int getDataVersion() { + return 703; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("EntityHorse".equals(cmp.getString("id"))) { + int i = cmp.getInt("Type"); + + switch (i) { + case 0: + default: + cmp.putString("id", "Horse"); + break; + + case 1: + cmp.putString("id", "Donkey"); + break; + + case 2: + cmp.putString("id", "Mule"); + break; + + case 3: + cmp.putString("id", "ZombieHorse"); + break; + + case 4: + cmp.putString("id", "SkeletonHorse"); + } + + cmp.remove("Type"); + } + + return cmp; + } + } + + private static class DataConverterTileEntity implements DataConverter { + + private static final Map a = Maps.newHashMap(); + + DataConverterTileEntity() { + } + + public int getDataVersion() { + return 704; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = DataConverterTileEntity.a.get(cmp.getString("id")); + + if (s != null) { + cmp.putString("id", s); + } + + return cmp; + } + + static { + DataConverterTileEntity.a.put("Airportal", "minecraft:end_portal"); + DataConverterTileEntity.a.put("Banner", "minecraft:banner"); + DataConverterTileEntity.a.put("Beacon", "minecraft:beacon"); + DataConverterTileEntity.a.put("Cauldron", "minecraft:brewing_stand"); + DataConverterTileEntity.a.put("Chest", "minecraft:chest"); + DataConverterTileEntity.a.put("Comparator", "minecraft:comparator"); + DataConverterTileEntity.a.put("Control", "minecraft:command_block"); + DataConverterTileEntity.a.put("DLDetector", "minecraft:daylight_detector"); + DataConverterTileEntity.a.put("Dropper", "minecraft:dropper"); + DataConverterTileEntity.a.put("EnchantTable", "minecraft:enchanting_table"); + DataConverterTileEntity.a.put("EndGateway", "minecraft:end_gateway"); + DataConverterTileEntity.a.put("EnderChest", "minecraft:ender_chest"); + DataConverterTileEntity.a.put("FlowerPot", "minecraft:flower_pot"); + DataConverterTileEntity.a.put("Furnace", "minecraft:furnace"); + DataConverterTileEntity.a.put("Hopper", "minecraft:hopper"); + DataConverterTileEntity.a.put("MobSpawner", "minecraft:mob_spawner"); + DataConverterTileEntity.a.put("Music", "minecraft:noteblock"); + DataConverterTileEntity.a.put("Piston", "minecraft:piston"); + DataConverterTileEntity.a.put("RecordPlayer", "minecraft:jukebox"); + DataConverterTileEntity.a.put("Sign", "minecraft:sign"); + DataConverterTileEntity.a.put("Skull", "minecraft:skull"); + DataConverterTileEntity.a.put("Structure", "minecraft:structure_block"); + DataConverterTileEntity.a.put("Trap", "minecraft:dispenser"); + } + } + + private static class DataConverterEntity implements DataConverter { + + private static final Map a = Maps.newHashMap(); + + DataConverterEntity() { + } + + public int getDataVersion() { + return 704; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = DataConverterEntity.a.get(cmp.getString("id")); + + if (s != null) { + cmp.putString("id", s); + } + + return cmp; + } + + static { + DataConverterEntity.a.put("AreaEffectCloud", "minecraft:area_effect_cloud"); + DataConverterEntity.a.put("ArmorStand", "minecraft:armor_stand"); + DataConverterEntity.a.put("Arrow", "minecraft:arrow"); + DataConverterEntity.a.put("Bat", "minecraft:bat"); + DataConverterEntity.a.put("Blaze", "minecraft:blaze"); + DataConverterEntity.a.put("Boat", "minecraft:boat"); + DataConverterEntity.a.put("CaveSpider", "minecraft:cave_spider"); + DataConverterEntity.a.put("Chicken", "minecraft:chicken"); + DataConverterEntity.a.put("Cow", "minecraft:cow"); + DataConverterEntity.a.put("Creeper", "minecraft:creeper"); + DataConverterEntity.a.put("Donkey", "minecraft:donkey"); + DataConverterEntity.a.put("DragonFireball", "minecraft:dragon_fireball"); + DataConverterEntity.a.put("ElderGuardian", "minecraft:elder_guardian"); + DataConverterEntity.a.put("EnderCrystal", "minecraft:ender_crystal"); + DataConverterEntity.a.put("EnderDragon", "minecraft:ender_dragon"); + DataConverterEntity.a.put("Enderman", "minecraft:enderman"); + DataConverterEntity.a.put("Endermite", "minecraft:endermite"); + DataConverterEntity.a.put("EyeOfEnderSignal", "minecraft:eye_of_ender_signal"); + DataConverterEntity.a.put("FallingSand", "minecraft:falling_block"); + DataConverterEntity.a.put("Fireball", "minecraft:fireball"); + DataConverterEntity.a.put("FireworksRocketEntity", "minecraft:fireworks_rocket"); + DataConverterEntity.a.put("Ghast", "minecraft:ghast"); + DataConverterEntity.a.put("Giant", "minecraft:giant"); + DataConverterEntity.a.put("Guardian", "minecraft:guardian"); + DataConverterEntity.a.put("Horse", "minecraft:horse"); + DataConverterEntity.a.put("Husk", "minecraft:husk"); + DataConverterEntity.a.put("Item", "minecraft:item"); + DataConverterEntity.a.put("ItemFrame", "minecraft:item_frame"); + DataConverterEntity.a.put("LavaSlime", "minecraft:magma_cube"); + DataConverterEntity.a.put("LeashKnot", "minecraft:leash_knot"); + DataConverterEntity.a.put("MinecartChest", "minecraft:chest_minecart"); + DataConverterEntity.a.put("MinecartCommandBlock", "minecraft:commandblock_minecart"); + DataConverterEntity.a.put("MinecartFurnace", "minecraft:furnace_minecart"); + DataConverterEntity.a.put("MinecartHopper", "minecraft:hopper_minecart"); + DataConverterEntity.a.put("MinecartRideable", "minecraft:minecart"); + DataConverterEntity.a.put("MinecartSpawner", "minecraft:spawner_minecart"); + DataConverterEntity.a.put("MinecartTNT", "minecraft:tnt_minecart"); + DataConverterEntity.a.put("Mule", "minecraft:mule"); + DataConverterEntity.a.put("MushroomCow", "minecraft:mooshroom"); + DataConverterEntity.a.put("Ozelot", "minecraft:ocelot"); + DataConverterEntity.a.put("Painting", "minecraft:painting"); + DataConverterEntity.a.put("Pig", "minecraft:pig"); + DataConverterEntity.a.put("PigZombie", "minecraft:zombie_pigman"); + DataConverterEntity.a.put("PolarBear", "minecraft:polar_bear"); + DataConverterEntity.a.put("PrimedTnt", "minecraft:tnt"); + DataConverterEntity.a.put("Rabbit", "minecraft:rabbit"); + DataConverterEntity.a.put("Sheep", "minecraft:sheep"); + DataConverterEntity.a.put("Shulker", "minecraft:shulker"); + DataConverterEntity.a.put("ShulkerBullet", "minecraft:shulker_bullet"); + DataConverterEntity.a.put("Silverfish", "minecraft:silverfish"); + DataConverterEntity.a.put("Skeleton", "minecraft:skeleton"); + DataConverterEntity.a.put("SkeletonHorse", "minecraft:skeleton_horse"); + DataConverterEntity.a.put("Slime", "minecraft:slime"); + DataConverterEntity.a.put("SmallFireball", "minecraft:small_fireball"); + DataConverterEntity.a.put("SnowMan", "minecraft:snowman"); + DataConverterEntity.a.put("Snowball", "minecraft:snowball"); + DataConverterEntity.a.put("SpectralArrow", "minecraft:spectral_arrow"); + DataConverterEntity.a.put("Spider", "minecraft:spider"); + DataConverterEntity.a.put("Squid", "minecraft:squid"); + DataConverterEntity.a.put("Stray", "minecraft:stray"); + DataConverterEntity.a.put("ThrownEgg", "minecraft:egg"); + DataConverterEntity.a.put("ThrownEnderpearl", "minecraft:ender_pearl"); + DataConverterEntity.a.put("ThrownExpBottle", "minecraft:xp_bottle"); + DataConverterEntity.a.put("ThrownPotion", "minecraft:potion"); + DataConverterEntity.a.put("Villager", "minecraft:villager"); + DataConverterEntity.a.put("VillagerGolem", "minecraft:villager_golem"); + DataConverterEntity.a.put("Witch", "minecraft:witch"); + DataConverterEntity.a.put("WitherBoss", "minecraft:wither"); + DataConverterEntity.a.put("WitherSkeleton", "minecraft:wither_skeleton"); + DataConverterEntity.a.put("WitherSkull", "minecraft:wither_skull"); + DataConverterEntity.a.put("Wolf", "minecraft:wolf"); + DataConverterEntity.a.put("XPOrb", "minecraft:xp_orb"); + DataConverterEntity.a.put("Zombie", "minecraft:zombie"); + DataConverterEntity.a.put("ZombieHorse", "minecraft:zombie_horse"); + DataConverterEntity.a.put("ZombieVillager", "minecraft:zombie_villager"); + } + } + + private static class DataConverterPotionWater implements DataConverter { + + DataConverterPotionWater() { + } + + public int getDataVersion() { + return 806; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = cmp.getString("id"); + + if ("minecraft:potion".equals(s) || "minecraft:splash_potion".equals(s) || "minecraft:lingering_potion".equals(s) || "minecraft:tipped_arrow".equals(s)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (!nbttagcompound1.contains("Potion", 8)) { + nbttagcompound1.putString("Potion", "minecraft:water"); + } + + if (!cmp.contains("tag", 10)) { + cmp.put("tag", nbttagcompound1); + } + } + + return cmp; + } + } + + private static class DataConverterShulker implements DataConverter { + + DataConverterShulker() { + } + + public int getDataVersion() { + return 808; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:shulker".equals(cmp.getString("id")) && !cmp.contains("Color", 99)) { + cmp.putByte("Color", (byte) 10); + } + + return cmp; + } + } + + private static class DataConverterShulkerBoxItem implements DataConverter { + + public static final String[] a = new String[] { "minecraft:white_shulker_box", "minecraft:orange_shulker_box", "minecraft:magenta_shulker_box", "minecraft:light_blue_shulker_box", "minecraft:yellow_shulker_box", "minecraft:lime_shulker_box", "minecraft:pink_shulker_box", "minecraft:gray_shulker_box", "minecraft:silver_shulker_box", "minecraft:cyan_shulker_box", "minecraft:purple_shulker_box", "minecraft:blue_shulker_box", "minecraft:brown_shulker_box", "minecraft:green_shulker_box", "minecraft:red_shulker_box", "minecraft:black_shulker_box" }; + + DataConverterShulkerBoxItem() { + } + + public int getDataVersion() { + return 813; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:shulker_box".equals(cmp.getString("id")) && cmp.contains("tag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); + + if (nbttagcompound2.getList("Items", 10).isEmpty()) { + nbttagcompound2.remove("Items"); + } + + int i = nbttagcompound2.getInt("Color"); + + nbttagcompound2.remove("Color"); + if (nbttagcompound2.isEmpty()) { + nbttagcompound1.remove("BlockEntityTag"); + } + + if (nbttagcompound1.isEmpty()) { + cmp.remove("tag"); + } + + cmp.putString("id", DataConverterShulkerBoxItem.a[i % 16]); + } + } + + return cmp; + } + } + + private static class DataConverterShulkerBoxBlock implements DataConverter { + + DataConverterShulkerBoxBlock() { + } + + public int getDataVersion() { + return 813; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:shulker".equals(cmp.getString("id"))) { + cmp.remove("Color"); + } + + return cmp; + } + } + + private static class DataConverterLang implements DataConverter { + + DataConverterLang() { + } + + public int getDataVersion() { + return 816; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.contains("lang", 8)) { + cmp.putString("lang", cmp.getString("lang").toLowerCase(Locale.ROOT)); + } + + return cmp; + } + } + + private static class DataConverterTotem implements DataConverter { + + DataConverterTotem() { + } + + public int getDataVersion() { + return 820; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:totem".equals(cmp.getString("id"))) { + cmp.putString("id", "minecraft:totem_of_undying"); + } + + return cmp; + } + } + + private static class DataConverterBedBlock implements DataConverter { + + private static final Logger a = LogManager.getLogger(PaperweightDataConverters.class); + + DataConverterBedBlock() { + } + + public int getDataVersion() { + return 1125; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + try { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level"); + int i = nbttagcompound1.getInt("xPos"); + int j = nbttagcompound1.getInt("zPos"); + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("TileEntities", 10); + net.minecraft.nbt.ListTag nbttaglist1 = nbttagcompound1.getList("Sections", 10); + + for (int k = 0; k < nbttaglist1.size(); ++k) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist1.getCompound(k); + byte b0 = nbttagcompound2.getByte("Y"); + byte[] abyte = nbttagcompound2.getByteArray("Blocks"); + + for (int l = 0; l < abyte.length; ++l) { + if (416 == (abyte[l] & 255) << 4) { + int i1 = l & 15; + int j1 = l >> 8 & 15; + int k1 = l >> 4 & 15; + net.minecraft.nbt.CompoundTag nbttagcompound3 = new net.minecraft.nbt.CompoundTag(); + + nbttagcompound3.putString("id", "bed"); + nbttagcompound3.putInt("x", i1 + (i << 4)); + nbttagcompound3.putInt("y", j1 + (b0 << 4)); + nbttagcompound3.putInt("z", k1 + (j << 4)); + nbttaglist.add(nbttagcompound3); + } + } + } + } catch (Exception exception) { + DataConverterBedBlock.a.warn("Unable to datafix Bed blocks, level format may be missing tags."); + } + + return cmp; + } + } + + private static class DataConverterBedItem implements DataConverter { + + DataConverterBedItem() { + } + + public int getDataVersion() { + return 1125; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:bed".equals(cmp.getString("id")) && cmp.getShort("Damage") == 0) { + cmp.putShort("Damage", (short) DyeColor.RED.getId()); + } + + return cmp; + } + } + + private static class DataConverterSignText implements DataConverter { + + public static final Gson a = new GsonBuilder().registerTypeAdapter(Component.class, new JsonDeserializer() { + MutableComponent a(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + if (jsonelement.isJsonPrimitive()) { + return Component.literal(jsonelement.getAsString()); + } else if (jsonelement.isJsonArray()) { + JsonArray jsonarray = jsonelement.getAsJsonArray(); + MutableComponent ichatbasecomponent = null; + Iterator iterator = jsonarray.iterator(); + + while (iterator.hasNext()) { + JsonElement jsonelement1 = (JsonElement) iterator.next(); + MutableComponent ichatbasecomponent1 = this.a(jsonelement1, jsonelement1.getClass(), jsondeserializationcontext); + + if (ichatbasecomponent == null) { + ichatbasecomponent = ichatbasecomponent1; + } else { + ichatbasecomponent.append(ichatbasecomponent1); + } + } + + return ichatbasecomponent; + } else { + throw new JsonParseException("Don't know how to turn " + jsonelement + " into a Component"); + } + } + + public Object deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + return this.a(jsonelement, type, jsondeserializationcontext); + } + }).create(); + + DataConverterSignText() { + } + + public int getDataVersion() { + return 101; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Sign".equals(cmp.getString("id"))) { + this.convert(cmp, "Text1"); + this.convert(cmp, "Text2"); + this.convert(cmp, "Text3"); + this.convert(cmp, "Text4"); + } + + return cmp; + } + + private void convert(net.minecraft.nbt.CompoundTag nbttagcompound, String s) { + String s1 = nbttagcompound.getString(s); + Component object = null; + + if (!"null".equals(s1) && !StringUtil.isNullOrEmpty(s1)) { + if ((s1.charAt(0) != 34 || s1.charAt(s1.length() - 1) != 34) && (s1.charAt(0) != 123 || s1.charAt(s1.length() - 1) != 125)) { + object = Component.literal(s1); + } else { + try { + object = GsonHelper.fromJson(DataConverterSignText.a, s1, Component.class, true); + if (object == null) { + object = Component.literal(""); + } + } catch (JsonParseException jsonparseexception) { + ; + } + + if (object == null) { + try { + object = Component.Serializer.fromJson(s1); + } catch (JsonParseException jsonparseexception1) { + ; + } + } + + if (object == null) { + try { + object = Component.Serializer.fromJsonLenient(s1); + } catch (JsonParseException jsonparseexception2) { + ; + } + } + + if (object == null) { + object = Component.literal(s1); + } + } + } else { + object = Component.literal(""); + } + + nbttagcompound.putString(s, Component.Serializer.toJson(object)); + } + } + + private static class DataInspectorPlayerVehicle implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (cmp.contains("RootVehicle", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("RootVehicle"); + + if (nbttagcompound1.contains("Entity", 10)) { + convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); + } + } + + return cmp; + } + } + + private static class DataInspectorLevelPlayer implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (cmp.contains("Player", 10)) { + convertCompound(LegacyType.PLAYER, cmp, "Player", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorStructure implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + net.minecraft.nbt.ListTag nbttaglist; + int j; + net.minecraft.nbt.CompoundTag nbttagcompound1; + + if (cmp.contains("entities", 9)) { + nbttaglist = cmp.getList("entities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j); + if (nbttagcompound1.contains("nbt", 10)) { + convertCompound(LegacyType.ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); + } + } + } + + if (cmp.contains("blocks", 9)) { + nbttaglist = cmp.getList("blocks", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j); + if (nbttagcompound1.contains("nbt", 10)) { + convertCompound(LegacyType.BLOCK_ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); + } + } + } + + return cmp; + } + } + + private static class DataInspectorChunks implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (cmp.contains("Level", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level"); + net.minecraft.nbt.ListTag nbttaglist; + int j; + + if (nbttagcompound1.contains("Entities", 9)) { + nbttaglist = nbttagcompound1.getList("Entities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ENTITY, (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), sourceVer, targetVer)); + } + } + + if (nbttagcompound1.contains("TileEntities", 9)) { + nbttaglist = nbttagcompound1.getList("TileEntities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.BLOCK_ENTITY, (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), sourceVer, targetVer)); + } + } + } + + return cmp; + } + } + + private static class DataInspectorEntityPassengers implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (cmp.contains("Passengers", 9)) { + net.minecraft.nbt.ListTag nbttaglist = cmp.getList("Passengers", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ENTITY, nbttaglist.getCompound(j), sourceVer, targetVer)); + } + } + + return cmp; + } + } + + private static class DataInspectorPlayer implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + convertItems(cmp, "Inventory", sourceVer, targetVer); + convertItems(cmp, "EnderItems", sourceVer, targetVer); + if (cmp.contains("ShoulderEntityLeft", 10)) { + convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityLeft", sourceVer, targetVer); + } + + if (cmp.contains("ShoulderEntityRight", 10)) { + convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityRight", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorVillagers implements DataInspector { + ResourceLocation entityVillager = getKey("EntityVillager"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (entityVillager.equals(new ResourceLocation(cmp.getString("id"))) && cmp.contains("Offers", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Offers"); + + if (nbttagcompound1.contains("Recipes", 9)) { + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("Recipes", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompound(j); + + convertItem(nbttagcompound2, "buy", sourceVer, targetVer); + convertItem(nbttagcompound2, "buyB", sourceVer, targetVer); + convertItem(nbttagcompound2, "sell", sourceVer, targetVer); + nbttaglist.set(j, nbttagcompound2); + } + } + } + + return cmp; + } + } + + private static class DataInspectorMobSpawnerMinecart implements DataInspector { + ResourceLocation entityMinecartMobSpawner = getKey("EntityMinecartMobSpawner"); + ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + String s = cmp.getString("id"); + if (entityMinecartMobSpawner.equals(new ResourceLocation(s))) { + cmp.putString("id", tileEntityMobSpawner.toString()); + convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer); + cmp.putString("id", s); + } + + return cmp; + } + } + + private static class DataInspectorMobSpawnerMobs implements DataInspector { + ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (tileEntityMobSpawner.equals(new ResourceLocation(cmp.getString("id")))) { + if (cmp.contains("SpawnPotentials", 9)) { + net.minecraft.nbt.ListTag nbttaglist = cmp.getList("SpawnPotentials", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttaglist.getCompound(j); + + convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); + } + } + + convertCompound(LegacyType.ENTITY, cmp, "SpawnData", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorCommandBlock implements DataInspector { + ResourceLocation tileEntityCommand = getKey("TileEntityCommand"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (tileEntityCommand.equals(new ResourceLocation(cmp.getString("id")))) { + cmp.putString("id", "Control"); + convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer); + cmp.putString("id", "MinecartCommandBlock"); + } + + return cmp; + } + } +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightFakePlayer.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightFakePlayer.java new file mode 100644 index 000000000..c27f4a59a --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightFakePlayer.java @@ -0,0 +1,98 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3; + +import com.mojang.authlib.GameProfile; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ClientInformation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.stats.Stat; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.HumanoidArm; +import net.minecraft.world.entity.player.ChatVisiblity; +import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.phys.Vec3; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import java.util.OptionalInt; +import java.util.UUID; + +class PaperweightFakePlayer extends ServerPlayer { + private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]"); + private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D); + private static final ClientInformation FAKE_CLIENT_INFO = new ClientInformation( + "en_US", 16, ChatVisiblity.FULL, true, 0, HumanoidArm.LEFT, false, false + ); + + PaperweightFakePlayer(ServerLevel world) { + super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE, FAKE_CLIENT_INFO); + } + + @Override + public Vec3 position() { + return ORIGIN; + } + + @Override + public void tick() { + } + + @Override + public void die(DamageSource damagesource) { + } + + @Override + public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) { + return this; + } + + @Override + public OptionalInt openMenu(MenuProvider factory) { + return OptionalInt.empty(); + } + + @Override + public void updateOptions(ClientInformation clientOptions) { + } + + @Override + public void displayClientMessage(Component message, boolean actionBar) { + } + + @Override + public void awardStat(Stat stat, int amount) { + } + + @Override + public void awardStat(Stat stat) { + } + + @Override + public boolean isInvulnerableTo(DamageSource damageSource) { + return true; + } + + @Override + public void openTextEdit(SignBlockEntity sign, boolean front) { + } +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightWorldNativeAccess.java new file mode 100644 index 000000000..b50ead936 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightWorldNativeAccess.java @@ -0,0 +1,181 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import com.sk89q.worldedit.world.block.BlockState; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.chunk.LevelChunk; +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 java.lang.ref.WeakReference; +import java.util.Objects; +import javax.annotation.Nullable; + +public class PaperweightWorldNativeAccess implements WorldNativeAccess { + private static final int UPDATE = 1; + private static final int NOTIFY = 2; + + private final PaperweightAdapter adapter; + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference world) { + this.adapter = adapter; + this.world = world; + } + + private ServerLevel getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public LevelChunk getChunk(int x, int z) { + return getWorld().getChunk(x, z); + } + + @Override + public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.stateById(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); + } + + @Override + public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) { + return chunk.getBlockState(position); + } + + @Nullable + @Override + public net.minecraft.world.level.block.state.BlockState setBlockState(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState state) { + return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE)); + } + + @Override + public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(net.minecraft.world.level.block.state.BlockState block, BlockPos position) { + return Block.updateFromNeighbourShapes(block, getWorld(), position); + } + + @Override + public BlockPos getPosition(int x, int y, int z) { + return new BlockPos(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPos position) { + getWorld().getChunkSource().getLightEngine().checkBlock(position); + } + + @Override + public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) { + return false; + } + + @Override + public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { + if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { + getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); + } + } + + @Override + public boolean isChunkTicking(LevelChunk chunk) { + return chunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); + } + + @Override + public void markBlockChanged(LevelChunk chunk, BlockPos position) { + if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { + getWorld().getChunkSource().blockChanged(position); + } + } + + @Override + public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { + ServerLevel world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.updateNeighborsAt(pos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + Block block = oldState.getBlock(); + fireNeighborChanged(pos, world, block, pos.west()); + fireNeighborChanged(pos, world, block, pos.east()); + fireNeighborChanged(pos, world, block, pos.below()); + fireNeighborChanged(pos, world, block, pos.above()); + fireNeighborChanged(pos, world, block, pos.north()); + fireNeighborChanged(pos, world, block, pos.south()); + } + if (newState.hasAnalogOutputSignal()) { + world.updateNeighbourForOutputSignal(pos, newState.getBlock()); + } + } + + // Not sure why neighborChanged is deprecated + @SuppressWarnings("deprecation") + private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { + world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false); + } + + @Override + public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) { + ServerLevel world = getWorld(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = world.getWorld(); + BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState)); + world.getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); + newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); + } + + @Override + public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { + getWorld().onBlockStateChange(pos, oldState, newState); + } + + @Override + public void flush() { + + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java new file mode 100644 index 000000000..edbe268fe --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java @@ -0,0 +1,185 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.google.common.base.Suppliers; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.util.ReflectionUtil; +import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.EmptyBlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.PushReaction; +import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; + +public class PaperweightBlockMaterial implements BlockMaterial { + + private final Block block; + private final BlockState blockState; + private final boolean isTranslucent; + private final CraftBlockData craftBlockData; + private final org.bukkit.Material craftMaterial; + private final int opacity; + private final CompoundTag tile; + + public PaperweightBlockMaterial(Block block) { + this(block, block.defaultBlockState()); + } + + public PaperweightBlockMaterial(Block block, BlockState blockState) { + this.block = block; + this.blockState = blockState; + this.craftBlockData = CraftBlockData.fromData(blockState); + this.craftMaterial = craftBlockData.getMaterial(); + BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block, + Refraction.pickName("properties", "aP")); + this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo, + Refraction.pickName("canOcclude", "n") + ); + opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); + BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity( + BlockPos.ZERO, + blockState + ); + tile = tileEntity == null + ? null + : new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); + } + + public Block getBlock() { + return block; + } + + public BlockState getState() { + return blockState; + } + + public CraftBlockData getCraftBlockData() { + return craftBlockData; + } + + @Override + public boolean isAir() { + return blockState.isAir(); + } + + @Override + public boolean isFullCube() { + return craftMaterial.isOccluding(); + } + + @Override + public boolean isOpaque() { + return blockState.isOpaque(); + } + + @Override + public boolean isPowerSource() { + return blockState.isSignalSource(); + } + + @Override + public boolean isLiquid() { + // TODO: Better check ? + return block instanceof LiquidBlock; + } + + @Override + public boolean isSolid() { + // TODO: Replace + return blockState.isSolid(); + } + + @Override + public float getHardness() { + return craftBlockData.getState().destroySpeed; + } + + @Override + public float getResistance() { + return block.getExplosionResistance(); + } + + @Override + public float getSlipperiness() { + return block.getFriction(); + } + + @Override + public int getLightValue() { + return blockState.getLightEmission(); + } + + @Override + public int getLightOpacity() { + return opacity; + } + + @Override + public boolean isFragileWhenPushed() { + return blockState.getPistonPushReaction() == PushReaction.DESTROY; + } + + @Override + public boolean isUnpushable() { + return blockState.getPistonPushReaction() == PushReaction.BLOCK; + } + + @Override + public boolean isTicksRandomly() { + return block.isRandomlyTicking(blockState); + } + + @Override + public boolean isMovementBlocker() { + return craftMaterial.isSolid(); + } + + @Override + public boolean isBurnable() { + return craftMaterial.isBurnable(); + } + + @Override + public boolean isToolRequired() { + // Removed in 1.16.1, this is not present in higher versions + return false; + } + + @Override + public boolean isReplacedDuringPlacement() { + return blockState.canBeReplaced(); + } + + @Override + public boolean isTranslucent() { + return isTranslucent; + } + + @Override + public boolean hasContainer() { + return block instanceof EntityBlock; + } + + @Override + public boolean isTile() { + return block instanceof EntityBlock; + } + + @Override + public CompoundTag getDefaultTile() { + return tile; + } + + @Override + public int getMapColor() { + // rgb field + return block.defaultMapColor().col; + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java new file mode 100644 index 000000000..4414719b8 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweAdapter.java @@ -0,0 +1,617 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; +import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; +import com.fastasyncworldedit.core.FaweCache; +import com.fastasyncworldedit.core.entity.LazyBaseEntity; +import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; +import com.fastasyncworldedit.core.queue.IBatchProcessor; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; +import com.fastasyncworldedit.core.util.NbtUtils; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.regen.PaperweightRegen; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.registry.state.BooleanProperty; +import com.sk89q.worldedit.registry.state.DirectionalProperty; +import com.sk89q.worldedit.registry.state.EnumProperty; +import com.sk89q.worldedit.registry.state.IntegerProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.nbt.BinaryTag; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import com.sk89q.worldedit.util.nbt.StringBinaryTag; +import com.sk89q.worldedit.world.RegenOptions; +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; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import io.papermc.lib.PaperLib; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.core.WritableRegistry; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.chunk.LevelChunk; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_20_R3.util.CraftNamespacedKey; +import org.bukkit.entity.Player; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.OptionalInt; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static net.minecraft.core.registries.Registries.BIOME; + +public final class PaperweightFaweAdapter extends FaweAdapter { + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE; + + static { + try { + CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave"); + } catch (NoSuchMethodException ignored) { // may not be present in newer paper versions + } + } + + private final com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter parent; + // ------------------------------------------------------------------------ + // Code that may break between versions of Minecraft + // ------------------------------------------------------------------------ + private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil(); + private char[] ibdToStateOrdinal = null; + private int[] ordinalToIbdID = null; + private boolean initialised = false; + private Map>> allBlockProperties = null; + + public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException { + this.parent = new com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3.PaperweightAdapter(); + } + + @Nullable + private static String getEntityId(Entity entity) { + ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); + return resourceLocation == null ? null : resourceLocation.toString(); + } + + private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) { + entity.save(compoundTag); + } + + @Override + public BukkitImplAdapter getParent() { + return parent; + } + + private synchronized boolean init() { + if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) { + return false; + } + ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size + ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size + for (int i = 0; i < ibdToStateOrdinal.length; i++) { + BlockState blockState = BlockTypesCache.states[i]; + PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial(); + int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState()); + char ordinal = blockState.getOrdinalChar(); + ibdToStateOrdinal[id] = ordinal; + ordinalToIbdID[ordinal] = id; + } + Map>> properties = new HashMap<>(); + try { + for (Field field : BlockStateProperties.class.getDeclaredFields()) { + Object obj = field.get(null); + if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property state)) { + continue; + } + Property property; + if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { + property = new BooleanProperty( + state.getName(), + (List) ImmutableList.copyOf(state.getPossibleValues()) + ); + } else if (state instanceof DirectionProperty) { + property = new DirectionalProperty( + state.getName(), + state + .getPossibleValues() + .stream() + .map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) + .collect(Collectors.toList()) + ); + } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { + property = new EnumProperty( + state.getName(), + state + .getPossibleValues() + .stream() + .map(e -> ((StringRepresentable) e).getSerializedName()) + .collect(Collectors.toList()) + ); + } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { + property = new IntegerProperty( + state.getName(), + (List) ImmutableList.copyOf(state.getPossibleValues()) + ); + } else { + throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state + .getClass() + .getSimpleName()); + } + properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> { + if (v == null) { + v = new ArrayList<>(Collections.singletonList(property)); + } else { + v.add(property); + } + return v; + }); + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } finally { + allBlockProperties = ImmutableMap.copyOf(properties); + } + initialised = true; + return true; + } + + @Override + public BlockMaterial getMaterial(BlockType blockType) { + Block block = getBlock(blockType); + return new PaperweightBlockMaterial(block); + } + + @Override + public synchronized BlockMaterial getMaterial(BlockState state) { + net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + return new PaperweightBlockMaterial(blockState.getBlock(), blockState); + } + + public Block getBlock(BlockType blockType) { + return DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.BLOCK) + .get(new ResourceLocation(blockType.getNamespace(), blockType.getResource())); + } + + @Deprecated + @Override + public BlockState getBlock(Location location) { + Preconditions.checkNotNull(location); + + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + final ServerLevel handle = getServerLevel(location.getWorld()); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + final BlockPos blockPos = new BlockPos(x, y, z); + final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); + BlockState state = adapt(blockData); + if (state == null) { + org.bukkit.block.Block bukkitBlock = location.getBlock(); + state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); + } + return state; + } + + @Override + public BaseBlock getFullBlock(final Location location) { + Preconditions.checkNotNull(location); + + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = getServerLevel(location.getWorld()); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + final BlockPos blockPos = new BlockPos(x, y, z); + final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); + BlockState state = adapt(blockData); + if (state == null) { + org.bukkit.block.Block bukkitBlock = location.getBlock(); + state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); + } + if (state.getBlockType().getMaterial().hasContainer()) { + + // Read the NBT data + BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); + if (blockEntity != null) { + net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(); + return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag)); + } + } + + return state.toBaseBlock(); + } + + @Override + public Set getSupportedSideEffects() { + return SideEffectSet.defaults().getSideEffectsToApply(); + } + + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world))); + } + + @Override + public BaseEntity getEntity(org.bukkit.entity.Entity entity) { + Preconditions.checkNotNull(entity); + + CraftEntity craftEntity = ((CraftEntity) entity); + Entity mcEntity = craftEntity.getHandle(); + + String id = getEntityId(mcEntity); + + if (id != null) { + EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); + Supplier saveTag = () -> { + final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); + readEntityIntoTag(mcEntity, minecraftTag); + //add Id for AbstractChangeSet to work + final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag); + final Map tags = NbtUtils.getCompoundBinaryTagValues(tag); + tags.put("Id", StringBinaryTag.of(id)); + return CompoundBinaryTag.from(tags); + }; + return new LazyBaseEntity(type, saveTag); + } else { + return null; + } + } + + @Override + public Component getRichBlockName(BlockType blockType) { + return parent.getRichBlockName(blockType); + } + + @Override + public Component getRichItemName(ItemType itemType) { + return parent.getRichItemName(itemType); + } + + @Override + public Component getRichItemName(BaseItemStack itemStack) { + return parent.getRichItemName(itemStack); + } + + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial(); + net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState(); + return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState)); + } + + @Override + public BlockState adapt(BlockData blockData) { + CraftBlockData cbd = ((CraftBlockData) blockData); + net.minecraft.world.level.block.state.BlockState ibd = cbd.getState(); + return adapt(ibd); + } + + public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) { + return BlockTypesCache.states[adaptToChar(blockState)]; + } + + public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) { + int id = Block.BLOCK_STATE_REGISTRY.getId(blockState); + if (initialised) { + return ibdToStateOrdinal[id]; + } + synchronized (this) { + if (initialised) { + return ibdToStateOrdinal[id]; + } + try { + init(); + return ibdToStateOrdinal[id]; + } catch (ArrayIndexOutOfBoundsException e1) { + LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!", + blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1 + ); + return BlockTypesCache.ReservedIDs.AIR; + } + } + } + + public char ibdIDToOrdinal(int id) { + if (initialised) { + return ibdToStateOrdinal[id]; + } + synchronized (this) { + if (initialised) { + return ibdToStateOrdinal[id]; + } + init(); + return ibdToStateOrdinal[id]; + } + } + + @Override + public char[] getIbdToStateOrdinal() { + if (initialised) { + return ibdToStateOrdinal; + } + synchronized (this) { + if (initialised) { + return ibdToStateOrdinal; + } + init(); + return ibdToStateOrdinal; + } + } + + public int ordinalToIbdID(char ordinal) { + if (initialised) { + return ordinalToIbdID[ordinal]; + } + synchronized (this) { + if (initialised) { + return ordinalToIbdID[ordinal]; + } + init(); + return ordinalToIbdID[ordinal]; + } + } + + @Override + public int[] getOrdinalToIbdID() { + if (initialised) { + return ordinalToIbdID; + } + synchronized (this) { + if (initialised) { + return ordinalToIbdID; + } + init(); + return ordinalToIbdID; + } + } + + @Override + public > BlockData adapt(B state) { + PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial(); + return material.getCraftBlockData(); + } + + @Override + public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { + ServerLevel nmsWorld = getServerLevel(world); + ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); + if (map != null && wasAccessibleSinceLastSave(map)) { + boolean flag = false; + // PlayerChunk.d players = map.players; + Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) + */ Stream.empty(); + + ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); + stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) + .forEach(entityPlayer -> { + synchronized (chunkPacket) { + ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket(); + if (nmsPacket == null) { + nmsPacket = mapUtil.create(this, chunkPacket); + chunkPacket.setNativePacket(nmsPacket); + } + try { + FaweCache.INSTANCE.CHUNK_FLAG.get().set(true); + entityPlayer.connection.send(nmsPacket); + } finally { + FaweCache.INSTANCE.CHUNK_FLAG.get().set(false); + } + } + }); + } + } + + @Override + public Map> getProperties(BlockType blockType) { + return getParent().getProperties(blockType); + } + + @Override + public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) { + int internalId = BlockStateIdAccess.getBlockStateId(blockState); + net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); + return blockState1.hasPostProcess( + getServerLevel(world), + new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) + ); + } + + @Override + public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { + ItemStack stack = new ItemStack( + DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.ITEM) + .get(ResourceLocation.tryParse(baseItemStack.getType().getId())), + baseItemStack.getAmount() + ); + stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData()))); + return CraftItemStack.asCraftMirror(stack); + } + + @Override + protected void preCaptureStates(final ServerLevel serverLevel) { + serverLevel.captureTreeGeneration = true; + serverLevel.captureBlockStates = true; + } + + @Override + protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { + return new ArrayList<>(serverLevel.capturedBlockStates.values()); + } + + @Override + protected void postCaptureBlockStates(final ServerLevel serverLevel) { + serverLevel.captureBlockStates = false; + serverLevel.captureTreeGeneration = false; + serverLevel.capturedBlockStates.clear(); + } + + @Override + protected ServerLevel getServerLevel(final World world) { + return ((CraftWorld) world).getHandle(); + } + + @Override + public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { + final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); + final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount()); + weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag()))); + return weStack; + } + + @Override + public Tag toNative(net.minecraft.nbt.Tag foreign) { + return parent.toNative(foreign); + } + + @Override + public net.minecraft.nbt.Tag fromNative(Tag foreign) { + if (foreign instanceof PaperweightLazyCompoundTag) { + return ((PaperweightLazyCompoundTag) foreign).get(); + } + return parent.fromNative(foreign); + } + + @Override + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { + return new PaperweightRegen(bukkitWorld, region, target, options).regenerate(); + } + + @Override + public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) { + return new PaperweightGetBlocks(world, chunkX, chunkZ); + } + + @Override + public int getInternalBiomeId(BiomeType biomeType) { + final Registry registry = MinecraftServer + .getServer() + .registryAccess() + .registryOrThrow(BIOME); + ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId()); + Biome biome = registry.get(resourceLocation); + return registry.getId(biome); + } + + @Override + public Iterable getRegisteredBiomes() { + WritableRegistry biomeRegistry = (WritableRegistry) ((CraftServer) Bukkit.getServer()) + .getServer() + .registryAccess() + .registryOrThrow(BIOME); + List keys = biomeRegistry.stream() + .map(biomeRegistry::getKey).filter(Objects::nonNull).toList(); + List namespacedKeys = new ArrayList<>(); + for (ResourceLocation key : keys) { + try { + namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key)); + } catch (IllegalArgumentException e) { + LOGGER.error("Error converting biome key {}", key.toString(), e); + } + } + return namespacedKeys; + } + + @Override + public RelighterFactory getRelighterFactory() { + if (PaperLib.isPaper()) { + return new PaperweightStarlightRelighterFactory(); + } else { + return new NMSRelighterFactory(); + } + } + + @Override + public Map>> getAllProperties() { + if (initialised) { + return allBlockProperties; + } + synchronized (this) { + if (initialised) { + return allBlockProperties; + } + init(); + return allBlockProperties; + } + } + + @Override + public IBatchProcessor getTickingPostProcessor() { + return new PaperweightPostProcessor(); + } + + private boolean wasAccessibleSinceLastSave(ChunkHolder holder) { + if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) { + try { + return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder); + } catch (IllegalAccessException | InvocationTargetException ignored) { + // fall-through + } + } + // Papers new chunk system has no related replacement - therefor we assume true. + return true; + } + +} 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 new file mode 100644 index 000000000..ad04e7b03 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightFaweWorldNativeAccess.java @@ -0,0 +1,286 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.fastasyncworldedit.core.Fawe; +import com.fastasyncworldedit.core.math.IntPair; +import com.fastasyncworldedit.core.util.TaskManager; +import com.fastasyncworldedit.core.util.task.RunnableVal; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import com.sk89q.worldedit.world.block.BlockState; +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.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.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; +import java.util.concurrent.atomic.AtomicInteger; + +public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess { + + private static final int UPDATE = 1; + private static final int NOTIFY = 2; + private static final Direction[] NEIGHBOUR_ORDER = { + Direction.EAST, + Direction.WEST, + Direction.DOWN, + Direction.UP, + Direction.NORTH, + Direction.SOUTH + }; + private final PaperweightFaweAdapter paperweightFaweAdapter; + private final WeakReference level; + private final AtomicInteger lastTick; + private final Set cachedChanges = new HashSet<>(); + private final Set cachedChunksToSend = new HashSet<>(); + private SideEffectSet sideEffectSet; + + public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference level) { + this.paperweightFaweAdapter = paperweightFaweAdapter; + 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); + } + + private Level getLevel() { + return Objects.requireNonNull(level.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public LevelChunk getChunk(int x, int z) { + return getLevel().getChunk(x, z); + } + + @Override + public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) { + int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar()); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.stateById(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState(); + } + + @Override + public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) { + return levelChunk.getBlockState(blockPos); + } + + @Nullable + @Override + public synchronized net.minecraft.world.level.block.state.BlockState setBlockState( + LevelChunk levelChunk, BlockPos blockPos, + net.minecraft.world.level.block.state.BlockState blockState + ) { + int currentTick = MinecraftServer.currentTick; + if (Fawe.isMainThread()) { + 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); + } + return blockState; + } + + @Override + public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition( + net.minecraft.world.level.block.state.BlockState blockState, + BlockPos blockPos + ) { + return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos); + } + + @Override + public BlockPos getPosition(int x, int y, int z) { + return new BlockPos(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPos blockPos) { + getLevel().getChunkSource().getLightEngine().checkBlock(blockPos); + } + + @Override + public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) { + // We will assume that the tile entity was created for us, + // though we do not do this on the other versions + BlockEntity blockEntity = getLevel().getBlockEntity(blockPos); + if (blockEntity == null) { + return false; + } + net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeBinary(tag); + blockEntity.load((CompoundTag) nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate( + LevelChunk levelChunk, BlockPos blockPos, + net.minecraft.world.level.block.state.BlockState oldState, + net.minecraft.world.level.block.state.BlockState newState + ) { + if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) { + getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY); + } + } + + @Override + public boolean isChunkTicking(LevelChunk levelChunk) { + return levelChunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING); + } + + @Override + public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) { + if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) { + ((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos); + } + } + + @Override + public void notifyNeighbors( + BlockPos blockPos, + net.minecraft.world.level.block.state.BlockState oldState, + net.minecraft.world.level.block.state.BlockState newState + ) { + Level level = getLevel(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + level.blockUpdated(blockPos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + // Un-nest neighbour updating + for (Direction direction : NEIGHBOUR_ORDER) { + BlockPos shifted = blockPos.relative(direction); + level.getBlockState(shifted).neighborChanged(level, shifted, oldState.getBlock(), blockPos, false); + } + } + if (newState.hasAnalogOutputSignal()) { + level.updateNeighbourForOutputSignal(blockPos, newState.getBlock()); + } + } + + @Override + public void updateNeighbors( + BlockPos blockPos, + net.minecraft.world.level.block.state.BlockState oldState, + net.minecraft.world.level.block.state.BlockState newState, + int recursionLimit + ) { + Level level = getLevel(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = level.getWorld(); + if (craftWorld != null) { + BlockPhysicsEvent event = new BlockPhysicsEvent( + craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()), + CraftBlockData.fromData(newState) + ); + level.getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + } + newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit); + newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit); + } + + @Override + public void onBlockStateChange( + BlockPos blockPos, + net.minecraft.world.level.block.state.BlockState oldState, + net.minecraft.world.level.block.state.BlockState newState + ) { + 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(); + } + RunnableVal runnableVal = new RunnableVal<>() { + @Override + public void run(Object value) { + changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState, + sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE) + )); + if (!sendChunks) { + return; + } + for (IntPair chunk : toSend) { + PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); + } + } + }; + TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal)); + } + + @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(); + } + + private record CachedChange( + LevelChunk levelChunk, + BlockPos blockPos, + net.minecraft.world.level.block.state.BlockState blockState + ) { + + } + +} 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 new file mode 100644 index 000000000..8139adc87 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -0,0 +1,1181 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; +import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; +import com.fastasyncworldedit.core.Fawe; +import com.fastasyncworldedit.core.FaweCache; +import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.math.BitArrayUnstretched; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.IChunkSet; +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.collection.AdaptedMap; +import com.google.common.base.Suppliers; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag; +import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import io.papermc.lib.PaperLib; +import io.papermc.paper.event.block.BeaconDeactivatedEvent; +import net.minecraft.core.*; +import net.minecraft.nbt.IntTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.BitStorage; +import net.minecraft.util.ZeroBitStorage; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.entity.BeaconBlockEntity; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.*; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.lighting.LevelLightEngine; +import org.apache.logging.log4j.Logger; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.block.CraftBlock; +import org.bukkit.event.entity.CreatureSpawnEvent; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static net.minecraft.core.registries.Registries.BIOME; + +public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); + private static final Function nmsTile2We = + tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId)); + private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin + .getInstance() + .getBukkitImplAdapter()); + private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); + private final ReentrantLock callLock = new ReentrantLock(); + private final ServerLevel serverLevel; + private final int chunkX; + private final int chunkZ; + private final int minHeight; + private final int maxHeight; + private final int minSectionPosition; + private final int maxSectionPosition; + private final Registry biomeRegistry; + private final IdMap> biomeHolderIdMap; + private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); + private final Object sendLock = new Object(); + private LevelChunkSection[] sections; + private LevelChunk levelChunk; + private DataLayer[] blockLight; + private DataLayer[] skyLight; + private boolean createCopy = false; + private boolean forceLoadSections = true; + private boolean lightUpdate = false; + private int copyKey = 0; + + public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { + this(((CraftWorld) world).getHandle(), chunkX, chunkZ); + } + + public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { + super(serverLevel.getMinBuildHeight() >> 4, (serverLevel.getMaxBuildHeight() - 1) >> 4); + this.serverLevel = serverLevel; + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.minHeight = serverLevel.getMinBuildHeight(); + this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. + this.minSectionPosition = minHeight >> 4; + this.maxSectionPosition = maxHeight >> 4; + this.skyLight = new DataLayer[getSectionCount()]; + this.blockLight = new DataLayer[getSectionCount()]; + this.biomeRegistry = serverLevel.registryAccess().registryOrThrow(BIOME); + this.biomeHolderIdMap = biomeRegistry.asHolderIdMap(); + } + + public int getChunkX() { + return chunkX; + } + + public int getChunkZ() { + return chunkZ; + } + + @Override + public boolean isCreateCopy() { + return createCopy; + } + + @Override + public int setCreateCopy(boolean createCopy) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); + } + this.createCopy = createCopy; + return ++this.copyKey; + } + + @Override + public IChunkGet getCopy(final int key) { + return copies.remove(key); + } + + @Override + public void lockCall() { + this.callLock.lock(); + } + + @Override + public void unlockCall() { + this.callLock.unlock(); + } + + @Override + public void setLightingToGet(char[][] light, int minSectionPosition, int maxSectionPosition) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setSkyLightingToGet(char[][] light, int minSectionPosition, int maxSectionPosition) { + if (light != null) { + lightUpdate = true; + try { + fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) { + // height + 1 to match server internal + BitArrayUnstretched bitArray = new BitArrayUnstretched(MathMan.log2nlz(getChunk().getHeight() + 1), 256); + bitArray.fromRaw(data); + Heightmap.Types nativeType = Heightmap.Types.valueOf(type.name()); + Heightmap heightMap = getChunk().heightmaps.get(nativeType); + heightMap.setRawData(getChunk(), nativeType, bitArray.getData()); + } + + @Override + public int getMaxY() { + return maxHeight; + } + + @Override + public int getMinY() { + return minHeight; + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + LevelChunkSection section = getSections(false)[(y >> 4) - getMinSectionPosition()]; + Holder biomes = section.getNoiseBiome(x >> 2, (y & 15) >> 2, z >> 2); + return PaperweightPlatformAdapter.adapt(biomes, serverLevel); + } + + @Override + public void removeSectionLighting(int layer, boolean sky) { + SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( + sectionPos); + if (dataLayer != null) { + lightUpdate = true; + synchronized (dataLayer) { + byte[] bytes = dataLayer.getData(); + Arrays.fill(bytes, (byte) 0); + } + } + if (sky) { + SectionPos sectionPos1 = SectionPos.of(getChunk().getPos(), layer); + DataLayer dataLayer1 = serverLevel + .getChunkSource() + .getLightEngine() + .getLayerListener(LightLayer.SKY) + .getDataLayerData(sectionPos1); + if (dataLayer1 != null) { + lightUpdate = true; + synchronized (dataLayer1) { + byte[] bytes = dataLayer1.getData(); + Arrays.fill(bytes, (byte) 0); + } + } + } + } + + @Override + public CompoundTag getTile(int x, int y, int z) { + BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + ( + chunkX << 4), y, (z & 15) + ( + chunkZ << 4))); + if (blockEntity == null) { + return null; + } + return new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)); + } + + @Override + public Map getTiles() { + Map nmsTiles = getChunk().getBlockEntities(); + if (nmsTiles.isEmpty()) { + return Collections.emptyMap(); + } + return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); + } + + @Override + public int getSkyLight(int x, int y, int z) { + int layer = y >> 4; + int alayer = layer - getMinSectionPosition(); + if (skyLight[alayer] == null) { + SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); + DataLayer dataLayer = + serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); + // If the server hasn't generated the section's NibbleArray yet, it will be null + if (dataLayer == null) { + byte[] LAYER_COUNT = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(LAYER_COUNT, (byte) 15); + dataLayer = new DataLayer(LAYER_COUNT); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( + LightLayer.BLOCK, + sectionPos, + dataLayer + ); + } + skyLight[alayer] = dataLayer; + } + return skyLight[alayer].get(x & 15, y & 15, z & 15); + } + + @Override + public int getEmittedLight(int x, int y, int z) { + int layer = y >> 4; + int alayer = layer - getMinSectionPosition(); + if (blockLight[alayer] == null) { + serverLevel.getRawBrightness(new BlockPos(1, 1, 1), 5); + SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); + DataLayer dataLayer = serverLevel + .getChunkSource() + .getLightEngine() + .getLayerListener(LightLayer.BLOCK) + .getDataLayerData(sectionPos); + // If the server hasn't generated the section's DataLayer yet, it will be null + if (dataLayer == null) { + byte[] LAYER_COUNT = new byte[2048]; + // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. + Arrays.fill(LAYER_COUNT, (byte) 15); + dataLayer = new DataLayer(LAYER_COUNT); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, + dataLayer + ); + } + blockLight[alayer] = dataLayer; + } + return blockLight[alayer].get(x & 15, y & 15, z & 15); + } + + @Override + public int[] getHeightMap(HeightMapType type) { + long[] longArray = getChunk().heightmaps.get(Heightmap.Types.valueOf(type.name())).getRawData(); + BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256, longArray); + return bitArray.toRaw(new int[256]); + } + + @Override + public CompoundTag getEntity(UUID uuid) { + ensureLoaded(serverLevel, chunkX, chunkZ); + List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + Entity entity = null; + for (Entity e : entities) { + if (e.getUUID().equals(uuid)) { + entity = e; + break; + } + } + if (entity != null) { + org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); + return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); + } + for (CompoundTag tag : getEntities()) { + if (uuid.equals(tag.getUUID())) { + return tag; + } + } + return null; + } + + @Override + public Set getEntities() { + ensureLoaded(serverLevel, chunkX, chunkZ); + List entities = PaperweightPlatformAdapter.getEntities(getChunk()); + if (entities.isEmpty()) { + return Collections.emptySet(); + } + int size = entities.size(); + return new AbstractSet<>() { + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof CompoundTag getTag)) { + return false; + } + UUID getUUID = getTag.getUUID(); + for (Entity entity : entities) { + UUID uuid = entity.getUUID(); + if (uuid.equals(getUUID)) { + return true; + } + } + return false; + } + + @Nonnull + @Override + public Iterator iterator() { + Iterable result = entities.stream().map(input -> { + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + input.save(tag); + return (CompoundTag) adapter.toNative(tag); + }).collect(Collectors.toList()); + return result.iterator(); + } + }; + } + + private void removeEntity(Entity entity) { + entity.discard(); + } + + public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { + return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ); + } + + @Override + @SuppressWarnings("rawtypes") + public synchronized > T call(IChunkSet set, Runnable finalizer) { + if (!callLock.isHeldByCurrentThread()) { + throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); + } + forceLoadSections = false; + PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; + if (createCopy) { + if (copies.containsKey(copyKey)) { + throw new IllegalStateException("Copy key already used."); + } + copies.put(copyKey, copy); + } + try { + ServerLevel nmsWorld = serverLevel; + LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); + + // Remove existing tiles. Create a copy so that we can remove blocks + Map chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); + List beacons = null; + if (!chunkTiles.isEmpty()) { + for (Map.Entry entry : chunkTiles.entrySet()) { + final BlockPos pos = entry.getKey(); + final int lx = pos.getX() & 15; + final int ly = pos.getY(); + final int lz = pos.getZ() & 15; + final int layer = ly >> 4; + if (!set.hasSection(layer)) { + continue; + } + + int ordinal = set.getBlock(lx, ly, lz).getOrdinal(); + if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) { + BlockEntity tile = entry.getValue(); + if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) { + if (beacons == null) { + beacons = new ArrayList<>(); + } + beacons.add(tile); + PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); + continue; + } + nmsChunk.removeBlockEntity(tile.getBlockPos()); + if (createCopy) { + copy.storeTile(tile); + } + } + } + } + final BiomeType[][] biomes = set.getBiomes(); + + int bitMask = 0; + synchronized (nmsChunk) { + LevelChunkSection[] levelChunkSections = nmsChunk.getSections(); + + for (int layerNo = getMinSectionPosition(); layerNo <= getMaxSectionPosition(); layerNo++) { + + int getSectionIndex = layerNo - getMinSectionPosition(); + int setSectionIndex = layerNo - set.getMinSectionPosition(); + + if (!set.hasSection(layerNo)) { + // No blocks, but might be biomes present. Handle this lazily. + if (biomes == null) { + continue; + } + if (layerNo < set.getMinSectionPosition() || layerNo > set.getMaxSectionPosition()) { + continue; + } + if (biomes[setSectionIndex] != null) { + synchronized (super.sectionLocks[getSectionIndex]) { + LevelChunkSection existingSection = levelChunkSections[getSectionIndex]; + if (createCopy && existingSection != null) { + copy.storeBiomes(getSectionIndex, existingSection.getBiomes()); + } + + if (existingSection == null) { + PalettedContainer> biomeData = PaperweightPlatformAdapter.getBiomePalettedContainer( + biomes[setSectionIndex], + biomeHolderIdMap + ); + LevelChunkSection newSection = PaperweightPlatformAdapter.newChunkSection( + layerNo, + new char[4096], + adapter, + biomeRegistry, + biomeData + ); + if (PaperweightPlatformAdapter.setSectionAtomic( + levelChunkSections, + null, + newSection, + getSectionIndex + )) { + updateGet(nmsChunk, levelChunkSections, newSection, new char[4096], getSectionIndex); + continue; + } else { + existingSection = levelChunkSections[getSectionIndex]; + if (existingSection == null) { + LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, + getSectionIndex + ); + continue; + } + } + } else { + setBiomesToPalettedContainer(biomes, setSectionIndex, existingSection.getBiomes()); + } + } + } + continue; + } + + bitMask |= 1 << getSectionIndex; + + // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to + // this chunk GET when #updateGet is called. Future dords, please listen this time. + char[] tmp = set.load(layerNo); + char[] setArr = new char[tmp.length]; + System.arraycopy(tmp, 0, setArr, 0, tmp.length); + + // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was + // submitted to keep loaded internal chunks to queue target size. + synchronized (super.sectionLocks[getSectionIndex]) { + + LevelChunkSection newSection; + LevelChunkSection existingSection = levelChunkSections[getSectionIndex]; + // Don't attempt to tick section whilst we're editing + if (existingSection != null) { + PaperweightPlatformAdapter.clearCounts(existingSection); + if (PaperLib.isPaper()) { + existingSection.tickingList.clear(); + } + } + + if (createCopy) { + char[] tmpLoad = loadPrivately(layerNo); + char[] copyArr = new char[4096]; + System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); + copy.storeSection(getSectionIndex, copyArr); + if (biomes != null && existingSection != null) { + copy.storeBiomes(getSectionIndex, existingSection.getBiomes()); + } + } + + if (existingSection == null) { + PalettedContainer> biomeData = biomes == null ? new PalettedContainer<>( + biomeHolderIdMap, + biomeHolderIdMap.byIdOrThrow(WorldEditPlugin + .getInstance() + .getBukkitImplAdapter() + .getInternalBiomeId( + BiomeTypes.PLAINS)), + PalettedContainer.Strategy.SECTION_BIOMES + ) : PaperweightPlatformAdapter.getBiomePalettedContainer(biomes[setSectionIndex], biomeHolderIdMap); + newSection = PaperweightPlatformAdapter.newChunkSection( + layerNo, + setArr, + adapter, + biomeRegistry, + biomeData + ); + if (PaperweightPlatformAdapter.setSectionAtomic( + levelChunkSections, + null, + newSection, + getSectionIndex + )) { + updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); + continue; + } else { + existingSection = levelChunkSections[getSectionIndex]; + if (existingSection == null) { + LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, + getSectionIndex + ); + continue; + } + } + } + + //ensure that the server doesn't try to tick the chunksection while we're editing it. (Again) + PaperweightPlatformAdapter.clearCounts(existingSection); + if (PaperLib.isPaper()) { + existingSection.tickingList.clear(); + } + DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection); + + // Synchronize to prevent further acquisitions + synchronized (lock) { + lock.acquire(); // Wait until we have the lock + lock.release(); + try { + sectionLock.writeLock().lock(); + if (this.getChunk() != nmsChunk) { + this.levelChunk = nmsChunk; + this.sections = null; + this.reset(); + } else if (existingSection != getSections(false)[getSectionIndex]) { + this.sections[getSectionIndex] = existingSection; + this.reset(); + } else if (!Arrays.equals( + update(getSectionIndex, new char[4096], true), + loadPrivately(layerNo) + )) { + this.reset(layerNo); + /*} else if (lock.isModified()) { + this.reset(layerNo);*/ + } + } finally { + sectionLock.writeLock().unlock(); + } + + PalettedContainer> biomeData = setBiomesToPalettedContainer( + biomes, + setSectionIndex, + existingSection.getBiomes() + ); + + newSection = + PaperweightPlatformAdapter.newChunkSection( + layerNo, + this::loadPrivately, + setArr, + adapter, + biomeRegistry, + biomeData + ); + if (!PaperweightPlatformAdapter.setSectionAtomic( + levelChunkSections, + existingSection, + newSection, + getSectionIndex + )) { + LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, + getSectionIndex + ); + } else { + updateGet(nmsChunk, levelChunkSections, newSection, setArr, getSectionIndex); + } + } + } + } + + Map heightMaps = set.getHeightMaps(); + for (Map.Entry entry : heightMaps.entrySet()) { + PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue()); + } + PaperweightGetBlocks.this.setLightingToGet( + set.getLight(), + set.getMinSectionPosition(), + set.getMaxSectionPosition() + ); + PaperweightGetBlocks.this.setSkyLightingToGet( + set.getSkyLight(), + set.getMinSectionPosition(), + set.getMaxSectionPosition() + ); + + Runnable[] syncTasks = null; + + int bx = chunkX << 4; + int bz = chunkZ << 4; + + // Call beacon deactivate events here synchronously + // list will be null on spigot, so this is an implicit isPaper check + if (beacons != null && !beacons.isEmpty()) { + final List finalBeacons = beacons; + + syncTasks = new Runnable[4]; + + syncTasks[3] = () -> { + for (BlockEntity beacon : finalBeacons) { + BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); + new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); + } + }; + } + + Set entityRemoves = set.getEntityRemoves(); + if (entityRemoves != null && !entityRemoves.isEmpty()) { + if (syncTasks == null) { + syncTasks = new Runnable[3]; + } + + syncTasks[2] = () -> { + Set entitiesRemoved = new HashSet<>(); + final List entities = PaperweightPlatformAdapter.getEntities(nmsChunk); + + for (Entity entity : entities) { + UUID uuid = entity.getUUID(); + if (entityRemoves.contains(uuid)) { + if (createCopy) { + copy.storeEntity(entity); + } + removeEntity(entity); + entitiesRemoved.add(uuid); + entityRemoves.remove(uuid); + } + } + if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { + for (UUID uuid : entityRemoves) { + Entity entity = nmsWorld.getEntities().get(uuid); + if (entity != null) { + removeEntity(entity); + } + } + } + // Only save entities that were actually removed to history + set.getEntityRemoves().clear(); + set.getEntityRemoves().addAll(entitiesRemoved); + }; + } + + Set entities = set.getEntities(); + if (entities != null && !entities.isEmpty()) { + if (syncTasks == null) { + syncTasks = new Runnable[2]; + } + + syncTasks[1] = () -> { + Iterator iterator = entities.iterator(); + while (iterator.hasNext()) { + final CompoundTag nativeTag = iterator.next(); + final Map entityTagMap = nativeTag.getValue(); + final StringTag idTag = (StringTag) entityTagMap.get("Id"); + final ListTag posTag = (ListTag) entityTagMap.get("Pos"); + final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + if (idTag == null || posTag == null || rotTag == null) { + LOGGER.error("Unknown entity tag: {}", nativeTag); + continue; + } + final double x = posTag.getDouble(0); + final double y = posTag.getDouble(1); + final double z = posTag.getDouble(2); + final float yaw = rotTag.getFloat(0); + final float pitch = rotTag.getFloat(1); + final String id = idTag.getValue(); + + EntityType type = EntityType.byString(id).orElse(null); + if (type != null) { + Entity entity = type.create(nmsWorld); + if (entity != null) { + final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( + nativeTag); + for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + entity.load(tag); + entity.absMoveTo(x, y, z, yaw, pitch); + entity.setUUID(nativeTag.getUUID()); + if (!nmsWorld.addFreshEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + LOGGER.warn( + "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", + id, + nmsWorld.getWorld().getName(), + x, + y, + z + ); + // Unsuccessful create should not be saved to history + iterator.remove(); + } + } + } + } + }; + } + + // set tiles + Map tiles = set.getTiles(); + if (tiles != null && !tiles.isEmpty()) { + if (syncTasks == null) { + syncTasks = new Runnable[1]; + } + + syncTasks[0] = () -> { + for (final Map.Entry entry : tiles.entrySet()) { + final CompoundTag nativeTag = entry.getValue(); + final BlockVector3 blockHash = entry.getKey(); + final int x = blockHash.getX() + bx; + final int y = blockHash.getY(); + final int z = blockHash.getZ() + bz; + final BlockPos pos = new BlockPos(x, y, z); + + synchronized (nmsWorld) { + BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeBlockEntity(pos); + tileEntity = nmsWorld.getBlockEntity(pos); + } + if (tileEntity != null) { + final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( + nativeTag); + tag.put("x", IntTag.valueOf(x)); + tag.put("y", IntTag.valueOf(y)); + tag.put("z", IntTag.valueOf(z)); + tileEntity.load(tag); + } + } + } + }; + } + + Runnable callback; + if (bitMask == 0 && biomes == null && !lightUpdate) { + callback = null; + } else { + int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; + boolean finalLightUpdate = lightUpdate; + callback = () -> { + // Set Modified + nmsChunk.setLightCorrect(true); // Set Modified + nmsChunk.mustNotSave = false; + nmsChunk.setUnsaved(true); + // send to player + if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING) { + this.send(finalMask, finalLightUpdate); + } + if (finalizer != null) { + finalizer.run(); + } + }; + } + if (syncTasks != null) { + QueueHandler queueHandler = Fawe.instance().getQueueHandler(); + Runnable[] finalSyncTasks = syncTasks; + + // Chain the sync tasks and the callback + Callable chain = () -> { + try { + // Run the sync tasks + for (Runnable task : finalSyncTasks) { + if (task != null) { + task.run(); + } + } + if (callback == null) { + if (finalizer != null) { + finalizer.run(); + } + return null; + } else { + return queueHandler.async(callback, null); + } + } catch (Throwable e) { + e.printStackTrace(); + throw e; + } + }; + //noinspection unchecked - required at compile time + return (T) (Future) queueHandler.sync(chain); + } else { + if (callback == null) { + if (finalizer != null) { + finalizer.run(); + } + } else { + callback.run(); + } + } + } + return null; + } catch (Throwable e) { + e.printStackTrace(); + return null; + } finally { + forceLoadSections = true; + } + } + + private void updateGet( + LevelChunk nmsChunk, + LevelChunkSection[] chunkSections, + LevelChunkSection section, + char[] arr, + int layer + ) { + try { + sectionLock.writeLock().lock(); + if (this.getChunk() != nmsChunk) { + this.levelChunk = nmsChunk; + this.sections = new LevelChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); + this.reset(); + } + if (this.sections == null) { + this.sections = new LevelChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); + } + if (this.sections[layer] != section) { + // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords + this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; + } + } finally { + sectionLock.writeLock().unlock(); + } + this.blocks[layer] = arr; + } + + private char[] loadPrivately(int layer) { + layer -= getMinSectionPosition(); + if (super.sections[layer] != null) { + synchronized (super.sectionLocks[layer]) { + if (super.sections[layer].isFull() && super.blocks[layer] != null) { + return super.blocks[layer]; + } + } + } + return PaperweightGetBlocks.this.update(layer, null, true); + } + + @Override + public void send(int mask, boolean lighting) { + synchronized (sendLock) { + PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); + } + } + + /** + * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this + * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation + * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. + * + * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) + * @param data array to be updated/filled with data or null + * @param aggressive if the cached section array should be re-acquired. + * @return the given array to be filled with data, or a new array if null is given. + */ + @Override + @SuppressWarnings("unchecked") + public char[] update(int layer, char[] data, boolean aggressive) { + LevelChunkSection section = getSections(aggressive)[layer]; + // Section is null, return empty array + if (section == null) { + data = new char[4096]; + Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); + return data; + } + if (data != null && data.length != 4096) { + data = new char[4096]; + Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); + } + if (data == null || data == FaweCache.INSTANCE.EMPTY_CHAR_4096) { + data = new char[4096]; + Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); + } + Semaphore lock = PaperweightPlatformAdapter.applyLock(section); + synchronized (lock) { + // Efficiently convert ChunkSection to raw data + try { + lock.acquire(); + + final PalettedContainer blocks = section.getStates(); + final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocks); + final BitStorage bits = (BitStorage) PaperweightPlatformAdapter.fieldStorage.get(dataObject); + + if (bits instanceof ZeroBitStorage) { + Arrays.fill(data, adapter.adaptToChar(blocks.get(0, 0, 0))); // get(int) is only public on paper + return data; + } + + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(dataObject); + + final int bitsPerEntry = bits.getBits(); + final long[] blockStates = bits.getRaw(); + + new BitArrayUnstretched(bitsPerEntry, 4096, blockStates).toRaw(data); + + int num_palette; + if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { + num_palette = palette.getSize(); + } else { + // The section's palette is the global block palette. + for (int i = 0; i < 4096; i++) { + char paletteVal = data[i]; + char ordinal = adapter.ibdIDToOrdinal(paletteVal); + data[i] = ordinal; + } + return data; + } + + char[] paletteToOrdinal = FaweCache.INSTANCE.PALETTE_TO_BLOCK_CHAR.get(); + try { + if (num_palette != 1) { + for (int i = 0; i < num_palette; i++) { + char ordinal = ordinal(palette.valueFor(i), adapter); + paletteToOrdinal[i] = ordinal; + } + for (int i = 0; i < 4096; i++) { + char paletteVal = data[i]; + char val = paletteToOrdinal[paletteVal]; + if (val == Character.MAX_VALUE) { + val = ordinal(palette.valueFor(i), adapter); + paletteToOrdinal[i] = val; + } + data[i] = val; + } + } else { + char ordinal = ordinal(palette.valueFor(0), adapter); + Arrays.fill(data, ordinal); + } + } finally { + for (int i = 0; i < num_palette; i++) { + paletteToOrdinal[i] = Character.MAX_VALUE; + } + } + return data; + } catch (IllegalAccessException | InterruptedException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } finally { + lock.release(); + } + } + } + + private char ordinal(BlockState ibd, PaperweightFaweAdapter adapter) { + if (ibd == null) { + return BlockTypesCache.ReservedIDs.AIR; + } else { + return adapter.adaptToChar(ibd); + } + } + + public LevelChunkSection[] getSections(boolean force) { + force &= forceLoadSections; + LevelChunkSection[] tmp = sections; + if (tmp == null || force) { + try { + sectionLock.writeLock().lock(); + tmp = sections; + if (tmp == null || force) { + LevelChunkSection[] chunkSections = getChunk().getSections(); + tmp = new LevelChunkSection[chunkSections.length]; + System.arraycopy(chunkSections, 0, tmp, 0, chunkSections.length); + sections = tmp; + } + } finally { + sectionLock.writeLock().unlock(); + } + } + return tmp; + } + + public LevelChunk getChunk() { + LevelChunk levelChunk = this.levelChunk; + if (levelChunk == null) { + synchronized (this) { + levelChunk = this.levelChunk; + if (levelChunk == null) { + this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ); + } + } + } + return levelChunk; + } + + private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { + for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { + if (light[Y] == null) { + continue; + } + SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); + DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( + sectionPos); + if (dataLayer == null) { + byte[] LAYER_COUNT = new byte[2048]; + Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); + dataLayer = new DataLayer(LAYER_COUNT); + ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( + lightLayer, + sectionPos, + dataLayer + ); + } + synchronized (dataLayer) { + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + int i = y << 8 | z << 4 | x; + if (light[Y][i] < 16) { + dataLayer.set(x, y, z, light[Y][i]); + } + } + } + } + } + } + } + + private PalettedContainer> setBiomesToPalettedContainer( + final BiomeType[][] biomes, + final int sectionIndex, + final PalettedContainerRO> data + ) { + PalettedContainer> biomeData; + if (data instanceof PalettedContainer> palettedContainer) { + biomeData = palettedContainer; + } else { + LOGGER.warn( + "Cannot correctly set biomes to world, existing biomes may be lost. Expected class " + + "type {} but got {}", + PalettedContainer.class.getSimpleName(), + data.getClass().getSimpleName() + ); + biomeData = data.recreate(); + } + BiomeType[] sectionBiomes; + if (biomes == null || (sectionBiomes = biomes[sectionIndex]) == null) { + return biomeData; + } + for (int y = 0, index = 0; y < 4; y++) { + for (int z = 0; z < 4; z++) { + for (int x = 0; x < 4; x++, index++) { + BiomeType biomeType = sectionBiomes[index]; + if (biomeType == null) { + continue; + } + biomeData.set( + x, + y, + z, + biomeHolderIdMap.byIdOrThrow(WorldEditPlugin + .getInstance() + .getBukkitImplAdapter() + .getInternalBiomeId(biomeType)) + ); + } + } + } + return biomeData; + } + + @Override + public boolean hasSection(int layer) { + layer -= getMinSectionPosition(); + return getSections(false)[layer] != null; + } + + @Override + @SuppressWarnings("unchecked") + public synchronized boolean trim(boolean aggressive) { + skyLight = new DataLayer[getSectionCount()]; + blockLight = new DataLayer[getSectionCount()]; + if (aggressive) { + sectionLock.writeLock().lock(); + sections = null; + levelChunk = null; + sectionLock.writeLock().unlock(); + return super.trim(true); + } else if (sections == null) { + // don't bother trimming if there are no sections stored. + return true; + } else { + for (int i = getMinSectionPosition(); i <= getMaxSectionPosition(); i++) { + int layer = i - getMinSectionPosition(); + if (!hasSection(i) || !super.sections[layer].isFull()) { + continue; + } + LevelChunkSection existing = getSections(true)[layer]; + try { + final PalettedContainer blocksExisting = existing.getStates(); + + final Object dataObject = PaperweightPlatformAdapter.fieldData.get(blocksExisting); + final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( + dataObject); + int paletteSize; + + if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { + paletteSize = palette.getSize(); + } else { + super.trim(false, i); + continue; + } + if (paletteSize == 1) { + //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. + continue; + } + super.trim(false, i); + } catch (IllegalAccessException ignored) { + super.trim(false, i); + } + } + return true; + } + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java new file mode 100644 index 000000000..92d9ec801 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks_Copy.java @@ -0,0 +1,254 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; +import com.fastasyncworldedit.core.queue.IBlocks; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.IChunkSet; +import com.google.common.base.Suppliers; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.math.BlockVector3; +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.BlockTypesCache; +import net.minecraft.core.Holder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.chunk.PalettedContainerRO; +import org.apache.logging.log4j.Logger; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Future; + +public class PaperweightGetBlocks_Copy implements IChunkGet { + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + private final Map tiles = new HashMap<>(); + private final Set entities = new HashSet<>(); + private final char[][] blocks; + private final int minHeight; + private final int maxHeight; + final ServerLevel serverLevel; + final LevelChunk levelChunk; + private PalettedContainer>[] biomes = null; + + protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { + this.levelChunk = levelChunk; + this.serverLevel = levelChunk.level; + this.minHeight = serverLevel.getMinBuildHeight(); + this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. + this.blocks = new char[getSectionCount()][]; + } + + protected void storeTile(BlockEntity blockEntity) { + tiles.put( + BlockVector3.at( + blockEntity.getBlockPos().getX(), + blockEntity.getBlockPos().getY(), + blockEntity.getBlockPos().getZ() + ), + new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId)) + ); + } + + @Override + public Map getTiles() { + return tiles; + } + + @Override + @Nullable + public CompoundTag getTile(int x, int y, int z) { + return tiles.get(BlockVector3.at(x, y, z)); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected void storeEntity(Entity entity) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); + entity.save(compoundTag); + entities.add((CompoundTag) adapter.toNative(compoundTag)); + } + + @Override + public Set getEntities() { + return this.entities; + } + + @Override + public CompoundTag getEntity(UUID uuid) { + for (CompoundTag tag : entities) { + if (uuid.equals(tag.getUUID())) { + return tag; + } + } + return null; + } + + @Override + public boolean isCreateCopy() { + return false; + } + + @Override + public int setCreateCopy(boolean createCopy) { + return -1; + } + + @Override + public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) { + } + + @Override + public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) { + } + + @Override + public void setHeightmapToGet(HeightMapType type, int[] data) { + } + + @Override + public int getMaxY() { + return maxHeight; + } + + @Override + public int getMinY() { + return minHeight; + } + + @Override + public int getMaxSectionPosition() { + return maxHeight >> 4; + } + + @Override + public int getMinSectionPosition() { + return minHeight >> 4; + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + Holder biome = biomes[(y >> 4) - getMinSectionPosition()].get(x >> 2, (y & 15) >> 2, z >> 2); + return PaperweightPlatformAdapter.adapt(biome, serverLevel); + } + + @Override + public void removeSectionLighting(int layer, boolean sky) { + } + + @Override + public boolean trim(boolean aggressive, int layer) { + return false; + } + + @Override + public IBlocks reset() { + return null; + } + + @Override + public int getSectionCount() { + return serverLevel.getSectionsCount(); + } + + protected void storeSection(int layer, char[] data) { + blocks[layer] = data; + } + + protected void storeBiomes(int layer, PalettedContainerRO> biomeData) { + if (biomes == null) { + biomes = new PalettedContainer[getSectionCount()]; + } + if (biomeData instanceof PalettedContainer> palettedContainer) { + biomes[layer] = palettedContainer.copy(); + } else { + LOGGER.error( + "Cannot correctly save biomes to history. Expected class type {} but got {}", + PalettedContainer.class.getSimpleName(), + biomeData.getClass().getSimpleName() + ); + } + } + + @Override + public BaseBlock getFullBlock(int x, int y, int z) { + BlockState state = BlockTypesCache.states[get(x, y, z)]; + return state.toBaseBlock(this, x, y, z); + } + + @Override + public boolean hasSection(int layer) { + layer -= getMinSectionPosition(); + return blocks[layer] != null; + } + + @Override + public char[] load(int layer) { + layer -= getMinSectionPosition(); + if (blocks[layer] == null) { + blocks[layer] = new char[4096]; + Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); + } + return blocks[layer]; + } + + @Override + public char[] loadIfPresent(int layer) { + layer -= getMinSectionPosition(); + return blocks[layer]; + } + + @Override + public BlockState getBlock(int x, int y, int z) { + return BlockTypesCache.states[get(x, y, z)]; + } + + @Override + public int getSkyLight(int x, int y, int z) { + return 0; + } + + @Override + public int getEmittedLight(int x, int y, int z) { + return 0; + } + + @Override + public int[] getHeightMap(HeightMapType type) { + return new int[0]; + } + + @Override + public > T call(IChunkSet set, Runnable finalize) { + return null; + } + + public char get(int x, int y, int z) { + final int layer = (y >> 4) - getMinSectionPosition(); + final int index = (y & 15) << 8 | z << 4 | x; + return blocks[layer][index]; + } + + + @Override + public boolean trim(boolean aggressive) { + return false; + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightMapChunkUtil.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightMapChunkUtil.java new file mode 100644 index 000000000..97eecfc56 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightMapChunkUtil.java @@ -0,0 +1,34 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil; +import com.sk89q.worldedit.bukkit.adapter.Refraction; +import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; + +//TODO un-very-break-this +public class PaperweightMapChunkUtil extends MapChunkUtil { + + public PaperweightMapChunkUtil() throws NoSuchFieldException { + fieldX = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a")); + fieldZ = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("x", "a")); + fieldBitMask = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("z", "b")); + fieldHeightMap = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("heightmaps", "b")); + fieldChunkData = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("chunkData", "c")); + fieldBlockEntities = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("buffer", "c")); + fieldFull = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("blockEntitiesData", "d")); + fieldX.setAccessible(true); + fieldZ.setAccessible(true); + fieldBitMask.setAccessible(true); + fieldHeightMap.setAccessible(true); + fieldChunkData.setAccessible(true); + fieldBlockEntities.setAccessible(true); + fieldFull.setAccessible(true); + } + + @Override + public ClientboundLevelChunkWithLightPacket createPacket() { + // TODO ??? return new ClientboundLevelChunkPacket(); + throw new UnsupportedOperationException(); + } + +} 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 new file mode 100644 index 000000000..147ec5be7 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPlatformAdapter.java @@ -0,0 +1,719 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.destroystokyo.paper.util.maplist.EntityList; +import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; +import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; +import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; +import com.fastasyncworldedit.core.Fawe; +import com.fastasyncworldedit.core.FaweCache; +import com.fastasyncworldedit.core.math.BitArrayUnstretched; +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.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.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.world.ChunkEntitySlices; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.IdMap; +import net.minecraft.core.Registry; +import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.TicketType; +import net.minecraft.util.BitStorage; +import net.minecraft.util.ExceptionCollector; +import net.minecraft.util.SimpleBitStorage; +import net.minecraft.util.ThreadingDetector; +import net.minecraft.util.Unit; +import net.minecraft.util.ZeroBitStorage; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.GlobalPalette; +import net.minecraft.world.level.chunk.HashMapPalette; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.LinearPalette; +import net.minecraft.world.level.chunk.Palette; +import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.chunk.SingleValuePalette; +import net.minecraft.world.level.entity.PersistentEntitySectionManager; +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; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; + +import static java.lang.invoke.MethodType.methodType; +import static net.minecraft.core.registries.Registries.BIOME; + +public final class PaperweightPlatformAdapter extends NMSAdapter { + + public static final Field fieldData; + + public static final Constructor dataConstructor; + + public static final Field fieldStorage; + public static final Field fieldPalette; + + private static final Field fieldTickingFluidCount; + private static final Field fieldTickingBlockCount; + private static final Field fieldNonEmptyBlockCount; + + private static final MethodHandle methodGetVisibleChunk; + + private static final Field fieldThreadingDetector; + private static final Field fieldLock; + + private static final MethodHandle methodRemoveGameEventListener; + private static final MethodHandle methodremoveTickingBlockEntity; + + /* + * This is a workaround for the changes from https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1fddefce1cdce44010927b888432bf70c0e88cde#src/main/java/org/bukkit/craftbukkit/CraftChunk.java + * and is only needed to support 1.19.4 versions before *and* after this change. + */ + private static final MethodHandle CRAFT_CHUNK_GET_HANDLE; + + private static final Field fieldRemove; + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + static final boolean POST_CHUNK_REWRITE; + private static Method PAPER_CHUNK_GEN_ALL_ENTITIES; + private static Field LEVEL_CHUNK_ENTITIES; + private static Field SERVER_LEVEL_ENTITY_MANAGER; + + static { + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d")); + fieldData.setAccessible(true); + + Class dataClazz = fieldData.getType(); + dataConstructor = dataClazz.getDeclaredConstructors()[0]; + dataConstructor.setAccessible(true); + + fieldStorage = dataClazz.getDeclaredField(Refraction.pickName("storage", "b")); + fieldStorage.setAccessible(true); + fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c")); + fieldPalette.setAccessible(true); + + fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "g")); + fieldTickingFluidCount.setAccessible(true); + fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f")); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "e")); + fieldNonEmptyBlockCount.setAccessible(true); + + Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName( + "getVisibleChunkIfPresent", + "b" + ), long.class); + getVisibleChunkIfPresent.setAccessible(true); + methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); + + if (!PaperLib.isPaper()) { + fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); + fieldThreadingDetector.setAccessible(true); + fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); + fieldLock.setAccessible(true); + } else { + // in paper, the used methods are synchronized properly + fieldThreadingDetector = null; + fieldLock = null; + } + + Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( + Refraction.pickName("removeGameEventListener", "a"), + BlockEntity.class, + ServerLevel.class + ); + removeGameEventListener.setAccessible(true); + methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); + + Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( + Refraction.pickName( + "removeBlockEntityTicker", + "l" + ), BlockPos.class + ); + removeBlockEntityTicker.setAccessible(true); + methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); + + fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q")); + fieldRemove.setAccessible(true); + + boolean chunkRewrite; + try { + ServerLevel.class.getDeclaredMethod("getEntityLookup"); + chunkRewrite = true; + PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities"); + PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true); + } catch (NoSuchMethodException ignored) { + chunkRewrite = false; + } + try { + // Paper - Pre-Chunk-Update + LEVEL_CHUNK_ENTITIES = LevelChunk.class.getDeclaredField("entities"); + LEVEL_CHUNK_ENTITIES.setAccessible(true); + } catch (NoSuchFieldException ignored) { + } + try { + // Non-Paper + SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "M")); + SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true); + } catch (NoSuchFieldException ignored) { + } + POST_CHUNK_REWRITE = chunkRewrite; + } catch (RuntimeException | Error e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + MethodHandle craftChunkGetHandle; + final MethodType type = methodType(LevelChunk.class); + try { + craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", type); + } catch (NoSuchMethodException | IllegalAccessException e) { + try { + final MethodType newType = methodType(ChunkAccess.class, ChunkStatus.class); + craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", newType); + craftChunkGetHandle = MethodHandles.insertArguments(craftChunkGetHandle, 1, ChunkStatus.FULL); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + CRAFT_CHUNK_GET_HANDLE = craftChunkGetHandle; + } + + static boolean setSectionAtomic( + LevelChunkSection[] sections, + LevelChunkSection expected, + LevelChunkSection value, + int layer + ) { + if (layer >= 0 && layer < sections.length) { + return ReflectionUtils.compareAndSet(sections, expected, value, layer); + } + return false; + } + + // There is no point in having a functional semaphore for paper servers. + private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = + ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); + + static DelegateSemaphore applyLock(LevelChunkSection section) { + if (PaperLib.isPaper()) { + return SEMAPHORE_THREAD_LOCAL.get(); + } + try { + synchronized (section) { + PalettedContainer blocks = section.getStates(); + ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks); + synchronized (currentThreadingDetector) { + Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector); + if (currentLock instanceof DelegateSemaphore delegateSemaphore) { + return delegateSemaphore; + } + DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); + fieldLock.set(currentThreadingDetector, newLock); + return newLock; + } + } + } catch (Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) { + if (!PaperLib.isPaper()) { + LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false); + if (nmsChunk != null) { + return nmsChunk; + } + if (Fawe.isMainThread()) { + return serverLevel.getChunk(chunkX, chunkZ); + } + } else { + LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); + if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); + return nmsChunk; + } + nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); + if (nmsChunk != null) { + addTicket(serverLevel, chunkX, chunkZ); + return nmsChunk; + } + // Avoid "async" methods from the main thread. + if (Fawe.isMainThread()) { + return serverLevel.getChunk(chunkX, chunkZ); + } + CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); + try { + CraftChunk chunk; + try { + chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); + } catch (TimeoutException e) { + String world = serverLevel.getWorld().getName(); + // We've already taken 10 seconds we can afford to wait a little here. + boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); + if (loaded) { + LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); + // Retry chunk load + chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); + } else { + throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); + } + } + addTicket(serverLevel, chunkX, chunkZ); + return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); + } catch (Throwable e) { + e.printStackTrace(); + } + } + return TaskManager.taskManager().sync(() -> 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 + .getChunkSource() + .addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); + } + + public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { + ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; + try { + return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ)); + } catch (Throwable thr) { + throw new RuntimeException(thr); + } + } + + @SuppressWarnings("deprecation") + public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) { + ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ); + if (chunkHolder == null) { + return; + } + ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ); + LevelChunk levelChunk; + if (PaperLib.isPaper()) { + // getChunkAtIfLoadedImmediately is paper only + levelChunk = nmsWorld + .getChunkSource() + .getChunkAtIfLoadedImmediately(chunkX, chunkZ); + } else { + levelChunk = ((Optional) ((Either) chunkHolder + .getTickingChunkFuture() // method is not present with new paper chunk system + .getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left()) + .orElse(null); + } + if (levelChunk == null) { + return; + } + TaskManager.taskManager().task(() -> { + ClientboundLevelChunkWithLightPacket packet; + if (PaperLib.isPaper()) { + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null + // last false is to not bother with x-ray + ); + } else { + // deprecated on paper - deprecation suppressed + packet = new ClientboundLevelChunkWithLightPacket( + levelChunk, + nmsWorld.getChunkSource().getLightEngine(), + null, + null + ); + } + nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); + }); + } + + private static List nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { + return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false); + } + + /* + NMS conversion + */ + public static LevelChunkSection newChunkSection( + final int layer, + final char[] blocks, + CachedBukkitAdapter adapter, + Registry biomeRegistry, + @Nullable PalettedContainer> biomes + ) { + return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes); + } + + public static LevelChunkSection newChunkSection( + final int layer, + final Function get, + char[] set, + CachedBukkitAdapter adapter, + Registry biomeRegistry, + @Nullable PalettedContainer> biomes + ) { + if (set == null) { + return newChunkSection(layer, biomeRegistry, biomes); + } + final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get(); + final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get(); + final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get(); + final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get(); + try { + int num_palette; + if (get == null) { + num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, null); + } else { + num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter, null); + } + + int bitsPerEntry = MathMan.log2nlz(num_palette - 1); + if (bitsPerEntry > 0 && bitsPerEntry < 5) { + bitsPerEntry = 4; + } else if (bitsPerEntry > 8) { + bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1); + } + + int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes + final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero); + final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong); + + if (num_palette == 1) { + for (int i = 0; i < blockBitArrayEnd; i++) { + blockStates[i] = 0; + } + } else { + final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntryNonZero, 4096, blockStates); + bitArray.fromRaw(blocksCopy); + } + + final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd); + final BitStorage nmsBits; + if (bitsPerEntry == 0) { + nmsBits = new ZeroBitStorage(4096); + } else { + nmsBits = new SimpleBitStorage(bitsPerEntry, 4096, bits); + } + List palette; + if (bitsPerEntry < 9) { + palette = new ArrayList<>(); + for (int i = 0; i < num_palette; i++) { + int ordinal = paletteToBlock[i]; + blockToPalette[ordinal] = Integer.MAX_VALUE; + final BlockState state = BlockTypesCache.states[ordinal]; + palette.add(((PaperweightBlockMaterial) state.getMaterial()).getState()); + } + } else { + palette = List.of(); + } + + // Create palette with data + @SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot + final PalettedContainer blockStatePalettedContainer = + new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + PalettedContainer.Strategy.SECTION_STATES, + PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry), + nmsBits, + palette + ); + if (biomes == null) { + IdMap> biomeHolderIdMap = biomeRegistry.asHolderIdMap(); + biomes = new PalettedContainer<>( + biomeHolderIdMap, + biomeHolderIdMap.byIdOrThrow(WorldEditPlugin + .getInstance() + .getBukkitImplAdapter() + .getInternalBiomeId( + BiomeTypes.PLAINS)), + PalettedContainer.Strategy.SECTION_BIOMES + ); + } + + return new LevelChunkSection(blockStatePalettedContainer, biomes); + } catch (final Throwable e) { + throw e; + } finally { + Arrays.fill(blockToPalette, Integer.MAX_VALUE); + Arrays.fill(paletteToBlock, Integer.MAX_VALUE); + Arrays.fill(blockStates, 0); + Arrays.fill(blocksCopy, 0); + } + } + + @SuppressWarnings("deprecation") // Only deprecated in paper + private static LevelChunkSection newChunkSection( + int layer, + Registry biomeRegistry, + @Nullable PalettedContainer> biomes + ) { + if (biomes == null) { + return new LevelChunkSection(biomeRegistry); + } + PalettedContainer dataPaletteBlocks = new PalettedContainer<>( + Block.BLOCK_STATE_REGISTRY, + Blocks.AIR.defaultBlockState(), + PalettedContainer.Strategy.SECTION_STATES + ); + return new LevelChunkSection(dataPaletteBlocks, biomes); + } + + /** + * Create a new {@link PalettedContainer}. Should only be used if no biome container existed beforehand. + */ + public static PalettedContainer> getBiomePalettedContainer( + BiomeType[] biomes, + IdMap> biomeRegistry + ) { + if (biomes == null) { + return null; + } + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + // Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length + Map> palette = new HashMap<>(); + for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) { + Holder biome; + if (biomeType == null) { + biome = biomeRegistry.byId(adapter.getInternalBiomeId(BiomeTypes.PLAINS)); + } else { + biome = biomeRegistry.byId(adapter.getInternalBiomeId(biomeType)); + } + palette.put(biomeType, biome); + } + int biomeCount = palette.size(); + int bitsPerEntry = MathMan.log2nlz(biomeCount - 1); + Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration( + new FakeIdMapBiome(biomeCount), + bitsPerEntry + ); + if (bitsPerEntry > 3) { + bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1); + } + PalettedContainer> biomePalettedContainer = new PalettedContainer<>( + biomeRegistry, + biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)), + PalettedContainer.Strategy.SECTION_BIOMES + ); + + final Palette> biomePalette; + if (bitsPerEntry == 0) { + biomePalette = new SingleValuePalette<>( + biomePalettedContainer.registry, + biomePalettedContainer, + new ArrayList<>(palette.values()) // Must be modifiable + ); + } else if (bitsPerEntry == 4) { + biomePalette = LinearPalette.create( + 4, + biomePalettedContainer.registry, + biomePalettedContainer, + new ArrayList<>(palette.values()) // Must be modifiable + ); + } else if (bitsPerEntry < 9) { + biomePalette = HashMapPalette.create( + bitsPerEntry, + biomePalettedContainer.registry, + biomePalettedContainer, + new ArrayList<>(palette.values()) // Must be modifiable + ); + } else { + biomePalette = GlobalPalette.create( + bitsPerEntry, + biomePalettedContainer.registry, + biomePalettedContainer, + null // unused + ); + } + + int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes + final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero); + final int arrayLength = MathMan.ceilZero(64f / blocksPerLong); + + + BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage( + bitsPerEntry, + 64, + new long[arrayLength] + ); + + try { + Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette); + fieldData.set(biomePalettedContainer, data); + int index = 0; + for (int y = 0; y < 4; y++) { + for (int z = 0; z < 4; z++) { + for (int x = 0; x < 4; x++, index++) { + BiomeType biomeType = biomes[index]; + if (biomeType == null) { + continue; + } + Holder biome = biomeRegistry.byId(WorldEditPlugin + .getInstance() + .getBukkitImplAdapter() + .getInternalBiomeId(biomeType)); + if (biome == null) { + continue; + } + biomePalettedContainer.set(x, y, z, biome); + } + } + } + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + return biomePalettedContainer; + } + + public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException { + fieldTickingFluidCount.setShort(section, (short) 0); + fieldTickingBlockCount.setShort(section, (short) 0); + } + + public static BiomeType adapt(Holder biome, LevelAccessor levelAccessor) { + final Registry biomeRegistry = levelAccessor.registryAccess().registryOrThrow(BIOME); + if (biomeRegistry.getKey(biome.value()) == null) { + return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN + : null; + } + return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString()); + } + + static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) { + try { + if (levelChunk.loaded || levelChunk.level.isClientSide()) { + BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos()); + if (blockEntity != null) { + if (!levelChunk.level.isClientSide) { + methodRemoveGameEventListener.invoke(levelChunk, beacon, levelChunk.level); + } + fieldRemove.set(beacon, true); + } + } + methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos()); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } + + static List getEntities(LevelChunk chunk) { + ExceptionCollector collector = new ExceptionCollector<>(); + if (PaperLib.isPaper()) { + if (POST_CHUNK_REWRITE) { + try { + //noinspection unchecked + return (List) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e); + } + } + try { + EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk); + return List.of(entityList.getRawData()); + } catch (IllegalAccessException e) { + collector.add(new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=false]", e)); + // fall through + } + } + try { + //noinspection unchecked + return ((PersistentEntitySectionManager) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos()); + } catch (IllegalAccessException e) { + collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e)); + } + collector.throwIfPresent(); + return List.of(); + } + + record FakeIdMapBlock(int size) implements IdMap { + + @Override + public int getId(final net.minecraft.world.level.block.state.BlockState entry) { + return 0; + } + + @Nullable + @Override + public net.minecraft.world.level.block.state.BlockState byId(final int index) { + return null; + } + + @Nonnull + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + } + + record FakeIdMapBiome(int size) implements IdMap { + + @Override + public int getId(final Biome entry) { + return 0; + } + + @Nullable + @Override + public Biome byId(final int index) { + return null; + } + + @Nonnull + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPostProcessor.java new file mode 100644 index 000000000..cfd9e2753 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightPostProcessor.java @@ -0,0 +1,175 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.extent.processor.ProcessorScope; +import com.fastasyncworldedit.core.queue.IBatchProcessor; +import com.fastasyncworldedit.core.queue.IChunk; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.queue.IChunkSet; +import com.fastasyncworldedit.core.registry.state.PropertyKey; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; + +import javax.annotation.Nullable; + +public class PaperweightPostProcessor implements IBatchProcessor { + + @Override + public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { + return set; + } + + @SuppressWarnings("deprecation") + @Override + public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) { + boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS; + // The PostProcessor shouldn't be added, but just in case + if (!tickFluid) { + return; + } + PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet; + layer: + for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) { + char[] set = iChunkSet.loadIfPresent(layer); + if (set == null) { + // No edit means no need to process + continue; + } + char[] get = null; + for (int i = 0; i < 4096; i++) { + char ordinal = set[i]; + char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__; + boolean fromGet = false; // Used for liquids + if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { + if (get == null) { + get = getBlocks.load(layer); + } + // If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't + // actually being set + if (get == null) { + continue layer; + } + fromGet = true; + ordinal = replacedOrdinal = get[i]; + } + if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { + continue; + } else if (!fromGet) { // if fromGet, don't do the same again + if (get == null) { + get = getBlocks.load(layer); + } + replacedOrdinal = get[i]; + } + boolean ticking = BlockTypesCache.ticking[ordinal]; + boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal]; + boolean replacedWasLiquid = false; + BlockState replacedState = null; + if (!ticking) { + // If the block being replaced was not ticking, it cannot be a liquid + if (!replacedWasTicking) { + continue; + } + // If the block being replaced is not fluid, we do not need to worry + if (!(replacedWasLiquid = + (replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) { + continue; + } + } + BlockState state = BlockState.getFromOrdinal(ordinal); + boolean liquid = state.getMaterial().isLiquid(); + int x = i & 15; + int y = (i >> 8) & 15; + int z = (i >> 4) & 15; + BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z); + if (liquid || replacedWasLiquid) { + if (liquid) { + addFluid(getBlocks.serverLevel, state, position); + continue; + } + // If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this + // may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up + // being ticked anyway. We only need it to be "hit" once. + if (!wasAdjacentToWater(get, set, i, x, y, z)) { + continue; + } + addFluid(getBlocks.serverLevel, replacedState, position); + } + } + } + } + + @Nullable + @Override + public Extent construct(final Extent child) { + throw new UnsupportedOperationException("Processing only"); + } + + @Override + public ProcessorScope getScope() { + return ProcessorScope.READING_SET_BLOCKS; + } + + private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { + if (set == null || get == null) { + return false; + } + char ordinal; + char reserved = BlockTypesCache.ReservedIDs.__RESERVED__; + if (x > 0 && set[i - 1] != reserved) { + if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) { + return true; + } + } + if (x < 15 && set[i + 1] != reserved) { + if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) { + return true; + } + } + if (z > 0 && set[i - 16] != reserved) { + if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) { + return true; + } + } + if (z < 15 && set[i + 16] != reserved) { + if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) { + return true; + } + } + if (y > 0 && set[i - 256] != reserved) { + if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) { + return true; + } + } + if (y < 15 && set[i + 256] != reserved) { + return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal); + } + return false; + } + + @SuppressWarnings("deprecation") + private boolean isFluid(char ordinal) { + return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid(); + } + + @SuppressWarnings("deprecation") + private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) { + Fluid type; + if (replacedState.getBlockType() == BlockTypes.LAVA) { + type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA; + } else { + type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER; + } + serverLevel.scheduleTick( + position, + type, + type.getTickDelay(serverLevel) + ); + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighter.java new file mode 100644 index 000000000..49f02bf8d --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighter.java @@ -0,0 +1,76 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; +import com.fastasyncworldedit.core.configuration.Settings; +import com.fastasyncworldedit.core.queue.IQueueExtent; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.TicketType; +import net.minecraft.util.Unit; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.ChunkStatus; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.IntConsumer; + +public class PaperweightStarlightRelighter extends StarlightRelighter { + + private static final TicketType FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0); + private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT); + + public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent queue) { + super(serverLevel, queue); + } + + @Override + protected ChunkPos createChunkPos(final long chunkKey) { + return new ChunkPos(chunkKey); + } + + @Override + protected long asLong(final int chunkX, final int chunkZ) { + return ChunkPos.asLong(chunkX, chunkZ); + } + + @Override + protected CompletableFuture chunkLoadFuture(final ChunkPos chunkPos) { + return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z) + .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel( + FAWE_TICKET, + chunkPos, + LIGHT_LEVEL, + Unit.INSTANCE + )); + } + + protected void invokeRelight( + Set coords, + Consumer chunkCallback, + IntConsumer processCallback + ) { + try { + serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback); + } catch (Exception e) { + LOGGER.error("Error occurred on relighting", e); + } + } + + /* + * Allow the server to unload the chunks again. + * Also, if chunk packets are sent delayed, we need to do that here + */ + 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); + } + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighterFactory.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighterFactory.java new file mode 100644 index 000000000..bf9a4e8df --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightStarlightRelighterFactory.java @@ -0,0 +1,25 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; + +import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter; +import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; +import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; +import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; +import com.fastasyncworldedit.core.queue.IQueueExtent; +import com.sk89q.worldedit.world.World; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; + +import javax.annotation.Nonnull; + +public class PaperweightStarlightRelighterFactory implements RelighterFactory { + + @Override + public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent queue) { + org.bukkit.World w = Bukkit.getWorld(world.getName()); + if (w == null) { + return NullRelighter.INSTANCE; + } + return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue); + } + +} diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/nbt/PaperweightLazyCompoundTag.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/nbt/PaperweightLazyCompoundTag.java new file mode 100644 index 000000000..9a8a51896 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/nbt/PaperweightLazyCompoundTag.java @@ -0,0 +1,161 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.LazyCompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import net.minecraft.nbt.NumericTag; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class PaperweightLazyCompoundTag extends LazyCompoundTag { + + private final Supplier compoundTagSupplier; + private CompoundTag compoundTag; + + public PaperweightLazyCompoundTag(Supplier compoundTagSupplier) { + super(new HashMap<>()); + this.compoundTagSupplier = compoundTagSupplier; + } + + public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) { + this(() -> compoundTag); + } + + public net.minecraft.nbt.CompoundTag get() { + return compoundTagSupplier.get(); + } + + @Override + @SuppressWarnings("unchecked") + public Map getValue() { + if (compoundTag == null) { + compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get()); + } + return compoundTag.getValue(); + } + + @Override + public CompoundBinaryTag asBinaryTag() { + getValue(); + return compoundTag.asBinaryTag(); + } + + public boolean containsKey(String key) { + return compoundTagSupplier.get().contains(key); + } + + public byte[] getByteArray(String key) { + return compoundTagSupplier.get().getByteArray(key); + } + + public byte getByte(String key) { + return compoundTagSupplier.get().getByte(key); + } + + public double getDouble(String key) { + return compoundTagSupplier.get().getDouble(key); + } + + public double asDouble(String key) { + net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); + if (tag instanceof NumericTag numTag) { + return numTag.getAsDouble(); + } + return 0; + } + + public float getFloat(String key) { + return compoundTagSupplier.get().getFloat(key); + } + + public int[] getIntArray(String key) { + return compoundTagSupplier.get().getIntArray(key); + } + + public int getInt(String key) { + return compoundTagSupplier.get().getInt(key); + } + + public int asInt(String key) { + net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); + if (tag instanceof NumericTag numTag) { + return numTag.getAsInt(); + } + return 0; + } + + @SuppressWarnings("unchecked") + public List getList(String key) { + net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); + if (tag instanceof net.minecraft.nbt.ListTag nbtList) { + ArrayList list = new ArrayList<>(); + for (net.minecraft.nbt.Tag elem : nbtList) { + if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) { + list.add(new PaperweightLazyCompoundTag(compoundTag)); + } else { + list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem)); + } + } + return list; + } + return Collections.emptyList(); + } + + @SuppressWarnings("unchecked") + public ListTag getListTag(String key) { + net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); + if (tag instanceof net.minecraft.nbt.ListTag) { + return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag); + } + return new ListTag(StringTag.class, Collections.emptyList()); + } + + @SuppressWarnings("unchecked") + public List getList(String key, Class listType) { + ListTag listTag = getListTag(key); + if (listTag.getType().equals(listType)) { + return (List) listTag.getValue(); + } else { + return Collections.emptyList(); + } + } + + public long[] getLongArray(String key) { + return compoundTagSupplier.get().getLongArray(key); + } + + public long getLong(String key) { + return compoundTagSupplier.get().getLong(key); + } + + public long asLong(String key) { + net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); + if (tag instanceof NumericTag numTag) { + return numTag.getAsLong(); + } + return 0; + } + + public short getShort(String key) { + return compoundTagSupplier.get().getShort(key); + } + + public String getString(String key) { + return compoundTagSupplier.get().getString(key); + } + + @Override + public String toString() { + return compoundTagSupplier.get().toString(); + } + +} 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 new file mode 100644 index 000000000..edf9e9f90 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/regen/PaperweightRegen.java @@ -0,0 +1,590 @@ +package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.regen; + +import com.fastasyncworldedit.bukkit.adapter.Regenerator; +import com.fastasyncworldedit.core.Fawe; +import com.fastasyncworldedit.core.queue.IChunkCache; +import com.fastasyncworldedit.core.queue.IChunkGet; +import com.fastasyncworldedit.core.util.TaskManager; +import com.google.common.collect.ImmutableList; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.PaperweightGetBlocks; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.util.LogManagerCompat; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.io.file.SafeFiles; +import com.sk89q.worldedit.world.RegenOptions; +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.util.thread.ProcessorHandle; +import net.minecraft.util.thread.ProcessorMailbox; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.FixedBiomeSource; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.FlatLevelSource; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; +import net.minecraft.world.level.levelgen.WorldOptions; +import net.minecraft.world.level.levelgen.blending.BlendingData; +import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; +import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.craftbukkit.v1_20_R3.CraftServer; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_20_R3.generator.CustomChunkGenerator; +import org.bukkit.generator.BiomeProvider; +import org.bukkit.generator.BlockPopulator; + +import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.OptionalLong; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +import static net.minecraft.core.registries.Registries.BIOME; + +public class PaperweightRegen extends Regenerator { + + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + private static final Field serverWorldsField; + private static final Field paperConfigField; + private static final Field flatBedrockField; + private static final Field generatorSettingFlatField; + private static final Field generatorSettingBaseSupplierField; + private static final Field delegateField; + private static final Field chunkSourceField; + private static final Field generatorStructureStateField; + private static final Field ringPositionsField; + private static final Field hasGeneratedPositionsField; + + //list of chunk stati in correct order without FULL + private static final Map chunkStati = new LinkedHashMap<>(); + + static { + chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing + chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps + chunkStati.put( + ChunkStatus.STRUCTURE_REFERENCES, + Concurrency.FULL + ); // structure refs: radius 8, but only writes to current chunk + chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0 + chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8 + chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE + chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results + /*chunkStati.put( + ChunkStatus.LIQUID_CARVERS, + Concurrency.NONE + ); // liquid carvers: radius 0, but RADIUS and FULL change results*/ + chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps + chunkStati.put( + ChunkStatus.LIGHT, + Concurrency.FULL + ); // light: radius 1, but no writes to other chunks, only current chunk + chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0 + // chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0 + + try { + serverWorldsField = CraftServer.class.getDeclaredField("worlds"); + serverWorldsField.setAccessible(true); + + Field tmpPaperConfigField; + Field tmpFlatBedrockField; + try { //only present on paper + tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); + tmpPaperConfigField.setAccessible(true); + + tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); + tmpFlatBedrockField.setAccessible(true); + } catch (Exception e) { + tmpPaperConfigField = null; + tmpFlatBedrockField = null; + } + paperConfigField = tmpPaperConfigField; + flatBedrockField = tmpFlatBedrockField; + + generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( + "settings", "e")); + generatorSettingBaseSupplierField.setAccessible(true); + + generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d")); + generatorSettingFlatField.setAccessible(true); + + delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); + delegateField.setAccessible(true); + + chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I")); + chunkSourceField.setAccessible(true); + + generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v")); + generatorStructureStateField.setAccessible(true); + + ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g")); + ringPositionsField.setAccessible(true); + + hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField( + Refraction.pickName("hasGeneratedPositions", "h") + ); + hasGeneratedPositionsField.setAccessible(true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + //runtime + private ServerLevel originalServerWorld; + private ServerChunkCache originalChunkProvider; + private ServerLevel freshWorld; + private ServerChunkCache freshChunkProvider; + private LevelStorageSource.LevelStorageAccess session; + private StructureTemplateManager structureTemplateManager; + private ThreadedLevelLightEngine threadedLevelLightEngine; + private ChunkGenerator chunkGenerator; + + private Path tempDir; + + private boolean generateFlatBedrock = false; + + public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { + super(originalBukkitWorld, region, target, options); + } + + @Override + protected boolean prepare() { + this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); + originalChunkProvider = originalServerWorld.getChunkSource(); + + //flat bedrock? (only on paper) + if (paperConfigField != null) { + try { + generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld)); + } catch (Exception ignored) { + } + } + + seed = options.getSeed().orElse(originalServerWorld.getSeed()); + chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c)); + + return true; + } + + @Override + @SuppressWarnings("unchecked") + protected boolean initNewWorld() throws Exception { + //world folder + tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); + + //prepare for world init (see upstream implementation for reference) + org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); + org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); + LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); + ResourceKey levelStemResourceKey = getWorldDimKey(environment); + session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey); + PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData; + + MinecraftServer server = originalServerWorld.getCraftServer().getServer(); + WorldOptions originalOpts = originalWorldData.worldGenOptions(); + WorldOptions newOpts = options.getSeed().isPresent() + ? originalOpts.withSeed(OptionalLong.of(seed)) + : originalOpts; + LevelSettings newWorldSettings = new LevelSettings( + "faweregentempworld", + originalWorldData.settings.gameType(), + originalWorldData.settings.hardcore(), + originalWorldData.settings.difficulty(), + originalWorldData.settings.allowCommands(), + originalWorldData.settings.gameRules(), + originalWorldData.settings.getDataConfiguration() + ); + + PrimaryLevelData.SpecialWorldProperty specialWorldProperty = + originalWorldData.isFlatWorld() + ? PrimaryLevelData.SpecialWorldProperty.FLAT + : originalWorldData.isDebugWorld() + ? PrimaryLevelData.SpecialWorldProperty.DEBUG + : PrimaryLevelData.SpecialWorldProperty.NONE; + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable()); + + BiomeProvider biomeProvider = getBiomeProvider(); + + + //init world + freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( + server, + server.executor, + session, + newWorldData, + originalServerWorld.dimension(), + DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow() + .getOrThrow(levelStemResourceKey), + new RegenNoOpWorldLoadListener(), + originalServerWorld.isDebug(), + seed, + ImmutableList.of(), + false, + originalServerWorld.getRandomSequences(), + environment, + generator, + biomeProvider + ) { + + private final Holder singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess() + .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) + ) : null; + + @Override + public void tick(BooleanSupplier shouldKeepTicking) { //no ticking + } + + @Override + public Holder getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { + if (options.hasBiomeType()) { + return singleBiome; + } + return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome( + biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler() + ); + } + }).get(); + freshWorld.noSave = true; + removeWorldFromWorldsMap(); + newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name + if (paperConfigField != null) { + paperConfigField.set(freshWorld, originalServerWorld.paperConfig()); + } + + ChunkGenerator originalGenerator = originalChunkProvider.getGenerator(); + if (originalGenerator instanceof FlatLevelSource flatLevelSource) { + FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings(); + chunkGenerator = new FlatLevelSource(generatorSettingFlat); + } else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) { + Holder generatorSettingBaseSupplier = (Holder) generatorSettingBaseSupplierField.get( + originalGenerator); + BiomeSource biomeSource; + if (options.hasBiomeType()) { + + biomeSource = new FixedBiomeSource( + DedicatedServer.getServer().registryAccess() + .registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow( + WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType()) + ) + ); + } else { + biomeSource = originalGenerator.getBiomeSource(); + } + chunkGenerator = new NoiseBasedChunkGenerator( + biomeSource, + generatorSettingBaseSupplier + ); + } else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) { + chunkGenerator = customChunkGenerator.getDelegate(); + } else { + LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName()); + return false; + } + if (generator != null) { + chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator); + generateConcurrent = generator.isParallelCapable(); + } +// chunkGenerator.conf = freshWorld.spigotConfig; - Does not exist anymore, may need to be re-addressed + + freshChunkProvider = new ServerChunkCache( + freshWorld, + session, + server.getFixerUpper(), + server.getStructureManager(), + server.executor, + chunkGenerator, + freshWorld.spigotConfig.viewDistance, + freshWorld.spigotConfig.simulationDistance, + server.forceSynchronousWrites(), + new RegenNoOpWorldLoadListener(), + (chunkCoordIntPair, state) -> { + }, + () -> server.overworld().getDataStorage() + ) { + // redirect to LevelChunks created in #createChunks + @Override + public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) { + ChunkAccess chunkAccess = getChunkAt(x, z); + if (chunkAccess == null && create) { + chunkAccess = createChunk(getProtoChunkAt(x, z)); + } + return chunkAccess; + } + }; + + if (seed == originalOpts.seed() && !options.hasBiomeType()) { + // Optimisation for needless ring position calculation when the seed and biome is the same. + ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap); + boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state); + if (hasGeneratedPositions) { + Map>> origPositions = + (Map>>) ringPositionsField.get(state); + Map>> copy = new Object2ObjectArrayMap<>( + origPositions); + ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap); + ringPositionsField.set(newState, copy); + hasGeneratedPositionsField.setBoolean(newState, true); + } + } + + + chunkSourceField.set(freshWorld, freshChunkProvider); + //let's start then + structureTemplateManager = server.getStructureManager(); + threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); + + return true; + } + + @Override + protected void cleanup() { + try { + session.close(); + } catch (Exception ignored) { + } + + //shutdown chunk provider + try { + Fawe.instance().getQueueHandler().sync(() -> { + try { + freshChunkProvider.close(false); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } catch (Exception ignored) { + } + + //remove world from server + try { + Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); + } catch (Exception ignored) { + } + + //delete directory + try { + SafeFiles.tryHardToDeleteDir(tempDir); + } catch (Exception ignored) { + } + } + + @Override + protected ProtoChunk createProtoChunk(int x, int z) { + return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, + this.freshWorld.registryAccess().registryOrThrow(BIOME), null + ); + } + + @Override + protected LevelChunk createChunk(ProtoChunk protoChunk) { + return new LevelChunk( + freshWorld, + protoChunk, + null // we don't want to add entities + ); + } + + @Override + protected ChunkStatusWrap getFullChunkStatus() { + return new ChunkStatusWrap(ChunkStatus.FULL); + } + + @Override + protected List getBlockPopulators() { + return originalServerWorld.getWorld().getPopulators(); + } + + @Override + protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) { + // BlockPopulator#populate has to be called synchronously for TileEntity access + TaskManager.taskManager().task(() -> { + final CraftWorld world = freshWorld.getWorld(); + final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); + blockPopulator.populate(world, random, chunk); + }); + } + + @Override + protected IChunkCache initSourceQueueCache() { + return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { + @Override + public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) { + return getChunkAt(x, z); + } + }; + } + + //util + @SuppressWarnings("unchecked") + private void removeWorldFromWorldsMap() { + Fawe.instance().getQueueHandler().sync(() -> { + try { + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("faweregentempworld"); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); + } + + private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { + return switch (env) { + case NETHER -> LevelStem.NETHER; + case THE_END -> LevelStem.END; + default -> LevelStem.OVERWORLD; + }; + } + + private static class RegenNoOpWorldLoadListener implements ChunkProgressListener { + + private RegenNoOpWorldLoadListener() { + } + + @Override + public void updateSpawnPos(ChunkPos spawnPos) { + } + + @Override + public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { + } + + @Override + public void start() { + + } + + @Override + public void stop() { + } + + // TODO Paper only(?) @Override + public void setChunkRadius(int radius) { + } + + } + + private class FastProtoChunk extends ProtoChunk { + + public FastProtoChunk( + final ChunkPos pos, + final UpgradeData upgradeData, + final LevelHeightAccessor world, + final Registry biomeRegistry, + @Nullable final BlendingData blendingData + ) { + super(pos, upgradeData, world, biomeRegistry, blendingData); + } + + // avoid warning on paper + + // compatibility with spigot + + public boolean generateFlatBedrock() { + return generateFlatBedrock; + } + + // no one will ever see the entities! + @Override + public List getEntities() { + return Collections.emptyList(); + } + + } + + protected class ChunkStatusWrap extends ChunkStatusWrapper { + + private final ChunkStatus chunkStatus; + + public ChunkStatusWrap(ChunkStatus chunkStatus) { + this.chunkStatus = chunkStatus; + } + + @Override + public int requiredNeighborChunkRadius() { + return chunkStatus.getRange(); + } + + @Override + public String name() { + return chunkStatus.toString(); + } + + @Override + public CompletableFuture processChunk(List accessibleChunks) { + return chunkStatus.generate( + Runnable::run, // TODO revisit, we might profit from this somehow? + freshWorld, + chunkGenerator, + structureTemplateManager, + threadedLevelLightEngine, + c -> CompletableFuture.completedFuture(Either.left(c)), + accessibleChunks + ); + } + + } + + /** + * A light engine that does nothing. As light is calculated after pasting anyway, we can avoid + * work this way. + */ + static class NoOpLightEngine extends ThreadedLevelLightEngine { + + private static final ProcessorMailbox MAILBOX = ProcessorMailbox.create(task -> { + }, "fawe-no-op"); + private static final ProcessorHandle> HANDLE = ProcessorHandle.of("fawe-no-op", m -> { + }); + + public NoOpLightEngine(final ServerChunkCache chunkProvider) { + super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE); + } + + @Override + public CompletableFuture lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) { + return CompletableFuture.completedFuture(chunk); + } + + } + +} diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index dfcae5783..272d3c84f 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -206,7 +206,7 @@ tasks { versionNumber.set("${project.version}") versionType.set("release") uploadFile.set(file("build/libs/${rootProject.name}-Bukkit-${project.version}.jar")) - gameVersions.addAll(listOf("1.20.2", "1.20.1", "1.20", "1.19.4", "1.18.2", "1.17.1")) + gameVersions.addAll(listOf("1.20.4", "1.20.3", "1.20.2", "1.20.1", "1.20", "1.19.4", "1.18.2", "1.17.1")) loaders.addAll(listOf("paper", "spigot")) changelog.set("The changelog is available on GitHub: https://github.com/IntellectualSites/" + "FastAsyncWorldEdit/releases/tag/${project.version}") diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java index 55340bb07..237c0b220 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java @@ -86,6 +86,11 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde priorityMap.put(BlockTypes.WHITE_BED, PlacementPriority.LAST); priorityMap.put(BlockTypes.YELLOW_BED, PlacementPriority.LAST); priorityMap.put(BlockTypes.GRASS, PlacementPriority.LAST); + // Keep "grass" for <1.20.3 compat + @SuppressWarnings("deprecation") + BlockType grass = BlockTypes.GRASS; + priorityMap.put(grass, PlacementPriority.LAST); + priorityMap.put(BlockTypes.SHORT_GRASS, PlacementPriority.LAST); priorityMap.put(BlockTypes.TALL_GRASS, PlacementPriority.LAST); priorityMap.put(BlockTypes.ROSE_BUSH, PlacementPriority.LAST); priorityMap.put(BlockTypes.DANDELION, PlacementPriority.LAST); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/FloraGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/FloraGenerator.java index 5ea9aa66e..a1ddb2240 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/FloraGenerator.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/FloraGenerator.java @@ -29,6 +29,7 @@ import com.sk89q.worldedit.function.pattern.RandomPattern; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; /** @@ -103,7 +104,12 @@ public class FloraGenerator implements RegionFunction { */ public static Pattern getTemperatePattern() { RandomPattern pattern = new RandomPattern(); - pattern.add(BlockTypes.GRASS.getDefaultState(), 300); + BlockType grass = BlockTypes.SHORT_GRASS; + if (grass == null) { + // Fallback for <1.20.3 compat + grass = BlockTypes.GRASS; + } + pattern.add(grass.getDefaultState(), 300); pattern.add(BlockTypes.POPPY.getDefaultState(), 5); pattern.add(BlockTypes.DANDELION.getDefaultState(), 5); return pattern; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategories.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategories.java index 47905dcd4..4d1bde5f8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategories.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockCategories.java @@ -48,6 +48,7 @@ public final class BlockCategories { public static final BlockCategory BIG_DRIPLEAF_PLACEABLE = get("minecraft:big_dripleaf_placeable"); public static final BlockCategory BIRCH_LOGS = get("minecraft:birch_logs"); public static final BlockCategory BUTTONS = get("minecraft:buttons"); + public static final BlockCategory CAMEL_SAND_STEP_SOUND_BLOCKS = get("minecraft:camel_sand_step_sound_blocks"); public static final BlockCategory CAMPFIRES = get("minecraft:campfires"); public static final BlockCategory CANDLE_CAKES = get("minecraft:candle_cakes"); public static final BlockCategory CANDLES = get("minecraft:candles"); @@ -60,6 +61,7 @@ public final class BlockCategories { public static final BlockCategory COAL_ORES = get("minecraft:coal_ores"); public static final BlockCategory COMBINATION_STEP_SOUND_BLOCKS = get("minecraft:combination_step_sound_blocks"); public static final BlockCategory COMPLETES_FIND_TREE_TUTORIAL = get("minecraft:completes_find_tree_tutorial"); + public static final BlockCategory CONCRETE_POWDER = get("minecraft:concrete_powder"); public static final BlockCategory CONVERTABLE_TO_MUD = get("minecraft:convertable_to_mud"); public static final BlockCategory COPPER_ORES = get("minecraft:copper_ores"); public static final BlockCategory CORAL_BLOCKS = get("minecraft:coral_blocks"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java index 0cd3f55e5..4b3204be4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockTypes.java @@ -415,6 +415,8 @@ public final class BlockTypes { @Nullable public static final BlockType CHISELED_BOOKSHELF = init(); @Nullable + public static final BlockType CHISELED_COPPER = init(); + @Nullable public static final BlockType CHISELED_DEEPSLATE = init(); @Nullable public static final BlockType CHISELED_NETHER_BRICKS = init(); @@ -429,6 +431,10 @@ public final class BlockTypes { @Nullable public static final BlockType CHISELED_STONE_BRICKS = init(); @Nullable + public static final BlockType CHISELED_TUFF = init(); + @Nullable + public static final BlockType CHISELED_TUFF_BRICKS = init(); + @Nullable public static final BlockType CHORUS_FLOWER = init(); @Nullable public static final BlockType CHORUS_PLANT = init(); @@ -471,6 +477,12 @@ public final class BlockTypes { @Nullable public static final BlockType COPPER_BLOCK = init(); @Nullable + public static final BlockType COPPER_BULB = init(); + @Nullable + public static final BlockType COPPER_DOOR = init(); + @Nullable + public static final BlockType COPPER_GRATE = init(); + @Nullable public static final BlockType CORNFLOWER = init(); @Nullable public static final BlockType CRACKED_DEEPSLATE_BRICKS = init(); @@ -673,6 +685,8 @@ public final class BlockTypes { @Nullable public static final BlockType DEEPSLATE_COPPER_ORE = init(); @Nullable + public static final BlockType COPPER_TRAPDOOR = init(); + @Nullable public static final BlockType DEEPSLATE_DIAMOND_ORE = init(); @Nullable public static final BlockType DEEPSLATE_EMERALD_ORE = init(); @@ -733,6 +747,8 @@ public final class BlockTypes { @Nullable public static final BlockType ENDER_CHEST = init(); @Nullable + public static final BlockType EXPOSED_CHISELED_COPPER = init(); + @Nullable public static final BlockType END_GATEWAY = init(); @Nullable public static final BlockType END_PORTAL = init(); @@ -753,6 +769,14 @@ public final class BlockTypes { @Nullable public static final BlockType EXPOSED_COPPER = init(); @Nullable + public static final BlockType EXPOSED_COPPER_BULB = init(); + @Nullable + public static final BlockType EXPOSED_COPPER_DOOR = init(); + @Nullable + public static final BlockType EXPOSED_COPPER_GRATE = init(); + @Nullable + public static final BlockType EXPOSED_COPPER_TRAPDOOR = init(); + @Nullable public static final BlockType EXPOSED_CUT_COPPER = init(); @Nullable public static final BlockType EXPOSED_CUT_COPPER_SLAB = init(); @@ -808,7 +832,7 @@ public final class BlockTypes { public static final BlockType GRANITE_STAIRS = init(); @Nullable public static final BlockType GRANITE_WALL = init(); - @Nullable + @Nullable @Deprecated public static final BlockType GRASS = init(); @Nullable public static final BlockType GRASS_BLOCK = init(); @@ -901,6 +925,8 @@ public final class BlockTypes { @Nullable public static final BlockType INFESTED_CRACKED_STONE_BRICKS = init(); @Nullable + public static final BlockType CRAFTER = init(); + @Nullable public static final BlockType INFESTED_DEEPSLATE = init(); @Nullable public static final BlockType INFESTED_MOSSY_STONE_BRICKS = init(); @@ -1293,8 +1319,18 @@ public final class BlockTypes { @Nullable public static final BlockType OXEYE_DAISY = init(); @Nullable + public static final BlockType OXIDIZED_CHISELED_COPPER = init(); + @Nullable public static final BlockType OXIDIZED_COPPER = init(); @Nullable + public static final BlockType OXIDIZED_COPPER_BULB = init(); + @Nullable + public static final BlockType OXIDIZED_COPPER_DOOR = init(); + @Nullable + public static final BlockType OXIDIZED_COPPER_GRATE = init(); + @Nullable + public static final BlockType OXIDIZED_COPPER_TRAPDOOR = init(); + @Nullable public static final BlockType OXIDIZED_CUT_COPPER = init(); @Nullable public static final BlockType OXIDIZED_CUT_COPPER_SLAB = init(); @@ -1411,6 +1447,14 @@ public final class BlockTypes { @Nullable public static final BlockType POLISHED_GRANITE_STAIRS = init(); @Nullable + public static final BlockType POLISHED_TUFF = init(); + @Nullable + public static final BlockType POLISHED_TUFF_SLAB = init(); + @Nullable + public static final BlockType POLISHED_TUFF_STAIRS = init(); + @Nullable + public static final BlockType POLISHED_TUFF_WALL = init(); + @Nullable public static final BlockType POPPY = init(); @Nullable public static final BlockType POTATOES = init(); @@ -1667,6 +1711,8 @@ public final class BlockTypes { @Nullable public static final BlockType SEAGRASS = init(); @Nullable + public static final BlockType SHORT_GRASS = init(); + @Nullable public static final BlockType SEA_LANTERN = init(); @Nullable public static final BlockType SEA_PICKLE = init(); @@ -1874,6 +1920,8 @@ public final class BlockTypes { @Nullable public static final BlockType TRAPPED_CHEST = init(); @Nullable + public static final BlockType TRIAL_SPAWNER = init(); + @Nullable public static final BlockType TRIPWIRE = init(); @Nullable public static final BlockType TRIPWIRE_HOOK = init(); @@ -1888,6 +1936,20 @@ public final class BlockTypes { @Nullable public static final BlockType TUFF = init(); @Nullable + public static final BlockType TUFF_BRICK_SLAB = init(); + @Nullable + public static final BlockType TUFF_BRICK_STAIRS = init(); + @Nullable + public static final BlockType TUFF_BRICK_WALL = init(); + @Nullable + public static final BlockType TUFF_BRICKS = init(); + @Nullable + public static final BlockType TUFF_SLAB = init(); + @Nullable + public static final BlockType TUFF_STAIRS = init(); + @Nullable + public static final BlockType TUFF_WALL = init(); + @Nullable public static final BlockType TURTLE_EGG = init(); @Nullable public static final BlockType TWISTING_VINES = init(); @@ -1947,16 +2009,36 @@ public final class BlockTypes { @Nullable public static final BlockType WATER_CAULDRON = init(); @Nullable + public static final BlockType WAXED_CHISELED_COPPER = init(); + @Nullable public static final BlockType WAXED_COPPER_BLOCK = init(); @Nullable + public static final BlockType WAXED_COPPER_BULB = init(); + @Nullable + public static final BlockType WAXED_COPPER_DOOR = init(); + @Nullable + public static final BlockType WAXED_COPPER_GRATE = init(); + @Nullable + public static final BlockType WAXED_COPPER_TRAPDOOR = init(); + @Nullable public static final BlockType WAXED_CUT_COPPER = init(); @Nullable public static final BlockType WAXED_CUT_COPPER_SLAB = init(); @Nullable public static final BlockType WAXED_CUT_COPPER_STAIRS = init(); + @ Nullable + public static final BlockType WAXED_EXPOSED_CHISELED_COPPER = init(); @Nullable public static final BlockType WAXED_EXPOSED_COPPER = init(); @Nullable + public static final BlockType WAXED_EXPOSED_COPPER_BULB = init(); + @Nullable + public static final BlockType WAXED_EXPOSED_COPPER_DOOR = init(); + @Nullable + public static final BlockType WAXED_EXPOSED_COPPER_GRATE = init(); + @Nullable + public static final BlockType WAXED_EXPOSED_COPPER_TRAPDOOR = init(); + @Nullable public static final BlockType WAXED_EXPOSED_CUT_COPPER = init(); @Nullable public static final BlockType WAXED_EXPOSED_CUT_COPPER_SLAB = init(); @@ -1965,22 +2047,52 @@ public final class BlockTypes { @Nullable public static final BlockType WAXED_OXIDIZED_COPPER = init(); @Nullable + public static final BlockType WAXED_OXIDIZED_COPPER_BULB = init(); + @Nullable + public static final BlockType WAXED_OXIDIZED_COPPER_DOOR = init(); + @Nullable + public static final BlockType WAXED_OXIDIZED_COPPER_GRATE = init(); + @Nullable + public static final BlockType WAXED_OXIDIZED_COPPER_TRAPDOOR = init(); + @Nullable + public static final BlockType WAXED_OXIDIZED_CHISELED_COPPER = init(); + @Nullable public static final BlockType WAXED_OXIDIZED_CUT_COPPER = init(); @Nullable public static final BlockType WAXED_OXIDIZED_CUT_COPPER_SLAB = init(); @Nullable public static final BlockType WAXED_OXIDIZED_CUT_COPPER_STAIRS = init(); @Nullable + public static final BlockType WAXED_WEATHERED_CHISELED_COPPER = init(); + @Nullable public static final BlockType WAXED_WEATHERED_COPPER = init(); @Nullable + public static final BlockType WAXED_WEATHERED_COPPER_BULB = init(); + @Nullable + public static final BlockType WAXED_WEATHERED_COPPER_DOOR = init(); + @Nullable + public static final BlockType WAXED_WEATHERED_COPPER_GRATE = init(); + @Nullable + public static final BlockType WAXED_WEATHERED_COPPER_TRAPDOOR = init(); + @Nullable public static final BlockType WAXED_WEATHERED_CUT_COPPER = init(); @Nullable public static final BlockType WAXED_WEATHERED_CUT_COPPER_SLAB = init(); @Nullable public static final BlockType WAXED_WEATHERED_CUT_COPPER_STAIRS = init(); @Nullable + public static final BlockType WEATHERED_CHISELED_COPPER = init(); + @Nullable public static final BlockType WEATHERED_COPPER = init(); @Nullable + public static final BlockType WEATHERED_COPPER_BULB = init(); + @Nullable + public static final BlockType WEATHERED_COPPER_DOOR = init(); + @Nullable + public static final BlockType WEATHERED_COPPER_GRATE = init(); + @Nullable + public static final BlockType WEATHERED_COPPER_TRAPDOOR = init(); + @Nullable public static final BlockType WEATHERED_CUT_COPPER = init(); @Nullable public static final BlockType WEATHERED_CUT_COPPER_SLAB = init(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityTypes.java index 4b650ac07..f981593fe 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/entity/EntityTypes.java @@ -51,6 +51,8 @@ public final class EntityTypes { @Nullable public static final EntityType BOAT = get("minecraft:boat"); @Nullable + public static final EntityType BREEZE = get("minecraft:breeze"); + @Nullable public static final EntityType CAMEL = get("minecraft:camel"); @Nullable public static final EntityType CAT = get("minecraft:cat"); @@ -259,6 +261,8 @@ public final class EntityTypes { @Nullable public static final EntityType WARDEN = get("minecraft:warden"); @Nullable + public static final EntityType WIND_CHARGE = get("minecraft:wind_charge"); + @Nullable public static final EntityType WITCH = get("minecraft:witch"); @Nullable public static final EntityType WITHER = get("minecraft:wither"); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemTypes.java index 663cf5c61..b8064bb94 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemTypes.java @@ -319,6 +319,8 @@ public final class ItemTypes { @Nullable public static final ItemType BREAD = init(); @Nullable + public static final ItemType BREEZE_SPAWN_EGG = init(); + @Nullable public static final ItemType BREWER_POTTERY_SHERD = init(); @Nullable public static final ItemType BREWING_STAND = init(); @@ -468,6 +470,8 @@ public final class ItemTypes { @Nullable public static final ItemType CHISELED_BOOKSHELF = init(); @Nullable + public static final ItemType CHISELED_COPPER = init(); + @Nullable public static final ItemType CHISELED_DEEPSLATE = init(); @Nullable public static final ItemType CHISELED_NETHER_BRICKS = init(); @@ -482,6 +486,10 @@ public final class ItemTypes { @Nullable public static final ItemType CHISELED_STONE_BRICKS = init(); @Nullable + public static final ItemType CHISELED_TUFF = init(); + @Nullable + public static final ItemType CHISELED_TUFF_BRICKS = init(); + @Nullable public static final ItemType CHORUS_FLOWER = init(); @Nullable public static final ItemType CHORUS_FRUIT = init(); @@ -560,10 +568,18 @@ public final class ItemTypes { @Nullable public static final ItemType COPPER_BLOCK = init(); @Nullable + public static final ItemType COPPER_BULB = init(); + @Nullable + public static final ItemType COPPER_DOOR = init(); + @Nullable + public static final ItemType COPPER_GRATE = init(); + @Nullable public static final ItemType COPPER_INGOT = init(); @Nullable public static final ItemType COPPER_ORE = init(); @Nullable + public static final ItemType COPPER_TRAPDOOR = init(); + @Nullable public static final ItemType CORNFLOWER = init(); @Nullable public static final ItemType COW_SPAWN_EGG = init(); @@ -578,6 +594,8 @@ public final class ItemTypes { @Nullable public static final ItemType CRACKED_STONE_BRICKS = init(); @Nullable + public static final ItemType CRAFTER = init(); + @Nullable public static final ItemType CRAFTING_TABLE = init(); @Nullable public static final ItemType CREEPER_BANNER_PATTERN = init(); @@ -903,8 +921,18 @@ public final class ItemTypes { @Nullable public static final ItemType EXPLORER_POTTERY_SHERD = init(); @Nullable + public static final ItemType EXPOSED_CHISELED_COPPER = init(); + @Nullable public static final ItemType EXPOSED_COPPER = init(); @Nullable + public static final ItemType EXPOSED_COPPER_BULB = init(); + @Nullable + public static final ItemType EXPOSED_COPPER_DOOR = init(); + @Nullable + public static final ItemType EXPOSED_COPPER_GRATE = init(); + @Nullable + public static final ItemType EXPOSED_COPPER_TRAPDOOR = init(); + @Nullable public static final ItemType EXPOSED_CUT_COPPER = init(); @Nullable public static final ItemType EXPOSED_CUT_COPPER_SLAB = init(); @@ -1036,7 +1064,7 @@ public final class ItemTypes { public static final ItemType GRANITE_STAIRS = init(); @Nullable public static final ItemType GRANITE_WALL = init(); - @Nullable + @Nullable @Deprecated public static final ItemType GRASS = init(); @Nullable public static final ItemType GRASS_BLOCK = init(); @@ -1670,8 +1698,18 @@ public final class ItemTypes { @Nullable public static final ItemType OXEYE_DAISY = init(); @Nullable + public static final ItemType OXIDIZED_CHISELED_COPPER = init(); + @Nullable public static final ItemType OXIDIZED_COPPER = init(); @Nullable + public static final ItemType OXIDIZED_COPPER_BULB = init(); + @Nullable + public static final ItemType OXIDIZED_COPPER_DOOR = init(); + @Nullable + public static final ItemType OXIDIZED_COPPER_GRATE = init(); + @Nullable + public static final ItemType OXIDIZED_COPPER_TRAPDOOR = init(); + @Nullable public static final ItemType OXIDIZED_CUT_COPPER = init(); @Nullable public static final ItemType OXIDIZED_CUT_COPPER_SLAB = init(); @@ -1808,6 +1846,14 @@ public final class ItemTypes { @Nullable public static final ItemType POLISHED_GRANITE_STAIRS = init(); @Nullable + public static final ItemType POLISHED_TUFF = init(); + @Nullable + public static final ItemType POLISHED_TUFF_SLAB = init(); + @Nullable + public static final ItemType POLISHED_TUFF_STAIRS = init(); + @Nullable + public static final ItemType POLISHED_TUFF_WALL = init(); + @Nullable public static final ItemType POPPED_CHORUS_FRUIT = init(); @Nullable public static final ItemType POPPY = init(); @@ -2061,6 +2107,8 @@ public final class ItemTypes { @Nullable public static final ItemType SHIELD = init(); @Nullable + public static final ItemType SHORT_GRASS = init(); + @Nullable public static final ItemType SHROOMLIGHT = init(); @Nullable public static final ItemType SHULKER_BOX = init(); @@ -2336,6 +2384,10 @@ public final class ItemTypes { @Nullable public static final ItemType TRAPPED_CHEST = init(); @Nullable + public static final ItemType TRIAL_KEY = init(); + @Nullable + public static final ItemType TRIAL_SPAWNER = init(); + @Nullable public static final ItemType TRIDENT = init(); @Nullable public static final ItemType TRIPWIRE_HOOK = init(); @@ -2354,6 +2406,20 @@ public final class ItemTypes { @Nullable public static final ItemType TUFF = init(); @Nullable + public static final ItemType TUFF_BRICK_SLAB = init(); + @Nullable + public static final ItemType TUFF_BRICK_STAIRS = init(); + @Nullable + public static final ItemType TUFF_BRICK_WALL = init(); + @Nullable + public static final ItemType TUFF_BRICKS = init(); + @Nullable + public static final ItemType TUFF_SLAB = init(); + @Nullable + public static final ItemType TUFF_STAIRS = init(); + @Nullable + public static final ItemType TUFF_WALL = init(); + @Nullable public static final ItemType TURTLE_EGG = init(); @Nullable public static final ItemType TURTLE_HELMET = init(); @@ -2418,32 +2484,72 @@ public final class ItemTypes { @Nullable public static final ItemType WATER_BUCKET = init(); @Nullable + public static final ItemType WAXED_CHISELED_COPPER = init(); + @Nullable public static final ItemType WAXED_COPPER_BLOCK = init(); @Nullable + public static final ItemType WAXED_COPPER_BULB = init(); + @Nullable + public static final ItemType WAXED_COPPER_DOOR = init(); + @Nullable + public static final ItemType WAXED_COPPER_GRATE = init(); + @Nullable + public static final ItemType WAXED_COPPER_TRAPDOOR = init(); + @Nullable public static final ItemType WAXED_CUT_COPPER = init(); @Nullable public static final ItemType WAXED_CUT_COPPER_SLAB = init(); @Nullable public static final ItemType WAXED_CUT_COPPER_STAIRS = init(); @Nullable + public static final ItemType WAXED_EXPOSED_CHISELED_COPPER = init(); + @Nullable public static final ItemType WAXED_EXPOSED_COPPER = init(); @Nullable + public static final ItemType WAXED_EXPOSED_COPPER_BULB = init(); + @Nullable + public static final ItemType WAXED_EXPOSED_COPPER_DOOR = init(); + @Nullable + public static final ItemType WAXED_EXPOSED_COPPER_GRATE = init(); + @Nullable + public static final ItemType WAXED_EXPOSED_COPPER_TRAPDOOR = init(); + @Nullable public static final ItemType WAXED_EXPOSED_CUT_COPPER = init(); @Nullable public static final ItemType WAXED_EXPOSED_CUT_COPPER_SLAB = init(); @Nullable public static final ItemType WAXED_EXPOSED_CUT_COPPER_STAIRS = init(); @Nullable + public static final ItemType WAXED_OXIDIZED_CHISELED_COPPER = init(); + @Nullable public static final ItemType WAXED_OXIDIZED_COPPER = init(); @Nullable + public static final ItemType WAXED_OXIDIZED_COPPER_BULB = init(); + @Nullable + public static final ItemType WAXED_OXIDIZED_COPPER_DOOR = init(); + @Nullable + public static final ItemType WAXED_OXIDIZED_COPPER_GRATE = init(); + @Nullable + public static final ItemType WAXED_OXIDIZED_COPPER_TRAPDOOR = init(); + @Nullable public static final ItemType WAXED_OXIDIZED_CUT_COPPER = init(); @Nullable public static final ItemType WAXED_OXIDIZED_CUT_COPPER_SLAB = init(); @Nullable public static final ItemType WAXED_OXIDIZED_CUT_COPPER_STAIRS = init(); @Nullable + public static final ItemType WAXED_WEATHERED_CHISELED_COPPER = init(); + @Nullable public static final ItemType WAXED_WEATHERED_COPPER = init(); @Nullable + public static final ItemType WAXED_WEATHERED_COPPER_BULB = init(); + @Nullable + public static final ItemType WAXED_WEATHERED_COPPER_DOOR = init(); + @Nullable + public static final ItemType WAXED_WEATHERED_COPPER_GRATE = init(); + @Nullable + public static final ItemType WAXED_WEATHERED_COPPER_TRAPDOOR = init(); + @Nullable public static final ItemType WAXED_WEATHERED_CUT_COPPER = init(); @Nullable public static final ItemType WAXED_WEATHERED_CUT_COPPER_SLAB = init(); @@ -2452,8 +2558,18 @@ public final class ItemTypes { @Nullable public static final ItemType WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE = init(); @Nullable + public static final ItemType WEATHERED_CHISELED_COPPER = init(); + @Nullable public static final ItemType WEATHERED_COPPER = init(); @Nullable + public static final ItemType WEATHERED_COPPER_BULB = init(); + @Nullable + public static final ItemType WEATHERED_COPPER_DOOR = init(); + @Nullable + public static final ItemType WEATHERED_COPPER_GRATE = init(); + @Nullable + public static final ItemType WEATHERED_COPPER_TRAPDOOR = init(); + @Nullable public static final ItemType WEATHERED_CUT_COPPER = init(); @Nullable public static final ItemType WEATHERED_CUT_COPPER_SLAB = init(); From 8496ddf5b87532475ceab4664df10c11ddba9dae Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 8 Dec 2023 07:37:23 +0100 Subject: [PATCH 018/114] Release 2.8.3 --- build.gradle.kts | 2 +- worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts | 2 +- worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts | 2 +- worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 390870651..a45dc3e4c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s-%s", rootVersion, buildNumber) +version = String.format("%s", rootVersion) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") diff --git a/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts index 20bff7359..227eee362 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - the().paperDevBundle("1.20.1-R0.1-20230916.212543-167") + the().paperDevBundle("1.20.1-R0.1-20230921.165944-178") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts index 70c495b7b..3b08adfe8 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - the().paperDevBundle("1.20.2-R0.1-20231125.095734-103") + the().paperDevBundle("1.20.2-R0.1-20231203.034718-121") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index befcb5705..34dda434a 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - the().paperDevBundle("1.20.3-R0.1-20231207.043048-3") + the().paperDevBundle("1.20.4-R0.1-20231207.202833-1") compileOnly(libs.paperlib) } From 6d295bde9a85933c6a740bd21f150ec5577ad893 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 8 Dec 2023 07:48:14 +0100 Subject: [PATCH 019/114] Back to snapshot for development --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a45dc3e4c..8d8f75c3c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.8.3") +var rootVersion by extra("2.8.4") var snapshot by extra("SNAPSHOT") var revision: String by extra("") var buildNumber by extra("") @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s", rootVersion) +version = String.format("%s-%s", rootVersion, buildNumber) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") From 026b2eca9c83fe558981b973f2a4782217270ec3 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 8 Dec 2023 16:26:06 +0100 Subject: [PATCH 020/114] Update Paperweight --- .../bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java index 2dc7d4b2b..899c6e0c4 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext.fawe/v1_20_R3/PaperweightAdapter.java @@ -181,8 +181,8 @@ public final class PaperweightAdapter implements BukkitImplAdapter Date: Fri, 8 Dec 2023 16:30:22 +0100 Subject: [PATCH 021/114] Update dependency com.modrinth.minotaur to v2.8.7 (#2513) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5ac2c2ab9..9cfc77234 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -50,7 +50,7 @@ mockito = "5.8.0" # Gradle plugins pluginyml = "0.6.0" -minotaur = "2.8.6" +minotaur = "2.8.7" [libraries] # Minecraft expectations From 4c3dd9accfbc5cb228897d991564b29a536ded1c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:30:31 +0100 Subject: [PATCH 022/114] Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.5.10 (#2514) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index a65407ce5..8b4b57369 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { implementation(gradleApi()) implementation("org.ajoberstar.grgit:grgit-gradle:5.2.1") implementation("com.github.johnrengelman:shadow:8.1.1") - implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5") + implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.10") } kotlin { From fe626a92e14ec1fb9db38a52895c131c9230443b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:30:50 +0100 Subject: [PATCH 023/114] Update dependency org.checkerframework:checker-qual to v3.41.0 (#2515) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9cfc77234..189057a75 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ sparsebitset = "1.3" parallelgzip = "1.0.5" adventure = "4.14.0" adventure-bukkit = "4.3.1" -checkerqual = "3.40.0" +checkerqual = "3.41.0" truezip = "6.8.4" auto-value = "1.10.4" findbugs = "3.0.2" From d4b68b384bf7d5d1baa82ec7a023b371ec550ec9 Mon Sep 17 00:00:00 2001 From: Jordan Date: Tue, 12 Dec 2023 07:00:40 +0000 Subject: [PATCH 024/114] fix: add chunk loc to tile entity location when trimming (#2500) - fixes #2499 --- .../core/extent/HeightBoundExtent.java | 6 ++--- .../core/queue/IBatchProcessor.java | 22 +++++++++++++++++++ .../fastasyncworldedit/core/queue/IChunk.java | 11 ++++++++++ .../sk89q/worldedit/regions/CuboidRegion.java | 14 +++++++----- .../com/sk89q/worldedit/regions/Region.java | 6 +++-- 5 files changed, 48 insertions(+), 11 deletions(-) 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 ff57e00da..c22d9e6be 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 @@ -6,12 +6,11 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.regions.RegionWrapper; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import java.util.Collection; import java.util.Collections; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; public class HeightBoundExtent extends FaweRegionExtent { @@ -50,7 +49,8 @@ public class HeightBoundExtent extends FaweRegionExtent { @Override public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { - if (trimY(set, min, max, true) | trimNBT(set, this::contains)) { + BlockVector3 chunkPos = chunk.getChunkBlockCoord().withY(0); + if (trimY(set, min, max, true) | trimNBT(set, this::contains, pos -> this.contains(pos.add(chunkPos)))) { return set; } return null; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java index 5eeafe28e..b096b93fe 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java @@ -155,7 +155,9 @@ public interface IBatchProcessor { * Utility method to trim entity and blocks with a provided contains function. * * @return false if chunk is empty of NBT + * @deprecated tiles are stored in chunk-normalised coordinate space and thus cannot use the same function as entities */ + @Deprecated(forRemoval = true, since = "TODO") default boolean trimNBT(IChunkSet set, Function contains) { Set ents = set.getEntities(); if (!ents.isEmpty()) { @@ -169,6 +171,26 @@ public interface IBatchProcessor { return !tiles.isEmpty() || !ents.isEmpty(); } + /** + * Utility method to trim entity and blocks with a provided contains function. + * + * @return false if chunk is empty of NBT + * @since TODO + */ + default boolean trimNBT( + IChunkSet set, Function containsEntity, Function containsTile + ) { + Set ents = set.getEntities(); + if (!ents.isEmpty()) { + ents.removeIf(ent -> !containsEntity.apply(ent.getEntityPosition().toBlockPoint())); + } + Map tiles = set.getTiles(); + if (!tiles.isEmpty()) { + tiles.entrySet().removeIf(blockVector3CompoundTagEntry -> !containsTile.apply(blockVector3CompoundTagEntry.getKey())); + } + return !tiles.isEmpty() || !ents.isEmpty(); + } + /** * Join two processors and return the result. */ diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java index 0acfa4a22..5a03a9987 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.core.queue; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; +import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import javax.annotation.Nullable; @@ -33,6 +34,16 @@ public interface IChunk extends Trimable, IChunkGet, IChunkSet { */ int getZ(); + /** + * Return the minimum block coordinate of the chunk + * + * @return BlockVector3 of minimum block coordinate + * @since TODO + */ + default BlockVector3 getChunkBlockCoord() { + return BlockVector3.at(getX() << 4, getMinY(), getZ() << 4); + } + /** * If the chunk is a delegate, returns its parent's root * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java index cc2f03aa2..d04e5aa8c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CuboidRegion.java @@ -22,7 +22,6 @@ package com.sk89q.worldedit.regions; import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.math.BlockVectorSet; -import com.fastasyncworldedit.core.math.MutableBlockVector2; import com.fastasyncworldedit.core.math.MutableBlockVector3; import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IChunk; @@ -805,7 +804,8 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { return set; } trimY(set, minY, maxY, true); - trimNBT(set, this::contains); + BlockVector3 chunkPos = chunk.getChunkBlockCoord().withY(0); + trimNBT(set, this::contains, pos -> this.contains(pos.add(chunkPos))); return set; } if (tx >= minX && bx <= maxX && tz >= minZ && bz <= maxZ) { @@ -868,8 +868,8 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { } set.setBlocks(layer, arr); } - - trimNBT(set, this::contains); + final BlockVector3 chunkPos = BlockVector3.at(chunk.getX() << 4, 0, chunk.getZ() << 4); + trimNBT(set, this::contains, pos -> this.contains(pos.add(chunkPos))); return set; } return null; @@ -893,7 +893,8 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { return null; } trimY(set, minY, maxY, false); - trimNBT(set, this::contains); + BlockVector3 chunkPos = chunk.getChunkBlockCoord().withY(0); + trimNBT(set, this::contains, pos -> this.contains(pos.add(chunkPos))); return set; } if (tx >= minX && bx <= maxX && tz >= minZ && bz <= maxZ) { @@ -943,7 +944,8 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion { } set.setBlocks(layer, arr); } - trimNBT(set, bv3 -> !this.contains(bv3)); + BlockVector3 chunkPos = chunk.getChunkBlockCoord().withY(0); + trimNBT(set, bv3 -> !this.contains(bv3), bv3 -> !this.contains(bv3.add(chunkPos))); return set; } return set; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java index b89536520..d2fa35310 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/Region.java @@ -425,7 +425,8 @@ public interface Region extends Iterable, Cloneable, IBatchProcess } } if (processExtra) { - trimNBT(set, this::contains); + BlockVector3 chunkPos = chunk.getChunkBlockCoord().withY(0); + trimNBT(set, this::contains, pos -> this.contains(pos.add(chunkPos))); } return set; } else { @@ -477,7 +478,8 @@ public interface Region extends Iterable, Cloneable, IBatchProcess } } if (processExtra) { - trimNBT(set, bv3 -> !this.contains(bv3)); + BlockVector3 chunkPos = chunk.getChunkBlockCoord().withY(0); + trimNBT(set, bv3 -> !this.contains(bv3), bv3 -> !this.contains(bv3.add(chunkPos))); } return set; } else { From d4d7cb03861decf1287a97396513b0f95d5efc20 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 22:40:32 +0100 Subject: [PATCH 025/114] Update plotsquared to v7.2.1 (#2520) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 189057a75..50b96e690 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" towny = "0.100.0.8" -plotsquared = "7.2.0" +plotsquared = "7.2.1" # Third party bstats = "3.0.2" From 0d35cfb6b30665c740bbba1feb6d0bc92e3e8d82 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 22:40:44 +0100 Subject: [PATCH 026/114] Update dependency org.checkerframework:checker-qual to v3.42.0 (#2521) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 50b96e690..c8d3867ad 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ sparsebitset = "1.3" parallelgzip = "1.0.5" adventure = "4.14.0" adventure-bukkit = "4.3.1" -checkerqual = "3.41.0" +checkerqual = "3.42.0" truezip = "6.8.4" auto-value = "1.10.4" findbugs = "3.0.2" From 885df021c1a0f4e87ad67f59f43525e8a8508e2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Dec 2023 22:42:42 +0100 Subject: [PATCH 027/114] Update github/codeql-action action to v3 (#2523) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8dd831550..b7e9c61ed 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,10 +27,10 @@ jobs: cache: gradle java-version: 17 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 17abaeb19e1932b364151d3bbdf1eb0bd9340f7d Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 16 Dec 2023 22:45:30 +0100 Subject: [PATCH 028/114] Update paper --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c8d3867ad..57aaf1fcf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] # Minecraft expectations -paper = "1.20.2-R0.1-SNAPSHOT" +paper = "1.20.4-R0.1-SNAPSHOT" fastutil = "8.5.9" guava = "31.1-jre" log4j = "2.19.0" From f44b1b48c7cca1ab1e7a118e479209cd956b9e85 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 18 Dec 2023 14:58:53 +0000 Subject: [PATCH 029/114] chore: add a more informative error when parsing block properties (#2524) --- .../worldedit/world/block/BlockState.java | 27 ++++++++++++++++--- .../src/main/resources/lang/strings.json | 1 + 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java index f08675fc5..5059c737a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/block/BlockState.java @@ -178,9 +178,18 @@ public class BlockState implements BlockStateHolder, Pattern { String name = property.getName(); charSequence.setSubstring(propStrStart + name.length() + 2, state.length() - 1); - int index = charSequence.length() <= 0 ? -1 : property.getIndexFor(charSequence); - if (index != -1) { - return type.withPropertyId(index); + try { + int index = charSequence.length() <= 0 ? -1 : property.getIndexFor(charSequence); + if (index != -1) { + return type.withPropertyId(index); + } + } catch (Exception e) { + throw new InputParseException(Caption.of( + "fawe.error.invalid-block-state-property", + TextComponent.of(charSequence.toString()), + TextComponent.of(name), + TextComponent.of(state) + ), e); } } int stateId; @@ -200,7 +209,17 @@ public class BlockState implements BlockStateHolder, Pattern { case ',': { charSequence.setSubstring(last, i); if (property != null) { - int index = property.getIndexFor(charSequence); + int index; + try { + index = property.getIndexFor(charSequence); + } catch (Exception e) { + throw new InputParseException(Caption.of( + "fawe.error.invalid-block-state-property", + TextComponent.of(charSequence.toString()), + TextComponent.of(property.getName()), + TextComponent.of(state) + ), e); + } if (index == -1) { throw SuggestInputParseException.of(charSequence.toString(), (List) property.getValues()); } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index d702ead53..3d1295ab0 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -92,6 +92,7 @@ "fawe.error.parser.invalid-data": "Invalid data: {0}", "fawe.error.unsupported": "Unsupported!", "fawe.error.invalid-block-type": "Does not match a valid block type: {0}", + "fawe.error.invalid-block-state-property": "Cannot parse value `{0}` for property `{1}`, block state: `{2}`", "fawe.error.nbt.forbidden": "You are not allowed to use nbt. Lacking permission: {0}", "fawe.error.invalid-arguments": "Invalid amount of arguments. Expected: {0}", "fawe.error.unrecognised-tag": "Unrecognised tag: {0} {1}", From 6caf4640ea85fdfbd883b12986246c0409beaab4 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 18 Dec 2023 15:04:34 +0000 Subject: [PATCH 030/114] feat: add config option for if the DOC should lock the file channel (#2526) - related to #2222 - effectively just a workaround as most people would not need to worry about locking the clipboard file --- .../core/configuration/Settings.java | 7 ++++ .../clipboard/DiskOptimizedClipboard.java | 32 ++++++++++--------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index 099386f00..009958270 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -721,6 +721,13 @@ public class Settings extends Config { " - Requires clipboard.use-disk to be enabled" }) public boolean SAVE_CLIPBOARD_NBT_TO_DISK = true; + @Comment({ + "Apply a file lock on the clipboard file (only relevant if clipboad.on-disk is enabled)", + " - Prevents other processes using the file whilst in use by FAWE", + " - This extends to other servers, useful if you have multiple servers using a unified clipboard folder", + " - May run into issues where a file lock is not correctly lifted" + }) + public boolean LOCK_CLIPBOARD_FILE = false; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java index 3f7c746c3..76f22a194 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java @@ -305,22 +305,24 @@ public class DiskOptimizedClipboard extends LinearClipboard { private void init() throws IOException { if (this.fileChannel == null) { this.fileChannel = braf.getChannel(); - try { - FileLock lock = this.fileChannel.lock(); - LOCK_HOLDER_CACHE.put(file.getName(), new LockHolder(lock)); - } catch (OverlappingFileLockException e) { - LockHolder existing = LOCK_HOLDER_CACHE.get(file.getName()); - if (existing != null) { - long ms = System.currentTimeMillis() - existing.lockHeldSince; - LOGGER.error( - "Cannot lock clipboard file {} acquired by thread {}, {}ms ago", - file.getName(), - existing.thread, - ms - ); + if (Settings.settings().CLIPBOARD.LOCK_CLIPBOARD_FILE) { + try { + FileLock lock = this.fileChannel.lock(); + LOCK_HOLDER_CACHE.put(file.getName(), new LockHolder(lock)); + } catch (OverlappingFileLockException e) { + LockHolder existing = LOCK_HOLDER_CACHE.get(file.getName()); + if (existing != null) { + long ms = System.currentTimeMillis() - existing.lockHeldSince; + LOGGER.error( + "Cannot lock clipboard file {} acquired by thread {}, {}ms ago", + file.getName(), + existing.thread, + ms + ); + } + // Rethrow to prevent clipboard access + throw e; } - // Rethrow to prevent clipboard access - throw e; } this.byteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, braf.length()); } From 2f92626433945cb8c810f4fcacc5bb20311eaa05 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 18 Dec 2023 15:33:02 +0000 Subject: [PATCH 031/114] feat: add switch to allow removal of entities on paste (#2525) - closes #2395 --- .../worldedit/command/ClipboardCommands.java | 56 +++++++++++++------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java index 8ccd4f44e..177366651 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ClipboardCommands.java @@ -47,6 +47,7 @@ import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.command.util.annotation.Preload; +import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; @@ -70,6 +71,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.transform.AffineTransform; import com.sk89q.worldedit.math.transform.Transform; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.NullRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.RegionIntersection; @@ -453,7 +455,9 @@ public class ClipboardCommands { @Switch(name = 'e', desc = "Paste entities if available") boolean pasteEntities, @Switch(name = 'b', desc = "Paste biomes if available") - boolean pasteBiomes + boolean pasteBiomes, + @Switch(name = 'x', desc = "Remove existing entities in the affected region") + boolean removeEntities ) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); final Clipboard clipboard = holder.getClipboard(); @@ -466,17 +470,22 @@ public class ClipboardCommands { } Region region = clipboard.getRegion().clone(); - if (selectPasted || onlySelect) { + if (selectPasted || onlySelect || removeEntities) { BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()); BlockVector3 realTo = to.add(holder.getTransform().apply(clipboardOffset.toVector3()).toBlockPoint()); BlockVector3 max = realTo.add(holder .getTransform() .apply(region.getMaximumPoint().subtract(region.getMinimumPoint()).toVector3()) .toBlockPoint()); - RegionSelector selector = new CuboidRegionSelector(world, realTo, max); - session.setRegionSelector(world, selector); - selector.learnChanges(); - selector.explainRegionAdjust(actor, session); + if (removeEntities) { + editSession.getEntities(new CuboidRegion(realTo, max)).forEach(Entity::remove); + } + if (selectPasted || onlySelect) { + RegionSelector selector = new CuboidRegionSelector(world, realTo, max); + session.setRegionSelector(world, selector); + selector.learnChanges(); + selector.explainRegionAdjust(actor, session); + } } if (onlySelect) { actor.print(Caption.of("worldedit.paste.selected")); @@ -513,14 +522,19 @@ public class ClipboardCommands { boolean pasteBiomes, @ArgFlag(name = 'm', desc = "Only paste blocks matching this mask") @ClipboardMask - Mask sourceMask + Mask sourceMask, + //FAWE start - entity removal + @Switch(name = 'x', desc = "Remove existing entities in the affected region") + boolean removeEntities + //FAWE end + ) throws WorldEditException { ClipboardHolder holder = session.getClipboard(); //FAWE start - use place if (holder.getTransform().isIdentity() && sourceMask == null) { place(actor, world, session, editSession, ignoreAirBlocks, atOrigin, selectPasted, onlySelect, - pasteEntities, pasteBiomes + pasteEntities, pasteBiomes, removeEntities ); return; } @@ -547,21 +561,29 @@ public class ClipboardCommands { messages.addAll(Lists.newArrayList(operation.getStatusMessages())); } - if (selectPasted || onlySelect) { + if (selectPasted || onlySelect || removeEntities) { BlockVector3 clipboardOffset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin()); Vector3 realTo = to.toVector3().add(holder.getTransform().apply(clipboardOffset.toVector3())); Vector3 max = realTo.add(holder .getTransform() .apply(region.getMaximumPoint().subtract(region.getMinimumPoint()).toVector3())); - final CuboidRegionSelector selector; - if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) { - selector = new ExtendingCuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint()); - } else { - selector = new CuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint()); + + // FAWE start - entity remova;l + if (removeEntities) { + editSession.getEntities(new CuboidRegion(realTo.toBlockPoint(), max.toBlockPoint())).forEach(Entity::remove); + } + if (selectPasted || onlySelect) { + //FAWE end + final CuboidRegionSelector selector; + if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) { + selector = new ExtendingCuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint()); + } else { + selector = new CuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint()); + } + session.setRegionSelector(world, selector); + selector.learnChanges(); + selector.explainRegionAdjust(actor, session); } - session.setRegionSelector(world, selector); - selector.learnChanges(); - selector.explainRegionAdjust(actor, session); } if (onlySelect) { From 28ab5662fdb72f1bf6eb2abbd2127ee24b4e9635 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 21 Dec 2023 13:11:44 +0000 Subject: [PATCH 032/114] fix: correct the delegated methods in AbstractDelegateExtent (#2365) - Not all methods should be overridden: we need to keep delegating-to-setblock methods in parent classes --- .../extent/AbstractDelegateExtent.java | 108 +++++++++++++++--- 1 file changed, 95 insertions(+), 13 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index 9dfd373ea..768af09c1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -25,27 +25,34 @@ import com.fastasyncworldedit.core.extent.HistoryExtent; import com.fastasyncworldedit.core.extent.NullExtent; import com.fastasyncworldedit.core.history.changeset.AbstractChangeSet; import com.fastasyncworldedit.core.internal.exception.FaweException; +import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IBatchProcessor; import com.fastasyncworldedit.core.util.ExtentTraverser; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.buffer.ForgetfulExtentBuffer; +import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.function.operation.OperationQueue; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.Location; 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; +import com.sk89q.worldedit.world.block.BlockType; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.util.List; +import java.util.Set; import java.util.UUID; import static com.google.common.base.Preconditions.checkNotNull; @@ -168,6 +175,7 @@ public class AbstractDelegateExtent implements Extent { } } + //FAWE start @Override public boolean cancel() { ExtentTraverser traverser = new ExtentTraverser<>(this); @@ -188,7 +196,6 @@ public class AbstractDelegateExtent implements Extent { return true; } - //FAWE start @Override public void removeEntity(int x, int y, int z, UUID uuid) { extent.removeEntity(x, y, z, uuid); @@ -225,11 +232,72 @@ public class AbstractDelegateExtent implements Extent { } } + @Override + public boolean isWorld() { + return extent.isWorld(); + } + + @Override + public List> getBlockDistribution(final Region region) { + return extent.getBlockDistribution(region); + } + + @Override + public List> getBlockDistributionWithData(final Region region) { + return extent.getBlockDistributionWithData(region); + } + @Override public int getMaxY() { return extent.getMaxY(); } + @Override + public int countBlocks(final Region region, final Set searchBlocks) { + return extent.countBlocks(region, searchBlocks); + } + + @Override + public int countBlocks(final Region region, final Mask searchMask) { + return extent.countBlocks(region, searchMask); + } + + @Override + public > int setBlocks(final Region region, final B block) throws MaxChangedBlocksException { + return extent.setBlocks(region, block); + } + + @Override + public int setBlocks(final Region region, final Pattern pattern) throws MaxChangedBlocksException { + return extent.setBlocks(region, pattern); + } + + @Override + public > int replaceBlocks( + final Region region, + final Set filter, + final B replacement + ) + throws MaxChangedBlocksException { + return extent.replaceBlocks(region, filter, replacement); + } + + @Override + public int replaceBlocks(final Region region, final Set filter, final Pattern pattern) throws + MaxChangedBlocksException { + return extent.replaceBlocks(region, filter, pattern); + } + + @Override + public int replaceBlocks(final Region region, final Mask mask, final Pattern pattern) throws MaxChangedBlocksException { + return extent.replaceBlocks(region, mask, pattern); + } + + @Override + public int setBlocks(final Set vset, final Pattern pattern) { + return extent.setBlocks(vset, pattern); + } + @Override public int getMinY() { return extent.getMinY(); @@ -295,23 +363,29 @@ public class AbstractDelegateExtent implements Extent { return this; } + @Override + public T apply(final Region region, final T filter, final boolean full) { + return extent.apply(region, filter, full); + } + //FAWE end + protected Operation commitBefore() { return null; } + @Override + public BiomeType getBiome(BlockVector3 position) { + //FAWE start - switch top x,y,z + return extent.getBiomeType(position.getX(), position.getY(), position.getZ()); + //FAWE end + } + + //FAWE start @Override public BiomeType getBiomeType(int x, int y, int z) { return extent.getBiomeType(x, y, z); } - @Override - public BiomeType getBiome(BlockVector3 position) { - return extent.getBiome(position); - } - /* - History - */ - @Override public int getEmittedLight(int x, int y, int z) { return extent.getEmittedLight(x, y, z); @@ -341,13 +415,17 @@ public class AbstractDelegateExtent implements Extent { new ExtentTraverser<>(this).setNext(new HistoryExtent(extent, changeSet)); } } + //FAWE end @Override public > boolean setBlock(BlockVector3 position, T block) throws WorldEditException { + //FAWE start - switch to x,y,z return extent.setBlock(position.getX(), position.getY(), position.getZ(), block); + //FAWE end } + //FAWE start @Override public > boolean setBlock( int x, int y, @@ -360,6 +438,7 @@ public class AbstractDelegateExtent implements Extent { public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException { return setBlock(x, y, z, getBlock(x, y, z).toBaseBlock(tile)); } + //FAWE end @Override public boolean fullySupports3DBiomes() { @@ -367,13 +446,16 @@ public class AbstractDelegateExtent implements Extent { } @Override - public boolean setBiome(int x, int y, int z, BiomeType biome) { - return extent.setBiome(x, y, z, biome); + public boolean setBiome(BlockVector3 position, BiomeType biome) { + //FAWE start - switch to x,y,z + return extent.setBiome(position.getX(), position.getY(), position.getZ(), biome); + //FAWE end } + //FAWE start @Override - public boolean setBiome(BlockVector3 position, BiomeType biome) { - return extent.setBiome(position.getX(), position.getY(), position.getZ(), biome); + public boolean setBiome(int x, int y, int z, BiomeType biome) { + return extent.setBiome(x, y, z, biome); } @Override From 04b4681ecad694bba369ee254d2ac7b10d25b3a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:56:50 +0100 Subject: [PATCH 033/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.0.11 (#2527) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 57aaf1fcf..2ed4c61dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.0.8" +towny = "0.100.0.11" plotsquared = "7.2.1" # Third party From af1230b98eb8dba3fefcdfdc0dca6dc9f171eb46 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:57:01 +0100 Subject: [PATCH 034/114] Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.5.11 (#2528) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 8b4b57369..fdb47a435 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { implementation(gradleApi()) implementation("org.ajoberstar.grgit:grgit-gradle:5.2.1") implementation("com.github.johnrengelman:shadow:8.1.1") - implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.10") + implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.11") } kotlin { From 0681de36c1c72f1a3fb8e045668265a034b40b4d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:57:08 +0100 Subject: [PATCH 035/114] Update dependency net.kyori:adventure-platform-bukkit to v4.3.2 (#2529) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ed4c61dd..79cddd351 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ bstats = "3.0.2" sparsebitset = "1.3" parallelgzip = "1.0.5" adventure = "4.14.0" -adventure-bukkit = "4.3.1" +adventure-bukkit = "4.3.2" checkerqual = "3.42.0" truezip = "6.8.4" auto-value = "1.10.4" From 4faf1ea6ec7d7c3f4705488c26d84b9c663b25fc Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 22 Dec 2023 18:11:37 +0100 Subject: [PATCH 036/114] Update paperweight --- worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index 34dda434a..48c83a606 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - the().paperDevBundle("1.20.4-R0.1-20231207.202833-1") + the().paperDevBundle("1.20.4-R0.1-20231221.211952-22") compileOnly(libs.paperlib) } From 129bf013c817c6f089fcb554de4947c50dab66e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 18:11:53 +0100 Subject: [PATCH 037/114] Update adventure to v4.15.0 (#2530) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 79cddd351..299806b45 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ plotsquared = "7.2.1" bstats = "3.0.2" sparsebitset = "1.3" parallelgzip = "1.0.5" -adventure = "4.14.0" +adventure = "4.15.0" adventure-bukkit = "4.3.2" checkerqual = "3.42.0" truezip = "6.8.4" From 2c0fb224fafc92b2251be048a851823244977070 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Dec 2023 18:12:06 +0100 Subject: [PATCH 038/114] Update dependency net.kyori:adventure-nbt to v4.15.0 (#2531) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- worldedit-bukkit/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 272d3c84f..c66c394fb 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -183,7 +183,7 @@ tasks.named("shadowJar") { include(dependency("org.lz4:lz4-java:1.8.0")) } relocate("net.kyori", "com.fastasyncworldedit.core.adventure") { - include(dependency("net.kyori:adventure-nbt:4.14.0")) + include(dependency("net.kyori:adventure-nbt:4.15.0")) } relocate("com.zaxxer", "com.fastasyncworldedit.core.math") { include(dependency("com.zaxxer:SparseBitSet:1.3")) From e4a214ec9bcde2fc4af912c17d23b98ebe3e6e14 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 22 Dec 2023 18:13:54 +0100 Subject: [PATCH 039/114] Release 2.8.4 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8d8f75c3c..313044b85 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s-%s", rootVersion, buildNumber) +version = String.format("%s", rootVersion) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") From a4a11265ec732d6f290a7400e94b3c9924363a3c Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 22 Dec 2023 18:37:07 +0100 Subject: [PATCH 040/114] Back to snapshot for development --- build.gradle.kts | 4 ++-- .../com/fastasyncworldedit/core/queue/IBatchProcessor.java | 4 ++-- .../main/java/com/fastasyncworldedit/core/queue/IChunk.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 313044b85..ad62a28c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.8.4") +var rootVersion by extra("2.8.5") var snapshot by extra("SNAPSHOT") var revision: String by extra("") var buildNumber by extra("") @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s", rootVersion) +version = String.format("%s-%s", rootVersion, buildNumber) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java index b096b93fe..778f85ce4 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IBatchProcessor.java @@ -157,7 +157,7 @@ public interface IBatchProcessor { * @return false if chunk is empty of NBT * @deprecated tiles are stored in chunk-normalised coordinate space and thus cannot use the same function as entities */ - @Deprecated(forRemoval = true, since = "TODO") + @Deprecated(forRemoval = true, since = "2.8.4") default boolean trimNBT(IChunkSet set, Function contains) { Set ents = set.getEntities(); if (!ents.isEmpty()) { @@ -175,7 +175,7 @@ public interface IBatchProcessor { * Utility method to trim entity and blocks with a provided contains function. * * @return false if chunk is empty of NBT - * @since TODO + * @since 2.8.4 */ default boolean trimNBT( IChunkSet set, Function containsEntity, Function containsTile diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java index 5a03a9987..2586ea24c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IChunk.java @@ -38,7 +38,7 @@ public interface IChunk extends Trimable, IChunkGet, IChunkSet { * Return the minimum block coordinate of the chunk * * @return BlockVector3 of minimum block coordinate - * @since TODO + * @since 2.8.4 */ default BlockVector3 getChunkBlockCoord() { return BlockVector3.at(getX() << 4, getMinY(), getZ() << 4); From 7b4b384df6234df9b2c69e83ea8a7892ce1cce6d Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 6 Jan 2024 10:53:53 +0100 Subject: [PATCH 041/114] Update upstream --- worldedit-bukkit/build.gradle.kts | 22 +++-- .../clipboard/io/SchematicLoadException.java | 43 ++++++++++ .../clipboard/io/SpongeSchematicReader.java | 14 +++- .../validation/DataValidatorExtent.java | 32 +++++++- .../WorldEditExceptionConverter.java | 6 ++ .../math/transform/AffineTransform.java | 82 +++++++++++-------- .../src/main/resources/lang/strings.json | 1 + 7 files changed, 149 insertions(+), 51 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicLoadException.java diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index c66c394fb..5a9273371 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -141,20 +141,15 @@ tasks.named("jar") { addJarManifest(WorldEditKind.Plugin, includeClasspath = true) tasks.named("shadowJar") { - dependsOn(project.project(":worldedit-bukkit:adapters").subprojects.map { it.tasks.named("assemble") }) - from(Callable { - adapters.resolve() - .map { f -> - zipTree(f).matching { - exclude("META-INF/") - } - } - }) + configurations.add(adapters) archiveFileName.set("${rootProject.name}-Bukkit-${project.version}.${archiveExtension.getOrElse("jar")}") dependencies { // In tandem with not bundling log4j, we shouldn't relocate base package here. // relocate("org.apache.logging", "com.sk89q.worldedit.log4j") relocate("org.antlr.v4", "com.sk89q.worldedit.antlr4") + + exclude(dependency("$group:$name")) + include(dependency(":worldedit-core")) include(dependency(":worldedit-libs:bukkit")) // Purposefully not included, we assume (even though no API exposes it) that Log4J will be present at runtime @@ -192,6 +187,15 @@ tasks.named("shadowJar") { include(dependency("org.anarres:parallelgzip:1.0.5")) } } + + project.project(":worldedit-bukkit:adapters").subprojects.forEach { + dependencies { + include(dependency("${it.group}:${it.name}")) + } + minimize { + exclude(dependency("${it.group}:${it.name}")) + } + } } tasks.named("assemble").configure { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicLoadException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicLoadException.java new file mode 100644 index 000000000..f94216669 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SchematicLoadException.java @@ -0,0 +1,43 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.extent.clipboard.io; + +import com.sk89q.worldedit.util.formatting.text.Component; + +/** + * Raised when a known exception occurs during schematic load. + */ +public final class SchematicLoadException extends RuntimeException { + + private final Component message; + + public SchematicLoadException(Component message) { + this.message = message; + } + + /** + * Get the message of this exception as a rich text component. + * + * @return The rich message + */ + public Component getRichMessage() { + return this.message; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java index 464ef3e4f..83d8c3007 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.extent.clipboard.io; +import com.fastasyncworldedit.core.configuration.Caption; import com.google.common.collect.Maps; import com.sk89q.jnbt.AdventureNBTConverter; import com.sk89q.jnbt.ByteArrayTag; @@ -46,6 +47,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; @@ -137,9 +139,8 @@ public class SpongeSchematicReader extends NBTSchematicReader { BlockArrayClipboard clip = readVersion1(schematicTag); return readVersion2(clip, schematicTag); } - throw new IOException("This schematic version is not supported; Version: " + schematicVersion + ", DataVersion: " + dataVersion + "." + - "It's very likely your schematic has an invalid file extension, if the schematic has been created on a version lower than" + - "1.13.2, the extension MUST be `.schematic`, elsewise the schematic can't be read properly."); + throw new SchematicLoadException(Caption.of("worldedit.schematic.load.unsupported-version", + TextComponent.of(schematicVersion))); } @Override @@ -169,6 +170,13 @@ public class SpongeSchematicReader extends NBTSchematicReader { // Check Map schematic = schematicTag.getValue(); + // Be lenient about the specific nesting level of the Schematic tag + // Also allows checking the version from newer versions of the specification + if (schematic.size() == 1 && schematic.containsKey("Schematic")) { + schematicTag = requireTag(schematic, "Schematic", CompoundTag.class); + schematic = schematicTag.getValue(); + } + schematicVersion = requireTag(schematic, "Version", IntTag.class).getValue(); return schematicTag; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/validation/DataValidatorExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/validation/DataValidatorExtent.java index 1b806c7d8..bc498711b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/validation/DataValidatorExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/validation/DataValidatorExtent.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.extent.AbstractDelegateExtent; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; @@ -34,7 +35,8 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class DataValidatorExtent extends AbstractDelegateExtent { - private final World world; + private final int minY; + private final int maxY; /** * Create a new instance. @@ -43,16 +45,27 @@ public class DataValidatorExtent extends AbstractDelegateExtent { * @param world the world */ public DataValidatorExtent(Extent extent, World world) { + this(extent, checkNotNull(world).getMinY(), world.getMaxY()); + } + + /** + * Create a new instance. + * + * @param extent The extent + * @param minY The minimum Y height to allow (inclusive) + * @param maxY The maximum Y height to allow (inclusive) + */ + public DataValidatorExtent(Extent extent, int minY, int maxY) { super(extent); - checkNotNull(world); - this.world = world; + this.minY = minY; + this.maxY = maxY; } @Override public > boolean setBlock(BlockVector3 location, B block) throws WorldEditException { final int y = location.getBlockY(); final BlockType type = block.getBlockType(); - if (y < world.getMinY() || y > world.getMaxY()) { + if (y < minY || y > maxY) { return false; } @@ -64,4 +77,15 @@ public class DataValidatorExtent extends AbstractDelegateExtent { return super.setBlock(location, block); } + @Override + public boolean setBiome(BlockVector3 location, BiomeType biome) { + final int y = location.getBlockY(); + + if (y < minY || y > maxY) { + return false; + } + + return super.setBiome(location, biome); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java index 3f5bfafb5..a0c5eb328 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/WorldEditExceptionConverter.java @@ -36,6 +36,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.InsufficientArgumentsException; import com.sk89q.worldedit.command.tool.InvalidToolBindException; +import com.sk89q.worldedit.extent.clipboard.io.SchematicLoadException; import com.sk89q.worldedit.internal.expression.ExpressionException; import com.sk89q.worldedit.regions.RegionOperationException; import com.sk89q.worldedit.util.formatting.text.Component; @@ -187,6 +188,11 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper { throw newCommandException(Caption.of("worldedit.error.file-aborted"), e); } + @ExceptionMatch + public void convert(SchematicLoadException e) throws CommandException { + throw newCommandException(e.getRichMessage(), e); + } + @ExceptionMatch public void convert(WorldEditException e) throws CommandException { throw newCommandException(e.getRichMessage(), e); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java b/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java index c06e0968e..02ee75d44 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/math/transform/AffineTransform.java @@ -195,6 +195,33 @@ public class AffineTransform implements Transform, Serializable { // =================================================================== // general methods + /** + * Returns the affine transform created by applying first the affine + * transform given by the parameters, then this affine transform. + * + * @return the composition this * that + */ + public AffineTransform concatenate(double o00, double o01, double o02, double o03, + double o10, double o11, double o12, double o13, + double o20, double o21, double o22, double o23) { + double n00 = m00 * o00 + m01 * o10 + m02 * o20; + double n01 = m00 * o01 + m01 * o11 + m02 * o21; + double n02 = m00 * o02 + m01 * o12 + m02 * o22; + double n03 = m00 * o03 + m01 * o13 + m02 * o23 + m03; + double n10 = m10 * o00 + m11 * o10 + m12 * o20; + double n11 = m10 * o01 + m11 * o11 + m12 * o21; + double n12 = m10 * o02 + m11 * o12 + m12 * o22; + double n13 = m10 * o03 + m11 * o13 + m12 * o23 + m13; + double n20 = m20 * o00 + m21 * o10 + m22 * o20; + double n21 = m20 * o01 + m21 * o11 + m22 * o21; + double n22 = m20 * o02 + m21 * o12 + m22 * o22; + double n23 = m20 * o03 + m21 * o13 + m22 * o23 + m23; + return new AffineTransform( + n00, n01, n02, n03, + n10, n11, n12, n13, + n20, n21, n22, n23); + } + /** * Returns the affine transform created by applying first the affine * transform given by {@code that}, then this affine transform. @@ -203,22 +230,10 @@ public class AffineTransform implements Transform, Serializable { * @return the composition this * that */ public AffineTransform concatenate(AffineTransform that) { - double n00 = m00 * that.m00 + m01 * that.m10 + m02 * that.m20; - double n01 = m00 * that.m01 + m01 * that.m11 + m02 * that.m21; - double n02 = m00 * that.m02 + m01 * that.m12 + m02 * that.m22; - double n03 = m00 * that.m03 + m01 * that.m13 + m02 * that.m23 + m03; - double n10 = m10 * that.m00 + m11 * that.m10 + m12 * that.m20; - double n11 = m10 * that.m01 + m11 * that.m11 + m12 * that.m21; - double n12 = m10 * that.m02 + m11 * that.m12 + m12 * that.m22; - double n13 = m10 * that.m03 + m11 * that.m13 + m12 * that.m23 + m13; - double n20 = m20 * that.m00 + m21 * that.m10 + m22 * that.m20; - double n21 = m20 * that.m01 + m21 * that.m11 + m22 * that.m21; - double n22 = m20 * that.m02 + m21 * that.m12 + m22 * that.m22; - double n23 = m20 * that.m03 + m21 * that.m13 + m22 * that.m23 + m23; - return new AffineTransform( - n00, n01, n02, n03, - n10, n11, n12, n13, - n20, n21, n22, n23 + return concatenate( + that.m00, that.m01, that.m02, that.m03, + that.m10, that.m11, that.m12, that.m13, + that.m20, that.m21, that.m22, that.m23 ); } @@ -258,40 +273,37 @@ public class AffineTransform implements Transform, Serializable { } public AffineTransform translate(double x, double y, double z) { - return concatenate(new AffineTransform(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z)); + return concatenate(1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z); } public AffineTransform rotateX(double theta) { double cot = MathUtils.dCos(theta); double sit = MathUtils.dSin(theta); return concatenate( - new AffineTransform( - 1, 0, 0, 0, - 0, cot, -sit, 0, - 0, sit, cot, 0 - )); + 1, 0, 0, 0, + 0, cot, -sit, 0, + 0, sit, cot, 0 + ); } public AffineTransform rotateY(double theta) { double cot = MathUtils.dCos(theta); double sit = MathUtils.dSin(theta); return concatenate( - new AffineTransform( - cot, 0, sit, 0, - 0, 1, 0, 0, - -sit, 0, cot, 0 - )); + cot, 0, sit, 0, + 0, 1, 0, 0, + -sit, 0, cot, 0 + ); } public AffineTransform rotateZ(double theta) { double cot = MathUtils.dCos(theta); double sit = MathUtils.dSin(theta); return concatenate( - new AffineTransform( - cot, -sit, 0, 0, - sit, cot, 0, 0, - 0, 0, 1, 0 - )); + cot, -sit, 0, 0, + sit, cot, 0, 0, + 0, 0, 1, 0 + ); } public AffineTransform scale(double s) { @@ -299,7 +311,7 @@ public class AffineTransform implements Transform, Serializable { } public AffineTransform scale(double sx, double sy, double sz) { - return concatenate(new AffineTransform(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0)); + return concatenate(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0); } public AffineTransform scale(Vector3 vec) { @@ -352,9 +364,9 @@ public class AffineTransform implements Transform, Serializable { //FAWE start - check other identity if (other instanceof Identity || other.isIdentity()) { return this; - } else if (other instanceof AffineTransform) { + } else if (other instanceof AffineTransform otherTransform) { //FAWE end - return concatenate((AffineTransform) other); + return concatenate(otherTransform); } else { return new CombinedTransform(this, other); } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 3d1295ab0..208de9027 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -354,6 +354,7 @@ "worldedit.schematic.unknown-format": "Unknown schematic format: {0}.", "worldedit.schematic.load.does-not-exist": "Schematic {0} does not exist!", "worldedit.schematic.load.loading": "(Please wait... loading schematic.)", + "worldedit.schematic.load.unsupported-version": "This schematic is not supported. Version: {0}.", "worldedit.schematic.save.already-exists": "That schematic already exists. Use the -f flag to overwrite it.", "worldedit.schematic.save.failed-directory": "Could not create folder for schematics!", "worldedit.schematic.save.saving": "(Please wait... saving schematic.)", From b3905c2a6dc8a0a6071c96db8436a88702eeb794 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 7 Jan 2024 20:53:32 +0100 Subject: [PATCH 042/114] Update Paperweight Signed-off-by: Alexander Brandes --- worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index 48c83a606..88a2a0fcf 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ - the().paperDevBundle("1.20.4-R0.1-20231221.211952-22") + the().paperDevBundle("1.20.4-R0.1-20240106.182028-62") compileOnly(libs.paperlib) } From a7c5f66a4595b59fc823b8512f0494111b41251c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Jan 2024 00:32:45 +0100 Subject: [PATCH 043/114] Update plotsquared to v7.3.1 (#2536) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 299806b45..71a034d45 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" towny = "0.100.0.11" -plotsquared = "7.2.1" +plotsquared = "7.3.1" # Third party bstats = "3.0.2" From e81507113479fda17fc91df4f9d6304a430c3a1d Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 21 Jan 2024 12:42:24 +0100 Subject: [PATCH 044/114] fix: fix plot swap (#2360) - deprecate and note for internal use only Clipboard#create as it's funky - fixes #2076 --- .../FaweDelegateRegionManager.java | 27 ++++++++++--------- .../worldedit/extent/clipboard/Clipboard.java | 6 +++++ 2 files changed, 20 insertions(+), 13 deletions(-) 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 65f8863f8..c111ff630 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 @@ -16,6 +16,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.MaxChangedBlocksException; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.function.FlatRegionFunction; @@ -217,7 +218,6 @@ public class FaweDelegateRegionManager { ) { TaskManager.taskManager().async(() -> { synchronized (FaweDelegateRegionManager.class) { - //todo because of the following code this should probably be in the Bukkit module World pos1World = BukkitAdapter.adapt(getWorld(pos1.getWorldName())); World pos3World = BukkitAdapter.adapt(getWorld(swapPos.getWorldName())); EditSession sessionA = WorldEdit.getInstance().newEditSessionBuilder().world(pos1World) @@ -236,14 +236,16 @@ public class FaweDelegateRegionManager { CuboidRegion regionB = new CuboidRegion( pos3World, swapPos.getBlockVector3(), - swapPos.getBlockVector3().add(pos2.getBlockVector3()).subtract(pos1.getBlockVector3()) + swapPos.getBlockVector3().add(pos2.getBlockVector3().subtract(pos1.getBlockVector3())).withY(pos2.getY()) ); - Clipboard clipA = Clipboard.create(regionA, UUID.randomUUID()); - Clipboard clipB = Clipboard.create(regionB, UUID.randomUUID()); + Clipboard clipA = new BlockArrayClipboard(regionA, UUID.randomUUID()); + Clipboard clipB = new BlockArrayClipboard(regionB, UUID.randomUUID()); ForwardExtentCopy copyA = new ForwardExtentCopy(sessionA, regionA, clipA, clipA.getMinimumPoint()); ForwardExtentCopy copyB = new ForwardExtentCopy(sessionB, regionB, clipB, clipB.getMinimumPoint()); copyA.setCopyingBiomes(true); copyB.setCopyingBiomes(true); + copyA.setCopyingEntities(true); + copyB.setCopyingEntities(true); try { Operations.completeLegacy(copyA); Operations.completeLegacy(copyB); @@ -257,17 +259,16 @@ public class FaweDelegateRegionManager { sessionA.close(); sessionB.close(); } - FaweAPI.fixLighting(pos1World, new CuboidRegion(pos1.getBlockVector3(), pos2.getBlockVector3()), null, + FaweAPI.fixLighting( + pos1World, + regionA, + null, RelightMode.valueOf(com.fastasyncworldedit.core.configuration.Settings.settings().LIGHTING.MODE) ); - FaweAPI.fixLighting(pos1World, new CuboidRegion( - swapPos.getBlockVector3(), - BlockVector3.at( - swapPos.getX() + pos2.getX() - pos1.getX(), - 0, - swapPos.getZ() + pos2.getZ() - pos1.getZ() - ) - ), null, + FaweAPI.fixLighting( + pos1World, + regionB, + null, RelightMode.valueOf(com.fastasyncworldedit.core.configuration.Settings.settings().LIGHTING.MODE) ); if (whenDone != null) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java index 15d6365e7..76e866c33 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/Clipboard.java @@ -73,7 +73,10 @@ public interface Clipboard extends Extent, Iterable, Closeable, Fl /** * Creates a new {@link ReadOnlyClipboard}. + * + * @deprecated Internal use only. Use {@link BlockArrayClipboard#BlockArrayClipboard(Region)} */ + @Deprecated static Clipboard create(Region region) { checkNotNull(region); checkNotNull( @@ -95,7 +98,10 @@ public interface Clipboard extends Extent, Iterable, Closeable, Fl * - {@link DiskOptimizedClipboard} * - {@link CPUOptimizedClipboard} * - {@link MemoryOptimizedClipboard} + * + * @deprecated Internal use only. Use {@link BlockArrayClipboard#BlockArrayClipboard(Region, UUID)} */ + @Deprecated static Clipboard create(Region region, UUID uuid) { if (Settings.settings().CLIPBOARD.USE_DISK) { return new DiskOptimizedClipboard(region, uuid); From ea6bbecc32ccd25f2fe1b03decb695d4afe5fbcb Mon Sep 17 00:00:00 2001 From: Pierre Maurice Schwang Date: Mon, 22 Jan 2024 19:14:10 +0100 Subject: [PATCH 045/114] Track paperweight-userdev versions with renovate (#2548) * fix: editorconfig file selectors * chore/ci: add renovate checks for paperweight-userdev --- .editorconfig | 20 ++++---- .github/renovate.json | 50 +++++++++++++++---- .../adapters/adapter-1_17_1/build.gradle.kts | 1 + .../adapters/adapter-1_18_2/build.gradle.kts | 2 +- .../adapters/adapter-1_19_4/build.gradle.kts | 1 + .../adapters/adapter-1_20/build.gradle.kts | 2 +- .../adapters/adapter-1_20_2/build.gradle.kts | 2 +- .../adapters/adapter-1_20_4/build.gradle.kts | 2 +- 8 files changed, 56 insertions(+), 24 deletions(-) diff --git a/.editorconfig b/.editorconfig index e43c0e094..c60879fb3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -342,7 +342,7 @@ ij_editorconfig_space_before_colon = false ij_editorconfig_space_before_comma = false ij_editorconfig_spaces_around_assignment_operators = true -[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.pom, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul}] +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}] ij_xml_align_attributes = true ij_xml_align_text = false ij_xml_attribute_wrap = normal @@ -360,7 +360,7 @@ ij_xml_space_around_equals_in_attribute = false ij_xml_space_inside_empty_tag = false ij_xml_text_wrap = normal -[{*.ats, *.ts}] +[{*.ats,*.ts}] ij_continuation_indent_size = 4 ij_typescript_align_imports = false ij_typescript_align_multiline_array_initializer_expression = false @@ -528,7 +528,7 @@ ij_typescript_while_brace_force = never ij_typescript_while_on_new_line = false ij_typescript_wrap_comments = false -[{*.bash, *.sh, *.zsh}] +[{*.bash,*.sh,*.zsh}] indent_size = 2 tab_width = 2 ij_shell_binary_ops_start_line = false @@ -537,7 +537,7 @@ ij_shell_minify_program = false ij_shell_redirect_followed_by_space = false ij_shell_switch_cases_indented = false -[{*.cjs, *.js}] +[{*.cjs,*.js}] ij_continuation_indent_size = 4 ij_javascript_align_imports = false ij_javascript_align_multiline_array_initializer_expression = false @@ -702,10 +702,10 @@ ij_javascript_while_brace_force = never ij_javascript_while_on_new_line = false ij_javascript_wrap_comments = false -[{*.ft, *.vm, *.vsl}] +[{*.ft,*.vm,*.vsl}] ij_vtl_keep_indents_on_empty_lines = false -[{*.gant, *.gradle, *.groovy, *.gy}] +[{*.gant,*.gradle,*.groovy,*.gy}] ij_groovy_align_group_field_declarations = false ij_groovy_align_multiline_array_initializer_expression = false ij_groovy_align_multiline_assignment = false @@ -884,7 +884,7 @@ ij_groovy_while_brace_force = never ij_groovy_while_on_new_line = false ij_groovy_wrap_long_lines = false -[{*.gradle.kts, *.kt, *.kts, *.main.kts}] +[{*.gradle.kts,*.kt,*.kts,*.main.kts}] ij_kotlin_align_in_columns_case_branch = false ij_kotlin_align_multiline_binary_operation = false ij_kotlin_align_multiline_extends_list = false @@ -963,7 +963,7 @@ ij_kotlin_wrap_elvis_expressions = 1 ij_kotlin_wrap_expression_body_functions = 0 ij_kotlin_wrap_first_method_in_call_chain = false -[{*.har, *.jsb2, *.jsb3, *.json, .babelrc, .eslintrc, .stylelintrc, bowerrc, jest.config, mcmod.info}] +[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config,mcmod.info}] indent_size = 2 ij_json_keep_blank_lines_in_code = 0 ij_json_keep_indents_on_empty_lines = false @@ -976,7 +976,7 @@ ij_json_spaces_within_braces = false ij_json_spaces_within_brackets = false ij_json_wrap_long_lines = false -[{*.htm, *.html, *.sht, *.shtm, *.shtml}] +[{*.htm,*.html,*.sht,*.shtm,*.shtml}] ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 ij_html_align_attributes = true ij_html_align_text = false @@ -1004,7 +1004,7 @@ ij_html_space_inside_empty_tag = false ij_html_text_wrap = normal ij_html_uniform_ident = false -[{*.yaml, *.yml}] +[{*.yaml,*.yml}] indent_size = 2 ij_yaml_keep_indents_on_empty_lines = false ij_yaml_keep_line_breaks = true diff --git a/.github/renovate.json b/.github/renovate.json index d0e59d791..740d991fb 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,18 +1,18 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base", + "$schema" : "https://docs.renovatebot.com/renovate-schema.json", + "extends" : [ + "config:recommended", ":semanticCommitsDisabled" ], - "automerge": true, - "ignoreDeps": [ - "guava", + "automerge" : true, + "ignoreDeps" : [ + "guava", "com.google.guava:guava", "rhino-runtime", "org.antlr", "antlr4-runtime", "fastutil", - "it.unimi.dsi:fastutil", + "it.unimi.dsi:fastutil", "auto-value-annotations", "auto-value", "com.google.code.gson:gson", @@ -29,7 +29,37 @@ "org.spongepowered:spongeapi", "org.yaml:snakeyaml" ], - "labels": ["Renovate"], - "rebaseWhen": "conflicted", - "schedule": ["on the first day of the month"] + "labels" : [ + "Renovate" + ], + "rebaseWhen" : "conflicted", + "schedule" : [ + "on the first day of the month" + ], + "customManagers" : [ + { + "customType" : "regex", + "datasourceTemplate" : "custom.paperweight-userdev", + "fileMatch" : "^worldedit-bukkit\\/adapters\\/adapter-\\d+_\\d+(_\\d+)?\\/build\\.gradle\\.kts$", + "matchStrings" : [ + "url=(?.*)\\s", + "paperDevBundle\\(\"(?.*?)\"\\)\\s" + ], + "matchStringsStrategy": "combination", + "depNameTemplate" : "paperweight-userdev", + "extractVersionTemplate" : "(?\\d+\\.\\d+\\.?\\d*-R0\\.1-\\d+\\.\\d+-\\d+)" + } + ], + "customDatasources" : { + "paperweight-userdev": { + "defaultRegistryUrlTemplate": "", + "format": "html" + } + }, + "packageRules" : [ + { + "matchDatasources" : ["custom.paperweight-userdev"], + "versioning": "regex:^(?\\d+)\\.(?\\d+)\\.(?\\d+)?-R0\\.1-\\d+\\d+\\.\\d+-(?\\d+)$" + } + ] } diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts index 24c121c3f..9eb68ea36 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts @@ -21,6 +21,7 @@ configurations.all { dependencies { + // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.17.1-R0.1-SNAPSHOT the().paperDevBundle("1.17.1-R0.1-20220414.034903-210") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts index f7f40ce66..3713af207 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts @@ -11,7 +11,7 @@ repositories { } dependencies { - // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ + // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.18.2-R0.1-SNAPSHOT the().paperDevBundle("1.18.2-R0.1-20220920.010157-167") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts index df27ae5d6..e53d2497e 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts @@ -11,6 +11,7 @@ repositories { } dependencies { + // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.19.4-R0.1-SNAPSHOT the().paperDevBundle("1.19.4-R0.1-20230608.201059-104") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts index 227eee362..51438592a 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20/build.gradle.kts @@ -11,7 +11,7 @@ repositories { } dependencies { - // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ + // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.1-R0.1-SNAPSHOT the().paperDevBundle("1.20.1-R0.1-20230921.165944-178") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts index 3b08adfe8..995446059 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_2/build.gradle.kts @@ -11,7 +11,7 @@ repositories { } dependencies { - // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ + // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.2-R0.1-SNAPSHOT the().paperDevBundle("1.20.2-R0.1-20231203.034718-121") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index 88a2a0fcf..02f5a3c53 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -11,7 +11,7 @@ repositories { } dependencies { - // https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ + // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT the().paperDevBundle("1.20.4-R0.1-20240106.182028-62") compileOnly(libs.paperlib) } From a99dd2d387322cc57d1821376a8d4c96ab6f63c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:01:11 +0100 Subject: [PATCH 046/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.5 (#2550) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 71a034d45..86527e5b8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "16.18.1" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.0.11" +towny = "0.100.1.5" plotsquared = "7.3.1" # Third party From 325309e39673e06de12fa5d96ec4d665bf372d43 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:01:24 +0100 Subject: [PATCH 047/114] Update dependency com.github.TechFortress:GriefPrevention to v17 (#2552) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 86527e5b8..c8f7b7c9e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ snakeyaml = "2.0" dummypermscompat = "1.10" worldguard-bukkit = "7.0.9" mapmanager = "1.8.0-SNAPSHOT" -griefprevention = "16.18.1" +griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" towny = "0.100.1.5" From b919633a87d47eecab154e4980b86d00f4fc1294 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:01:30 +0100 Subject: [PATCH 048/114] Update dependency org.mockito:mockito-core to v5.9.0 (#2551) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- worldedit-sponge/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c8f7b7c9e..9d255510e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,7 +46,7 @@ text = "3.0.4" piston = "0.5.7" # Tests -mockito = "5.8.0" +mockito = "5.9.0" # Gradle plugins pluginyml = "0.6.0" diff --git a/worldedit-sponge/build.gradle.kts b/worldedit-sponge/build.gradle.kts index 8d3ba1539..272ea46ec 100644 --- a/worldedit-sponge/build.gradle.kts +++ b/worldedit-sponge/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { }) api("org.apache.logging.log4j:log4j-api") api("org.bstats:bstats-sponge:1.7") - testImplementation("org.mockito:mockito-core:5.8.0") + testImplementation("org.mockito:mockito-core:5.9.0") } <<<<<<< HEAD From a31f2f389035f17f6eec388d2bfa65eb6e85b5d2 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Thu, 25 Jan 2024 09:31:45 +0100 Subject: [PATCH 049/114] History: Read change positions at once (#2542) --- .../history/change/BlockPositionChange.java | 16 +++++ .../history/change/MutableBlockChange.java | 10 +-- .../change/MutableFullBlockChange.java | 9 +-- .../changeset/FaweStreamChangeSet.java | 67 +++++++------------ 4 files changed, 46 insertions(+), 56 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/history/change/BlockPositionChange.java 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 000000000..e9c233269 --- /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 f4797581f..473940507 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 @@ package com.fastasyncworldedit.core.history.change; 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 2b99cb8a9..b23903f45 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.WorldEditException; 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 71232a31c..f20af641b 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.MutableBlockChange; 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.regions.Region; 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 @@ import java.util.NoSuchElementException; /** * 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 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet { } } - 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 abstract class FaweStreamChangeSet extends AbstractChangeSet { } 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 abstract class FaweStreamChangeSet extends AbstractChangeSet { 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 abstract class FaweStreamChangeSet extends AbstractChangeSet { } @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 abstract class FaweStreamChangeSet extends AbstractChangeSet { 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 abstract class FaweStreamChangeSet extends AbstractChangeSet { 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 abstract class FaweStreamChangeSet extends AbstractChangeSet { 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) { From 763a497cdcf63bc73983529835f21497d111d259 Mon Sep 17 00:00:00 2001 From: Zeranny Date: Sun, 28 Jan 2024 12:10:48 +0000 Subject: [PATCH 050/114] Fix out of bounds error for erode and pull brushes (#2554) - Fixes #2380 --- .../core/command/tool/brush/ErodeBrush.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ErodeBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ErodeBrush.java index 13490dc2f..21879751c 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ErodeBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/ErodeBrush.java @@ -67,11 +67,11 @@ public class ErodeBrush implements Brush { final int by = target.getBlockY(); final int bz = target.getBlockZ(); - for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) { + for (int x = -brushSize, relx = 0; x <= brushSize && relx < buffer1.getWidth(); x++, relx++) { int x0 = x + bx; - for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) { + for (int y = -brushSize, rely = 0; y <= brushSize && rely < buffer1.getHeight(); y++, rely++) { int y0 = y + by; - for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) { + for (int z = -brushSize, relz = 0; z <= brushSize && relz < buffer1.getLength(); z++, relz++) { int z0 = z + bz; BlockState state = es.getBlock(x0, y0, z0); buffer1.setBlock(relx, rely, relz, state); @@ -115,11 +115,11 @@ public class ErodeBrush implements Brush { Clipboard current, Clipboard target ) { int[] frequency = null; - for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) { + for (int x = -brushSize, relx = 0; x <= brushSize && relx < target.getWidth(); x++, relx++) { int x2 = x * x; - for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) { + for (int z = -brushSize, relz = 0; z <= brushSize && relz < target.getLength(); z++, relz++) { int x2y2 = x2 + z * z; - for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) { + for (int y = -brushSize, rely = 0; y <= brushSize && rely < target.getHeight(); y++, rely++) { int cube = x2y2 + y * y; target.setBlock(relx, rely, relz, current.getBlock(relx, rely, relz)); if (cube >= brushSizeSquared) { @@ -166,11 +166,11 @@ public class ErodeBrush implements Brush { Clipboard current, Clipboard target ) { int[] frequency = null; - for (int x = -brushSize, relx = 0; x <= brushSize; x++, relx++) { + for (int x = -brushSize, relx = 0; x <= brushSize && relx < target.getWidth(); x++, relx++) { int x2 = x * x; - for (int z = -brushSize, relz = 0; z <= brushSize; z++, relz++) { + for (int z = -brushSize, relz = 0; z <= brushSize && relz < target.getLength(); z++, relz++) { int x2y2 = x2 + z * z; - for (int y = -brushSize, rely = 0; y <= brushSize; y++, rely++) { + for (int y = -brushSize, rely = 0; y <= brushSize && rely < target.getHeight(); y++, rely++) { int cube = x2y2 + y * y; target.setBlock(relx, rely, relz, current.getBlock(relx, rely, relz)); if (cube >= brushSizeSquared) { From 9ffe76dce3fe5bb46fe02897b1fb3721a6eab3eb Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sun, 28 Jan 2024 13:32:25 +0100 Subject: [PATCH 051/114] fix: add missing methods top TestOfflinePermissible --- .../java/com/sk89q/wepif/TestOfflinePermissible.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java b/worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java index 052c772d5..507da7e01 100644 --- a/worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java +++ b/worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java @@ -245,6 +245,11 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public @Nullable Location getRespawnLocation() { + return null; + } + @Override public void incrementStatistic(@Nonnull Statistic statistic) throws IllegalArgumentException { @@ -365,4 +370,9 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible { return null; } + @Override + public @Nullable Location getLocation() { + return null; + } + } From 033b8e35d235205c6f72f09156b0b4a3d393a4da Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 2 Feb 2024 18:06:57 +0100 Subject: [PATCH 052/114] fix: always set createCopy status on chunk get as chunks are cached (#2567) - fixes #2539 --- .../adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java | 1 + .../bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java | 1 + .../bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java | 1 + .../bukkit/adapter/impl/fawe/v1_20_R1/PaperweightGetBlocks.java | 1 + .../bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java | 1 + .../bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java | 1 + .../core/queue/implementation/chunk/ChunkHolder.java | 2 +- 7 files changed, 7 insertions(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java index a93437c1a..214a39bc6 100644 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java @@ -148,6 +148,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); } this.createCopy = createCopy; + // Increment regardless of whether copy will be created or not to return null from getCopy() return ++this.copyKey; } diff --git a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java index 81580910c..7291c0f69 100644 --- a/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_18_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_18_R2/PaperweightGetBlocks.java @@ -153,6 +153,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); } this.createCopy = createCopy; + // Increment regardless of whether copy will be created or not to return null from getCopy() return ++this.copyKey; } diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java index 60695c1e8..63bdb5997 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightGetBlocks.java @@ -156,6 +156,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); } this.createCopy = createCopy; + // Increment regardless of whether copy will be created or not to return null from getCopy() return ++this.copyKey; } 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 67bcd6902..5f74d1073 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 @@ -156,6 +156,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); } this.createCopy = createCopy; + // Increment regardless of whether copy will be created or not to return null from getCopy() return ++this.copyKey; } 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 d51d31500..1531358fb 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 @@ -135,6 +135,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); } this.createCopy = createCopy; + // Increment regardless of whether copy will be created or not to return null from getCopy() return ++this.copyKey; } 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 8139adc87..42333f9e5 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 @@ -135,6 +135,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); } this.createCopy = createCopy; + // Increment regardless of whether copy will be created or not to return null from getCopy() return ++this.copyKey; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index e4a18ef69..9458aa1b0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -1031,10 +1031,10 @@ public class ChunkHolder> implements IQueueChunk { try { get.lockCall(); boolean postProcess = !(getExtent().getPostProcessor() instanceof EmptyBatchProcessor); + final int copyKey = get.setCreateCopy(postProcess); final IChunkSet iChunkSet = getExtent().processSet(this, get, set); Runnable finalizer; if (postProcess) { - int copyKey = get.setCreateCopy(true); finalizer = () -> { getExtent().postProcess(this, get.getCopy(copyKey), iChunkSet); finalize.run(); From a502287906da63cc689d53eeba855ef7df9efea9 Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 2 Feb 2024 18:07:13 +0100 Subject: [PATCH 053/114] refactor: minor adjustments to error handling (ignoring) and blocking queue (#2566) --- .../fastasyncworldedit/core/FaweCache.java | 36 ++++++++++++++++--- .../queue/implementation/QueueHandler.java | 3 +- .../SingleThreadQueueExtent.java | 1 + .../implementation/chunk/ChunkHolder.java | 3 +- 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index 0655e4214..f5cc88284 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -612,12 +612,38 @@ public enum FaweCache implements Trimable { /* Thread stuff */ + + /** + * Create a new blocking executor with default name and FaweCache logger + * + * @return new blocking executor + */ public ThreadPoolExecutor newBlockingExecutor() { + return newBlockingExecutor("FAWE Blocking Executor - %d"); + } + + /** + * Create a new blocking executor with specified name and FaweCache logger + * + * @return new blocking executor + * @since TODO + */ + public ThreadPoolExecutor newBlockingExecutor(String name) { + return newBlockingExecutor(name, LOGGER); + } + + /** + * Create a new blocking executor with specified name and logger + * + * @return new blocking executor + * @since TODO + */ + public ThreadPoolExecutor newBlockingExecutor(String name, Logger logger) { int nThreads = Settings.settings().QUEUE.PARALLEL_THREADS; ArrayBlockingQueue queue = new ArrayBlockingQueue<>(nThreads, true); return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, queue, - new ThreadFactoryBuilder().setNameFormat("FAWE Blocking Executor - %d").build(), + new ThreadFactoryBuilder().setNameFormat(name).build(), new ThreadPoolExecutor.CallerRunsPolicy() ) { @@ -652,10 +678,10 @@ public enum FaweCache implements Trimable { int hash = throwable.getMessage() != null ? throwable.getMessage().hashCode() : 0; if (hash != lastException) { lastException = hash; - LOGGER.catching(throwable); + logger.catching(throwable); count = 0; } else if (count < Settings.settings().QUEUE.PARALLEL_THREADS) { - LOGGER.warn(throwable.getMessage()); + logger.warn(throwable.getMessage()); count++; } } @@ -665,10 +691,10 @@ public enum FaweCache implements Trimable { private void handleFaweException(FaweException e) { FaweException.Type type = e.getType(); if (e.getType() == FaweException.Type.OTHER) { - LOGGER.catching(e); + logger.catching(e); } else if (!faweExceptionReasonsUsed[type.ordinal()]) { faweExceptionReasonsUsed[type.ordinal()] = true; - LOGGER.warn("FaweException: " + e.getMessage()); + logger.warn("FaweException: " + e.getMessage()); } } }; 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 014b94fce..956c33fb2 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 @@ -68,7 +68,8 @@ public abstract class QueueHandler implements Trimable, Runnable { * Main "work-horse" queue for FAWE. Handles chunk submission (and chunk submission alone). Blocking in order to forcibly * prevent overworking/over-submission of chunk process tasks. */ - private final ThreadPoolExecutor blockingExecutor = FaweCache.INSTANCE.newBlockingExecutor(); + private final ThreadPoolExecutor blockingExecutor = FaweCache.INSTANCE.newBlockingExecutor( + "FAWE QueueHandler Blocking Executor - %d"); /** * Queue for tasks to be completed on the main thread. These take priority of tasks submitted to syncWhenFree queue */ 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 34dd5191e..132229d1d 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 @@ -158,6 +158,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen this.setProcessor(EmptyBatchProcessor.getInstance()); this.setPostProcessor(EmptyBatchProcessor.getInstance()); this.world = null; + this.faweExceptionReasonsUsed = new boolean[FaweException.Type.values().length]; } /** diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index 9458aa1b0..6103a1649 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -12,6 +12,7 @@ import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.fastasyncworldedit.core.queue.Pool; +import com.fastasyncworldedit.core.util.MemUtil; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; @@ -959,7 +960,7 @@ public class ChunkHolder> implements IQueueChunk { public final IChunkGet getOrCreateGet() { if (chunkExisting == null) { chunkExisting = newWrappedGet(); - chunkExisting.trim(false); + chunkExisting.trim(MemUtil.isMemoryLimited()); } return chunkExisting; } From d1e1d5105e04dcce486aed1ae18e6866ae0881e3 Mon Sep 17 00:00:00 2001 From: Jordan Date: Mon, 5 Feb 2024 22:44:45 +0100 Subject: [PATCH 054/114] fix: invalidate cached allowed regions if WG region deleted (#2572) --- .../bukkit/regions/WorldGuardFeature.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/WorldGuardFeature.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/WorldGuardFeature.java index ade2d8258..1043d9a3e 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/WorldGuardFeature.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/WorldGuardFeature.java @@ -163,13 +163,22 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener { final Location location = player.getLocation(); final Set regions = this.getRegions(localplayer, location, isWhitelist); if (!regions.isEmpty()) { + RegionManager manager = WorldGuard + .getInstance() + .getPlatform() + .getRegionContainer() + .get(BukkitAdapter.adapt(location.getWorld())); + if (manager == null) { + return null; + } Set result = new HashSet<>(); for (ProtectedRegion myRegion : regions) { if (myRegion.getId().equals("__global__")) { return new FaweMask(RegionWrapper.GLOBAL()) { @Override public boolean isValid(com.sk89q.worldedit.entity.Player player, MaskType type) { - return isAllowed(worldguard.wrapPlayer(BukkitAdapter.adapt(player)), myRegion); + return manager.hasRegion(myRegion.getId()) + && isAllowed(worldguard.wrapPlayer(BukkitAdapter.adapt(player)), myRegion); } }; } else { @@ -185,7 +194,7 @@ public class WorldGuardFeature extends BukkitMaskManager implements Listener { public boolean isValid(com.sk89q.worldedit.entity.Player player, MaskType type) { final LocalPlayer localplayer = worldguard.wrapPlayer(BukkitAdapter.adapt(player)); for (ProtectedRegion myRegion : regions) { - if (!isAllowed(localplayer, myRegion)) { + if (!manager.hasRegion(myRegion.getId()) || !isAllowed(localplayer, myRegion)) { return false; } } From 36ce7afcfffa26eeaf88026a372b952f82ab68b0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:45:15 +0100 Subject: [PATCH 055/114] Update gradle/wrapper-validation-action action to v2 (#2565) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-pr.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/upload-release-assets.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 394cb9f56..de239164e 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -11,7 +11,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Setup Java uses: actions/setup-java@v4 with: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 571658fb4..675f0cc1c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Setup Java uses: actions/setup-java@v4 with: diff --git a/.github/workflows/upload-release-assets.yml b/.github/workflows/upload-release-assets.yml index 7315f1d6c..a621e9d13 100644 --- a/.github/workflows/upload-release-assets.yml +++ b/.github/workflows/upload-release-assets.yml @@ -9,7 +9,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Setup Java uses: actions/setup-java@v4 with: From 9beda23f4a66e2680f750c7e27b8e90b573ce01a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:45:23 +0100 Subject: [PATCH 056/114] Update dependency org.mockito:mockito-core to v5.10.0 (#2564) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- worldedit-sponge/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9d255510e..5e715d3e3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,7 +46,7 @@ text = "3.0.4" piston = "0.5.7" # Tests -mockito = "5.9.0" +mockito = "5.10.0" # Gradle plugins pluginyml = "0.6.0" diff --git a/worldedit-sponge/build.gradle.kts b/worldedit-sponge/build.gradle.kts index 272ea46ec..c37fe591e 100644 --- a/worldedit-sponge/build.gradle.kts +++ b/worldedit-sponge/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { }) api("org.apache.logging.log4j:log4j-api") api("org.bstats:bstats-sponge:1.7") - testImplementation("org.mockito:mockito-core:5.9.0") + testImplementation("org.mockito:mockito-core:5.10.0") } <<<<<<< HEAD From f6c82eaa8234a554b541e6458fa8de4bc8772553 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:45:32 +0100 Subject: [PATCH 057/114] Update plotsquared to v7.3.2 (#2563) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5e715d3e3..bd29cd455 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" towny = "0.100.1.5" -plotsquared = "7.3.1" +plotsquared = "7.3.2" # Third party bstats = "3.0.2" From 40f0c7ca9f8fe497ebb81834469f28f02c6c84ee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:12:08 +0100 Subject: [PATCH 058/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.10 (#2573) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bd29cd455..5d9cede7c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.1.5" +towny = "0.100.1.10" plotsquared = "7.3.2" # Third party From 000b41331637c0a92b15d7234b3e93c9d231533b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:12:17 +0100 Subject: [PATCH 059/114] Update dependency gradle to v8.6 (#2577) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e0930..a80b22ce5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 6689b85be..7101f8e46 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From a52924e03f8a45aba8a28817db214c2f3316c486 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:14:06 +0100 Subject: [PATCH 060/114] Update plugin xyz.jpenilla.run-paper to v2.2.3 (#2576) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index ad62a28c1..20c957a9d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ import xyz.jpenilla.runpaper.task.RunServer plugins { id("io.github.gradle-nexus.publish-plugin") version "1.3.0" - id("xyz.jpenilla.run-paper") version "2.2.2" + id("xyz.jpenilla.run-paper") version "2.2.3" } if (!File("$rootDir/.git").exists()) { From 63689d8cd87462684ef70b05fa863988aa9ba0f8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:15:03 +0100 Subject: [PATCH 061/114] Update piston to v0.5.8 (#2574) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5d9cede7c..4280b03ff 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -43,7 +43,7 @@ serverlib = "2.3.4" ## Internal text-adapter = "3.0.6" text = "3.0.4" -piston = "0.5.7" +piston = "0.5.8" # Tests mockito = "5.10.0" From e13ef63c6533ad054b19aa99b983626903009f0c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:28:54 +0100 Subject: [PATCH 062/114] Update plotsquared to v7.3.3 (#2575) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4280b03ff..ced8af919 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" towny = "0.100.1.10" -plotsquared = "7.3.2" +plotsquared = "7.3.3" # Third party bstats = "3.0.2" From 4ef4b874984e5bb5bd9622d3060292864b4e2865 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:29:22 +0100 Subject: [PATCH 063/114] Update release-drafter/release-drafter action to v6 (#2578) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 90248b436..131fb810e 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -12,6 +12,6 @@ jobs: if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }} runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 974078c2cd0ed65e19ecef80a3f81d0dd0cff72a Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Tue, 6 Feb 2024 21:45:25 +0100 Subject: [PATCH 064/114] Release 2.9.2 Signed-off-by: Alexander Brandes --- build.gradle.kts | 4 ++-- .../src/main/java/com/fastasyncworldedit/core/FaweCache.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 20c957a9d..fb3256d36 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.8.5") +var rootVersion by extra("2.9.0") var snapshot by extra("SNAPSHOT") var revision: String by extra("") var buildNumber by extra("") @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s-%s", rootVersion, buildNumber) +version = String.format("%s", rootVersion) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java index f5cc88284..46a3a1574 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/FaweCache.java @@ -626,7 +626,7 @@ public enum FaweCache implements Trimable { * Create a new blocking executor with specified name and FaweCache logger * * @return new blocking executor - * @since TODO + * @since 2.9.0 */ public ThreadPoolExecutor newBlockingExecutor(String name) { return newBlockingExecutor(name, LOGGER); @@ -636,7 +636,7 @@ public enum FaweCache implements Trimable { * Create a new blocking executor with specified name and logger * * @return new blocking executor - * @since TODO + * @since 2.9.0 */ public ThreadPoolExecutor newBlockingExecutor(String name, Logger logger) { int nThreads = Settings.settings().QUEUE.PARALLEL_THREADS; From c93ec878fa1f4c04f7e3a783a425171c587a958a Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Tue, 6 Feb 2024 23:09:06 +0100 Subject: [PATCH 065/114] Back to snapshot for development Signed-off-by: Alexander Brandes --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fb3256d36..84685390e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.9.0") +var rootVersion by extra("2.9.1") var snapshot by extra("SNAPSHOT") var revision: String by extra("") var buildNumber by extra("") @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s", rootVersion) +version = String.format("%s-%s", rootVersion, buildNumber) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") From f94b96d5b2dbc9863bc183bb3764b8158601add2 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sun, 18 Feb 2024 09:55:06 +0100 Subject: [PATCH 066/114] Use actual radius for inverse and adjust inside region check (#2582) --- .../java/com/sk89q/worldedit/regions/CylinderRegion.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java index 03b38a55c..6439d6079 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/CylinderRegion.java @@ -141,7 +141,7 @@ public class CylinderRegion extends AbstractRegion implements FlatRegion { */ public void setRadius(Vector2 radius) { this.radius = radius.add(0.5, 0.5); - this.radiusInverse = Vector2.ONE.divide(radius); + this.radiusInverse = Vector2.ONE.divide(this.radius); } /** @@ -413,11 +413,12 @@ public class CylinderRegion extends AbstractRegion implements FlatRegion { final IChunk chunk, final Filter filter, final ChunkFilterBlock block, final IChunkGet get, final IChunkSet set, boolean full ) { - int bcx = chunk.getX() >> 4; - int bcz = chunk.getZ() >> 4; + int bcx = chunk.getX() << 4; + int bcz = chunk.getZ() << 4; int tcx = bcx + 15; int tcz = bcz + 15; - if (contains(bcx, bcz) && contains(tcx, tcz)) { + // must contain all 4 corners for fast path + if (contains(bcx, bcz) && contains(tcx, tcz) && contains(bcx, tcz) && contains(tcx, bcz)) { filter(chunk, filter, block, get, set, minY, maxY, full); return; } From 5a749b3b2ce531baa9a95622811600f1dde8afa3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 12:00:21 +0100 Subject: [PATCH 067/114] Update dependency org.mockito:mockito-core to v5.11.0 (#2601) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- worldedit-sponge/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ced8af919..513d46a23 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,7 +46,7 @@ text = "3.0.4" piston = "0.5.8" # Tests -mockito = "5.10.0" +mockito = "5.11.0" # Gradle plugins pluginyml = "0.6.0" diff --git a/worldedit-sponge/build.gradle.kts b/worldedit-sponge/build.gradle.kts index c37fe591e..6498cca7e 100644 --- a/worldedit-sponge/build.gradle.kts +++ b/worldedit-sponge/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { }) api("org.apache.logging.log4j:log4j-api") api("org.bstats:bstats-sponge:1.7") - testImplementation("org.mockito:mockito-core:5.10.0") + testImplementation("org.mockito:mockito-core:5.11.0") } <<<<<<< HEAD From c74486f1403b74b174f59015018d78bc8e54ea3d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 12:00:40 +0100 Subject: [PATCH 068/114] Update dependency net.kyori:adventure-nbt to v4.16.0 (#2600) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- worldedit-bukkit/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 5a9273371..1a922b7af 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -178,7 +178,7 @@ tasks.named("shadowJar") { include(dependency("org.lz4:lz4-java:1.8.0")) } relocate("net.kyori", "com.fastasyncworldedit.core.adventure") { - include(dependency("net.kyori:adventure-nbt:4.15.0")) + include(dependency("net.kyori:adventure-nbt:4.16.0")) } relocate("com.zaxxer", "com.fastasyncworldedit.core.math") { include(dependency("com.zaxxer:SparseBitSet:1.3")) From dbc84a4565311d8f68b98d3846e99f8cb3d8c046 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 12:01:09 +0100 Subject: [PATCH 069/114] Update adventure to v4.16.0 (#2599) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 513d46a23..48443e60e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ plotsquared = "7.3.3" bstats = "3.0.2" sparsebitset = "1.3" parallelgzip = "1.0.5" -adventure = "4.15.0" +adventure = "4.16.0" adventure-bukkit = "4.3.2" checkerqual = "3.42.0" truezip = "6.8.4" From d62e631ce3e0789679a9349c3076ec321f3891b6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 12:01:49 +0100 Subject: [PATCH 070/114] Update plotsquared to v7.3.5 (#2598) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 48443e60e..264ff5a59 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" towny = "0.100.1.10" -plotsquared = "7.3.3" +plotsquared = "7.3.5" # Third party bstats = "3.0.2" From 7af7c28a9ae5ef7ffe57dd31d680a2d2c9c7a0fa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 12:02:08 +0100 Subject: [PATCH 071/114] Update dependency org.ajoberstar.grgit:grgit-gradle to v5.2.2 (#2596) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index fdb47a435..e4227d576 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -22,7 +22,7 @@ val properties = Properties().also { props -> dependencies { implementation(gradleApi()) - implementation("org.ajoberstar.grgit:grgit-gradle:5.2.1") + implementation("org.ajoberstar.grgit:grgit-gradle:5.2.2") implementation("com.github.johnrengelman:shadow:8.1.1") implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.11") } From 3a9bbb72932ef9740111830364cb942165b8bf8e Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sat, 2 Mar 2024 12:31:25 +0100 Subject: [PATCH 072/114] Update renovate.json --- .github/renovate.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index 740d991fb..3d0f91b5a 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -33,9 +33,6 @@ "Renovate" ], "rebaseWhen" : "conflicted", - "schedule" : [ - "on the first day of the month" - ], "customManagers" : [ { "customType" : "regex", From f5dfe3ae168c8225ef57e44ad4cd1b2b20f49880 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sat, 2 Mar 2024 13:43:38 +0100 Subject: [PATCH 073/114] fix rollback database query (#2591) --- .../core/database/RollbackDatabase.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java index 603e8061a..1677521fe 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/database/RollbackDatabase.java @@ -159,18 +159,23 @@ public class RollbackDatabase extends AsyncNotifyQueue { Future future = call(() -> { try { int count = 0; - String stmtStr; + String stmtStr = """ + SELECT * FROM `%sedits` + WHERE `time` > ? + AND `x2` >= ? + AND `x1` <= ? + AND `z2` >= ? + AND `z1` <= ? + AND `y2` >= ? + AND `y1` <= ? + """; + if (uuid != null) { + stmtStr += "\n AND `player`= ?"; + } if (ascending) { - if (uuid == null) { - stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + - "`y2`>=? AND `y1`<=? ORDER BY `time` , `id`"; - } else { - stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + - "`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` ASC, `id` ASC"; - } + stmtStr += "\n ORDER BY `time` ASC, `id` ASC"; } else { - stmtStr = "SELECT * FROM`%sedits` WHERE `time`>? AND `x2`>=? AND `x1`<=? AND `z2`>=? AND `z1`<=? AND " + - "`y2`>=? AND `y1`<=? AND `player`=? ORDER BY `time` DESC, `id` DESC"; + stmtStr += "\n ORDER BY `time` DESC, `id` DESC"; } try (PreparedStatement stmt = connection.prepareStatement(stmtStr.formatted(this.prefix))) { stmt.setInt(1, (int) (minTime / 1000)); From b6d691d12b1ff5910705c301f0f818e9fb271e25 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 11:02:35 +0000 Subject: [PATCH 074/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.17 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 264ff5a59..0119179f7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.1.10" +towny = "0.100.1.17" plotsquared = "7.3.5" # Third party From 164271374b46957775f52aedf7a39bebd3ff3df3 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Mon, 4 Mar 2024 07:31:56 +0100 Subject: [PATCH 075/114] Decrease lock contention in SingleThreadQueueExtent (#2594) * thread local extent * avoid race conditions due to ChunkHolder pooling * clean up JFR events, javadoc * remove ThreadLocalPassthroughExtent --- .../implementation/ParallelQueueExtent.java | 33 +++++++++++++- .../queue/implementation/QueueHandler.java | 2 +- .../SingleThreadQueueExtent.java | 16 +++---- .../implementation/chunk/ChunkHolder.java | 43 +++++++++++-------- .../com/sk89q/worldedit/LocalSession.java | 5 +++ 5 files changed, 67 insertions(+), 32 deletions(-) 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 2d2b45aae..52e6cf2c6 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 @@ -18,6 +18,7 @@ import com.fastasyncworldedit.core.queue.Filter; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.function.mask.BlockMask; import com.sk89q.worldedit.function.mask.ExistingBlockMask; @@ -45,6 +46,7 @@ import java.util.stream.IntStream; public class ParallelQueueExtent extends PassthroughExtent { private static final Logger LOGGER = LogManagerCompat.getLogger(); + private static final ThreadLocal extents = new ThreadLocal<>(); private final World world; private final QueueHandler handler; @@ -73,10 +75,36 @@ public class ParallelQueueExtent extends PassthroughExtent { this.fastmode = fastmode; } + /** + * Removes the extent currently associated with the calling thread. + */ + public static void clearCurrentExtent() { + extents.remove(); + } + + /** + * Sets the extent associated with the calling thread. + */ + public static void setCurrentExtent(Extent extent) { + extents.set(extent); + } + + private void enter(Extent extent) { + setCurrentExtent(extent); + } + + private void exit() { + clearCurrentExtent(); + } + @Override @SuppressWarnings({"unchecked", "rawtypes"}) public IQueueExtent getExtent() { - return (IQueueExtent) super.getExtent(); + Extent extent = extents.get(); + if (extent == null) { + extent = super.getExtent(); + } + return (IQueueExtent) extent; } @Override @@ -114,6 +142,7 @@ public class ParallelQueueExtent extends PassthroughExtent { final SingleThreadQueueExtent queue = (SingleThreadQueueExtent) getNewQueue(); queue.setFastMode(fastmode); queue.setFaweExceptionArray(faweExceptionReasonsUsed); + enter(queue); synchronized (queue) { try { ChunkFilterBlock block = null; @@ -154,6 +183,8 @@ public class ParallelQueueExtent extends PassthroughExtent { exceptionCount++; LOGGER.warn(message); } + } finally { + exit(); } })).toArray(ForkJoinTask[]::new); // Join filters 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 956c33fb2..7bdbf4645 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 @@ -408,7 +408,7 @@ public abstract class QueueHandler implements Trimable, Runnable { * Sets the current thread's {@link IQueueExtent} instance in the queue pool to null. */ public void unCache() { - queuePool.set(null); + queuePool.remove(); } private IQueueExtent pool() { 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 132229d1d..6e06ce348 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 @@ -9,7 +9,6 @@ import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder; import com.fastasyncworldedit.core.extent.processor.ProcessorScope; import com.fastasyncworldedit.core.internal.exception.FaweException; -import com.fastasyncworldedit.core.queue.IChunk; import com.fastasyncworldedit.core.queue.IChunkCache; import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; @@ -48,11 +47,9 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen private static final Logger LOGGER = LogManagerCompat.getLogger(); - // Pool discarded chunks for reuse (can safely be cleared by another thread) - // private static final ConcurrentLinkedQueue CHUNK_POOL = new ConcurrentLinkedQueue<>(); // Chunks currently being queued / worked on - private final Long2ObjectLinkedOpenHashMap chunks = new Long2ObjectLinkedOpenHashMap<>(); - private final ConcurrentLinkedQueue submissions = new ConcurrentLinkedQueue<>(); + private final Long2ObjectLinkedOpenHashMap> chunks = new Long2ObjectLinkedOpenHashMap<>(); + private final ConcurrentLinkedQueue> submissions = new ConcurrentLinkedQueue<>(); private final ReentrantLock getChunkLock = new ReentrantLock(); private World world = null; private int minY = 0; @@ -142,12 +139,10 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen if (!this.initialized) { return; } - if (!this.chunks.isEmpty()) { - getChunkLock.lock(); - for (IChunk chunk : this.chunks.values()) { - chunk.recycle(); - } + getChunkLock.lock(); + try { this.chunks.clear(); + } finally { getChunkLock.unlock(); } this.enabledQueue = true; @@ -234,7 +229,6 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen } } if (chunk.isEmpty()) { - chunk.recycle(); Future result = Futures.immediateFuture(null); return (V) result; } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java index 6103a1649..a7417eddf 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/chunk/ChunkHolder.java @@ -1,7 +1,5 @@ package com.fastasyncworldedit.core.queue.implementation.chunk; -import com.fastasyncworldedit.core.FaweCache; -import com.fastasyncworldedit.core.configuration.Settings; import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor; import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; @@ -11,36 +9,34 @@ import com.fastasyncworldedit.core.queue.IChunkGet; import com.fastasyncworldedit.core.queue.IChunkSet; import com.fastasyncworldedit.core.queue.IQueueChunk; import com.fastasyncworldedit.core.queue.IQueueExtent; -import com.fastasyncworldedit.core.queue.Pool; +import com.fastasyncworldedit.core.queue.implementation.ParallelQueueExtent; import com.fastasyncworldedit.core.util.MemUtil; import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; 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; +import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; /** * An abstract {@link IChunk} class that implements basic get/set blocks. */ @SuppressWarnings("rawtypes") public class ChunkHolder> implements IQueueChunk { - - private static final Pool POOL = FaweCache.INSTANCE.registerPool( - ChunkHolder.class, - ChunkHolder::new, - Settings.settings().QUEUE.POOL - ); + private static final Logger LOGGER = LogManagerCompat.getLogger(); public static ChunkHolder newInstance() { - return POOL.poll(); + return new ChunkHolder(); } private volatile IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes) @@ -63,16 +59,12 @@ public class ChunkHolder> implements IQueueChunk { this.delegate = delegate; } + private static final AtomicBoolean recycleWarning = new AtomicBoolean(false); @Override - public synchronized void recycle() { - delegate = NULL; - if (chunkSet != null) { - chunkSet.recycle(); - chunkSet = null; + public void recycle() { + if (!recycleWarning.getAndSet(true)) { + LOGGER.warn("ChunkHolder should not be recycled.", new Exception()); } - chunkExisting = null; - extent = null; - POOL.offer(this); } public long initAge() { @@ -1018,7 +1010,6 @@ public class ChunkHolder> implements IQueueChunk { // Do nothing }); } - recycle(); return null; } @@ -1031,6 +1022,7 @@ public class ChunkHolder> implements IQueueChunk { IChunkGet get = getOrCreateGet(); try { get.lockCall(); + trackExtent(); boolean postProcess = !(getExtent().getPostProcessor() instanceof EmptyBatchProcessor); final int copyKey = get.setCreateCopy(postProcess); final IChunkSet iChunkSet = getExtent().processSet(this, get, set); @@ -1046,11 +1038,24 @@ public class ChunkHolder> implements IQueueChunk { return get.call(set, finalizer); } finally { get.unlockCall(); + untrackExtent(); } } return null; } + // "call" can be called by QueueHandler#blockingExecutor. In such case, we still want the other thread + // to use this SingleThreadQueueExtent. Otherwise, many threads might end up locking on **one** STQE. + // This way, locking is spread across multiple STQEs, allowing for better performance + + private void trackExtent() { + ParallelQueueExtent.setCurrentExtent(extent); + } + + private void untrackExtent() { + ParallelQueueExtent.clearCurrentExtent(); + } + /** * Get the extent this chunk is in. */ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index 67bbff7af..a00b26702 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -32,6 +32,7 @@ import com.fastasyncworldedit.core.internal.io.FaweOutputStream; import com.fastasyncworldedit.core.limit.FaweLimit; import com.fastasyncworldedit.core.util.BrushCache; import com.fastasyncworldedit.core.util.MainUtil; +import com.fastasyncworldedit.core.util.MaskTraverser; import com.fastasyncworldedit.core.util.StringMan; import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TextureHolder; @@ -53,6 +54,7 @@ import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Locatable; +import com.sk89q.worldedit.extent.NullExtent; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.inventory.BlockBag; @@ -594,6 +596,9 @@ public class LocalSession implements TextureHolder { long size = MainUtil.getSize(item); historySize -= size; } + // free the mask from any remaining references to e.g. extents + // if used again + new MaskTraverser(mask).reset(NullExtent.INSTANCE); } finally { historyWriteLock.unlock(); } From 641297497abf26886f2f11f24d2978143fad5c33 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Mon, 4 Mar 2024 07:32:33 +0100 Subject: [PATCH 076/114] Maintain insertion order for RandomPattern sub-patterns (#2603) --- .../core/util/collection/RandomCollection.java | 2 +- .../com/sk89q/worldedit/function/pattern/RandomPattern.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 a157ebd7c..6214777e5 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 @@ -33,7 +33,7 @@ public abstract class RandomCollection { public static RandomCollection of(Map weights, SimpleRandom random) { checkNotNull(random); return FastRandomCollection.create(weights, random) - .orElse(new SimpleRandomCollection<>(weights, random)); + .orElseGet(() -> new SimpleRandomCollection<>(weights, random)); } public void setRandom(SimpleRandom random) { 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 0568fd5b8..1d2df7223 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,7 +27,7 @@ import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -41,7 +41,7 @@ public class RandomPattern extends AbstractPattern { //FAWE start - SimpleRandom > Random, LHS

> List private final SimpleRandom random; - private Map weights = new HashMap<>(); + private Map weights = new LinkedHashMap<>(); private RandomCollection collection; private LinkedHashSet patterns = new LinkedHashSet<>(); //FAWE end From eca4dbdca1df8a02fc073f57f9bc84960207d9e6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:56:45 +0000 Subject: [PATCH 077/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.18 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0119179f7..c55c9169c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.1.17" +towny = "0.100.1.18" plotsquared = "7.3.5" # Third party From 9fa3385edccfeee1dc40a1e4d91aa657ea523bc5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 9 Mar 2024 13:02:30 +0000 Subject: [PATCH 078/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.19 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c55c9169c..414e204f5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.1.18" +towny = "0.100.1.19" plotsquared = "7.3.5" # Third party From e682917c437096885b84332cf421d62fc395bcf8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 10 Mar 2024 18:53:53 +0000 Subject: [PATCH 079/114] Update plotsquared to v7.3.6 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 414e204f5..19a0bd368 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" towny = "0.100.1.19" -plotsquared = "7.3.5" +plotsquared = "7.3.6" # Third party bstats = "3.0.2" From 1053d467a43c215083908679d16b5d4727cadf40 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:47:53 +0000 Subject: [PATCH 080/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.20 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 19a0bd368..a6acb6c2e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.1.19" +towny = "0.100.1.20" plotsquared = "7.3.6" # Third party From facd31ce31fc31b7a6e7fdbed5b81777231aaf13 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 14 Mar 2024 17:56:08 +0100 Subject: [PATCH 081/114] fix: do not forcefully direct to x,y,z methods in AbstractDelegateExtent (#2614) - we probably shouldn't be doing this --- .../extent/AbstractDelegateExtent.java | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java index 768af09c1..f2d7dc884 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/AbstractDelegateExtent.java @@ -91,9 +91,7 @@ public class AbstractDelegateExtent implements Extent { @Override public BlockState getBlock(BlockVector3 position) { - //FAWE start - return coordinates - return extent.getBlock(position.getX(), position.getY(), position.getZ()); - //FAWE end + return extent.getBlock(position); } @Override @@ -103,9 +101,7 @@ public class AbstractDelegateExtent implements Extent { @Override public BaseBlock getFullBlock(BlockVector3 position) { - //FAWE start - return coordinates - return extent.getFullBlock(position.getX(), position.getY(), position.getZ()); - //FAWE end + return extent.getFullBlock(position); } //FAWE start @@ -117,9 +113,7 @@ public class AbstractDelegateExtent implements Extent { @Override public BaseBlock getFullBlock(int x, int y, int z) { - //FAWE start - return coordinates return extent.getFullBlock(x, y, z); - //FAWE end } @Override @@ -375,9 +369,7 @@ public class AbstractDelegateExtent implements Extent { @Override public BiomeType getBiome(BlockVector3 position) { - //FAWE start - switch top x,y,z - return extent.getBiomeType(position.getX(), position.getY(), position.getZ()); - //FAWE end + return extent.getBiome(position); } //FAWE start @@ -420,9 +412,7 @@ public class AbstractDelegateExtent implements Extent { @Override public > boolean setBlock(BlockVector3 position, T block) throws WorldEditException { - //FAWE start - switch to x,y,z - return extent.setBlock(position.getX(), position.getY(), position.getZ(), block); - //FAWE end + return extent.setBlock(position, block); } //FAWE start @@ -447,9 +437,7 @@ public class AbstractDelegateExtent implements Extent { @Override public boolean setBiome(BlockVector3 position, BiomeType biome) { - //FAWE start - switch to x,y,z - return extent.setBiome(position.getX(), position.getY(), position.getZ(), biome); - //FAWE end + return extent.setBiome(position, biome); } //FAWE start From df9527b0b7c4a686b6496dae26661392356d74fa Mon Sep 17 00:00:00 2001 From: MineFact <66020920+MineFact@users.noreply.github.com> Date: Fri, 15 Mar 2024 11:27:25 +0100 Subject: [PATCH 082/114] fix: set and clone Expression Environment after Expression cloning (#2617) - Fixes #2616 --- .../sk89q/worldedit/internal/expression/Expression.java | 5 ++++- .../internal/expression/ExpressionEnvironment.java | 5 ++++- .../regions/shape/WorldEditExpressionEnvironment.java | 9 +++++++-- .../internal/expression/BaseExpressionTest.java | 5 +++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java index b7ce1acf3..23ba96a88 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/Expression.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.antlr.ExpressionLexer; import com.sk89q.worldedit.antlr.ExpressionParser; import com.sk89q.worldedit.internal.expression.invoke.ExpressionCompiler; +import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; @@ -199,7 +200,9 @@ public class Expression implements Cloneable { //FAWE start public Expression clone() { - return new Expression(initialExpression, new HashSet<>(providedSlots)); + Expression expression = new Expression(initialExpression, new HashSet<>(providedSlots)); + expression.setEnvironment(getEnvironment().clone()); + return expression; } //FAWE end diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionEnvironment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionEnvironment.java index 061a520a9..5e44d9da1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionEnvironment.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/expression/ExpressionEnvironment.java @@ -22,7 +22,7 @@ package com.sk89q.worldedit.internal.expression; /** * Represents a way to access blocks in a world. Has to accept non-rounded coordinates. */ -public interface ExpressionEnvironment { +public interface ExpressionEnvironment extends Cloneable { int getBlockType(double x, double y, double z); @@ -36,4 +36,7 @@ public interface ExpressionEnvironment { int getBlockDataRel(double x, double y, double z); + // FAWE start + ExpressionEnvironment clone(); + // FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java index 15e93c7db..4b87ebcc6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java @@ -28,6 +28,8 @@ import com.sk89q.worldedit.math.Vector3; public class WorldEditExpressionEnvironment implements ExpressionEnvironment { + private static final Vector3 BLOCK_CENTER_OFFSET = Vector3.at(0.5, 0.5, 0.5); + private final Vector3 unit; private final Vector3 zero2; //FAWE start - MutableVector3 @@ -42,7 +44,7 @@ public class WorldEditExpressionEnvironment implements ExpressionEnvironment { public WorldEditExpressionEnvironment(Extent extent, Vector3 unit, Vector3 zero) { this.extent = extent; this.unit = unit; - this.zero2 = zero.add(0.5, 0.5, 0.5); + this.zero2 = zero.add(BLOCK_CENTER_OFFSET); } public BlockVector3 toWorld(double x, double y, double z) { @@ -94,10 +96,13 @@ public class WorldEditExpressionEnvironment implements ExpressionEnvironment { public Vector3 toWorldRel(double x, double y, double z) { return current.add(x, y, z); } + + public WorldEditExpressionEnvironment clone() { + return new WorldEditExpressionEnvironment(extent, unit, zero2.subtract(BLOCK_CENTER_OFFSET)); + } //FAWe end public void setCurrentBlock(Vector3 current) { this.current = current; } - } diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java index 3491347a4..3c8ba65d6 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/expression/BaseExpressionTest.java @@ -114,6 +114,11 @@ class BaseExpressionTest { public int getBlockDataRel(double x, double y, double z) { return (int) y * 100; } + + @Override + public ExpressionEnvironment clone() { + return this; + } }); return expression.evaluate(); From 8363badf8015b58475b500f2814ed05b290f6ac1 Mon Sep 17 00:00:00 2001 From: Jordan Date: Fri, 15 Mar 2024 18:39:35 +0100 Subject: [PATCH 083/114] fix: correctly (de)serialise ItemType (#2620) - InternalId changes depending on ~~magic~~ so we should not store this value - fixes #2589 --- .../sk89q/worldedit/util/gson/GsonUtil.java | 3 +++ .../worldedit/util/gson/ItemTypeAdapter.java | 26 +++++++++++++++++++ .../sk89q/worldedit/world/item/ItemType.java | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/ItemTypeAdapter.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java index 9b9a9a6e5..d31750e11 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java @@ -23,6 +23,8 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.item.ItemTypes; /** * Utility methods for Google's GSON library. @@ -41,6 +43,7 @@ public final class GsonUtil { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Vector3.class, new VectorAdapter()); gsonBuilder.registerTypeAdapter(BlockVector3.class, new BlockVectorAdapter()); + gsonBuilder.registerTypeAdapter(ItemType.class, new ItemTypeAdapter()); return gsonBuilder; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/ItemTypeAdapter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/ItemTypeAdapter.java new file mode 100644 index 000000000..5f8d6d9a2 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/ItemTypeAdapter.java @@ -0,0 +1,26 @@ +package com.sk89q.worldedit.util.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.item.ItemTypes; + +import java.lang.reflect.Type; + +public final class ItemTypeAdapter implements JsonDeserializer { + + @Override + public ItemType deserialize(JsonElement json, Type type, JsonDeserializationContext cont) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + String id = jsonObject.get("id").getAsString(); + ItemType itemType = ItemTypes.get(id); + if (itemType == null) { + throw new JsonParseException("Could not parse item type `" + id + "`"); + } + return itemType; + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java index 5a1e7653f..16a65c308 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/item/ItemType.java @@ -79,7 +79,7 @@ public class ItemType implements RegistryItem, Keyed { } //FAWE start - private int internalId; + private transient int internalId; @Override public void setInternalId(int internalId) { From 10dc64eeaf5693cf2d1e4064c045cd36262cf42a Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Fri, 15 Mar 2024 18:41:16 +0100 Subject: [PATCH 084/114] Make sure to process all chunks in PQE even if PARALLEL_THREADS = 1 (#2611) make sure to process all chunks in PQE even if PARALLEL_THREADS = 1 --- .../core/queue/implementation/ParallelQueueExtent.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 52e6cf2c6..e88a9ccd3 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 @@ -131,9 +131,12 @@ public class ParallelQueueExtent extends PassthroughExtent { // Get a pool, to operate on the chunks in parallel final int size = Math.min(chunks.size(), Settings.settings().QUEUE.PARALLEL_THREADS); - if (size <= 1 && chunksIter.hasNext()) { - BlockVector2 pos = chunksIter.next(); - getExtent().apply(null, filter, region, pos.getX(), pos.getZ(), full); + if (size <= 1) { + // if PQE is ever used with PARALLEL_THREADS = 1, or only one chunk is edited, just run sequentially + while (chunksIter.hasNext()) { + BlockVector2 pos = chunksIter.next(); + getExtent().apply(null, filter, region, pos.getX(), pos.getZ(), full); + } } else { final ForkJoinTask[] tasks = IntStream.range(0, size).mapToObj(i -> handler.submit(() -> { try { From 37d4e9bb6f431204ceccb59b836de9e5358c7acb Mon Sep 17 00:00:00 2001 From: Zeranny Date: Fri, 15 Mar 2024 17:41:53 +0000 Subject: [PATCH 085/114] Allow factory suggestions to have parser context (#2613) * Add context to rich parser * Description for getSuggestions with context * Tidy up imports * Swap which getSuggestions is primary * Revert "Swap which getSuggestions is primary" This reverts commit 5c257f60ea9ec002e407b2a4a8a7c6ede6bee5e7. * Revert default swap and add deprecation note --- .../extension/factory/parser/RichParser.java | 25 ++++++++++++++++--- .../factory/parser/mask/RichMaskParser.java | 8 +++--- .../command/argument/FactoryConverter.java | 14 ++++++++--- .../extension/factory/MaskFactory.java | 6 ++--- .../internal/registry/AbstractFactory.java | 7 +++++- .../internal/registry/InputParser.java | 13 ++++++++++ 6 files changed, 58 insertions(+), 15 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/RichParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/RichParser.java index 617143495..30dad8f37 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/RichParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/RichParser.java @@ -53,7 +53,7 @@ public abstract class RichParser extends InputParser implements AliasedPar } @Nonnull - private Function> extractArguments(String input) { + private Function> extractArguments(String input, ParserContext context) { return prefix -> { if (input.length() > prefix.length() && input.startsWith(prefix + "[")) { // input already contains argument(s) -> extract them @@ -65,7 +65,7 @@ public abstract class RichParser extends InputParser implements AliasedPar } String previous = prefix + builder; // read the suggestions for the last argument - return getSuggestions(strings[strings.length - 1], strings.length - 1) + return getSuggestions(strings[strings.length - 1], strings.length - 1, context) .map(suggestion -> previous + "[" + suggestion); } else { return Stream.of(prefix); @@ -95,7 +95,7 @@ public abstract class RichParser extends InputParser implements AliasedPar public Stream getSuggestions(String input) { return Arrays.stream(this.prefixes) .filter(validPrefix(input)) - .flatMap(extractArguments(input)); + .flatMap(extractArguments(input, new ParserContext())); } @Override @@ -122,8 +122,25 @@ public abstract class RichParser extends InputParser implements AliasedPar * @param argumentInput the already provided input for the argument at the given index. * @param index the index of the argument to get suggestions for. * @return a stream of suggestions matching the given input for the argument at the given index. + * + * @deprecated Use the version that takes a {@link ParserContext}, {@link #getSuggestions(String, int, ParserContext)} */ - protected abstract Stream getSuggestions(String argumentInput, int index); + @Deprecated + protected Stream getSuggestions(String argumentInput, int index) { + return Stream.empty(); + } + + /** + * Returns a stream of suggestions for the argument at the given index. + * + * @param argumentInput the already provided input for the argument at the given index. + * @param index the index of the argument to get suggestions for. + * @param context the context which may optionally be provided by a parser. + * @return a stream of suggestions matching the given input for the argument at the given index. + */ + protected Stream getSuggestions(String argumentInput, int index, ParserContext context) { + return getSuggestions(argumentInput, index); + } /** * Parses the already split arguments. diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java index 4e0fbfa91..84437c19a 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/mask/RichMaskParser.java @@ -97,11 +97,11 @@ public class RichMaskParser extends FaweParser { )), () -> { if (full.length() == 1) { - return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions("")); + return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions("", context)); } return new ArrayList<>(worldEdit .getMaskFactory() - .getSuggestions(command.toLowerCase(Locale.ROOT))); + .getSuggestions(command.toLowerCase(Locale.ROOT), context)); } ); } @@ -164,11 +164,11 @@ public class RichMaskParser extends FaweParser { )), () -> { if (full.length() == 1) { - return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions("")); + return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions("", context)); } return new ArrayList<>(worldEdit .getMaskFactory() - .getSuggestions(command.toLowerCase(Locale.ROOT))); + .getSuggestions(command.toLowerCase(Locale.ROOT), context)); } ); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java index 1a382d43c..d82d25394 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java @@ -113,8 +113,7 @@ public class FactoryConverter implements ArgumentConverter { ); } - @Override - public ConversionResult convert(String argument, InjectedValueAccess context) { + private ParserContext createContext(InjectedValueAccess context) { Actor actor = context.injectedValue(Key.of(Actor.class)) .orElseThrow(() -> new IllegalStateException("No actor")); LocalSession session = WorldEdit.getInstance().getSessionManager().get(actor); @@ -139,6 +138,13 @@ public class FactoryConverter implements ArgumentConverter { contextTweaker.accept(parserContext); } + return parserContext; + } + + @Override + public ConversionResult convert(String argument, InjectedValueAccess context) { + ParserContext parserContext = createContext(context); + try { return SuccessfulConversion.fromSingle( factoryExtractor.apply(worldEdit).parseFromInput(argument, parserContext) @@ -150,7 +156,9 @@ public class FactoryConverter implements ArgumentConverter { @Override public List getSuggestions(String input, InjectedValueAccess context) { - return factoryExtractor.apply(worldEdit).getSuggestions(input); + ParserContext parserContext = createContext(context); + + return factoryExtractor.apply(worldEdit).getSuggestions(input, parserContext); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java index 24c674f0b..5c8ff7d6f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java @@ -127,13 +127,13 @@ public final class MaskFactory extends AbstractFactory { } @Override - public List getSuggestions(String input) { + public List getSuggestions(String input, final ParserContext parserContext) { final String[] split = input.split(" "); if (split.length > 1) { String prev = input.substring(0, input.lastIndexOf(" ")) + " "; - return super.getSuggestions(split[split.length - 1]).stream().map(s -> prev + s).collect(Collectors.toList()); + return super.getSuggestions(split[split.length - 1], parserContext).stream().map(s -> prev + s).collect(Collectors.toList()); } - return super.getSuggestions(input); + return super.getSuggestions(input, parserContext); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java index ef4299b33..237dd1c4c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java @@ -96,9 +96,14 @@ public abstract class AbstractFactory { throw new NoMatchException(Caption.of("worldedit.error.no-match", TextComponent.of(input))); } + @Deprecated public List getSuggestions(String input) { + return getSuggestions(input, new ParserContext()); + } + + public List getSuggestions(String input, ParserContext context) { return parsers.stream().flatMap( - p -> p.getSuggestions(input) + p -> p.getSuggestions(input, context) ).collect(Collectors.toList()); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java index 87829a272..57ec3a247 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java @@ -45,9 +45,22 @@ public abstract class InputParser { * Gets a stream of suggestions of input to this parser. * * @return a stream of suggestions + * @deprecated Use the version that takes a {@link ParserContext}, {@link #getSuggestions(String, ParserContext)} */ + @Deprecated public Stream getSuggestions(String input) { return Stream.empty(); } + /** + * Gets a stream of suggestions of input to this parser. + * + * @param input The string input + * @param context The parser context + * + * @return a stream of suggestions + */ + public Stream getSuggestions(String input, ParserContext context) { + return getSuggestions(input); + } } From 84f661d57f595d5b80ffb81e8cc26172634b420d Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 15 Mar 2024 23:37:02 +0100 Subject: [PATCH 086/114] Release 2.9.1 Signed-off-by: Alexander Brandes --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 84685390e..9d5734134 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s-%s", rootVersion, buildNumber) +version = String.format("%s", rootVersion) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") From bb9a0d27f26903f768ba72c729ee3d5f221e8775 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Fri, 15 Mar 2024 23:48:14 +0100 Subject: [PATCH 087/114] Back to snapshot for development Signed-off-by: Alexander Brandes --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9d5734134..c5c27082b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,7 @@ logger.lifecycle(""" ******************************************* """) -var rootVersion by extra("2.9.1") +var rootVersion by extra("2.9.2") var snapshot by extra("SNAPSHOT") var revision: String by extra("") var buildNumber by extra("") @@ -52,7 +52,7 @@ ext { } } -version = String.format("%s", rootVersion) +version = String.format("%s-%s", rootVersion, buildNumber) if (!project.hasProperty("gitCommitHash")) { apply(plugin = "org.ajoberstar.grgit") From f60539f41ddd6d917b52f6119274c1985dcff2b4 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 16 Mar 2024 20:12:02 +0100 Subject: [PATCH 088/114] chore: deprecate transform extent for removal (#2623) --- .../com/fastasyncworldedit/core/extent/TransformExtent.java | 4 ++++ 1 file changed, 4 insertions(+) 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 index 91b99b025..64b940d97 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TransformExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TransformExtent.java @@ -12,6 +12,10 @@ 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(); From 472eb19312cde3c27c73e1a49a20edb6e0e08288 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sat, 16 Mar 2024 20:12:21 +0100 Subject: [PATCH 089/114] Allow repeated keys in RandomCollection (#2624) --- .../extent/transform/RandomTransform.java | 28 +++++++------- .../util/collection/FastRandomCollection.java | 24 ++++++------ .../util/collection/RandomCollection.java | 34 +++++------------ .../collection/SimpleRandomCollection.java | 38 +++++++++---------- .../function/pattern/RandomPattern.java | 36 ++++++++---------- 5 files changed, 68 insertions(+), 92 deletions(-) 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 93ab1ac99..a839ec323 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.fastasyncworldedit.core.util.collection.RandomCollection; 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 @@ import static com.google.common.base.Preconditions.checkNotNull; 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 class RandomTransform extends SelectTransform { */ 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 class RandomTransform extends SelectTransform { */ 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/util/collection/FastRandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/FastRandomCollection.java index 61afe2dcd..e332ad231 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.math.random.SimpleRandom; 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 @@ public class FastRandomCollection extends RandomCollection { * {@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 class FastRandomCollection extends RandomCollection { 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/RandomCollection.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/collection/RandomCollection.java index 6214777e5..098818558 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 @@ package com.fastasyncworldedit.core.util.collection; 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); + + record Weighted(T value, double weight) { + } - - public SimpleRandom getRandom() { - return random; - } - - public abstract T next(int x, int y, int z); - } 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 dc2107559..c831bb8f1 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 @@ package com.fastasyncworldedit.core.util.collection; 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/sk89q/worldedit/function/pattern/RandomPattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/RandomPattern.java index 1d2df7223..380051ec2 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.extent.Extent; 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 @@ import static com.google.common.base.Preconditions.checkNotNull; */ 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 class RandomPattern extends AbstractPattern { public RandomPattern(SimpleRandom random) { this.random = random; + this.weights = new ArrayList<>(); } /** @@ -63,9 +63,8 @@ public class RandomPattern extends AbstractPattern { */ 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); } //FAWE end @@ -80,18 +79,15 @@ public class RandomPattern extends AbstractPattern { */ 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() { @@ -100,12 +96,12 @@ public class RandomPattern extends AbstractPattern { @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); } //FAWE end From fb12ff20b4a35224ddf8742d1f2f54217ccded1f Mon Sep 17 00:00:00 2001 From: Jordan Date: Sat, 16 Mar 2024 20:14:33 +0100 Subject: [PATCH 090/114] refactor: apply final/abstract to various fawe extent classes (#2615) * refactor: apply final/abstract to various fawe extent classes * Remove forremoval as targets v3 --- .../core/extent/BlockTranslateExtent.java | 2 +- .../core/extent/DisallowedBlocksExtent.java | 2 +- .../core/extent/ExtentHeightCacher.java | 2 +- .../core/extent/HeightBoundExtent.java | 2 +- .../core/extent/HistoryExtent.java | 2 +- .../core/extent/LimitExtent.java | 2 +- .../core/extent/MemoryCheckingExtent.java | 2 +- .../core/extent/MultiRegionExtent.java | 2 +- .../core/extent/NullExtent.java | 2 +- .../core/extent/PassthroughExtent.java | 2 +- .../core/extent/PositionTransformExtent.java | 2 +- .../core/extent/ProcessedWEExtent.java | 2 +- .../core/extent/ResettableExtent.java | 2 +- .../core/extent/SingleRegionExtent.java | 2 +- .../core/extent/SlowExtent.java | 2 +- .../core/extent/SourceMaskExtent.java | 2 +- .../core/extent/StripNBTExtent.java | 2 +- .../core/extent/SupplyingExtent.java | 2 +- .../core/extent/TemporalExtent.java | 2 +- .../core/extent/TransformExtent.java | 116 ------------------ .../implementation/ParallelQueueExtent.java | 2 +- .../SingleThreadQueueExtent.java | 2 +- 22 files changed, 21 insertions(+), 137 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TransformExtent.java 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 d7b8a986e..f04412e70 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.BaseBlock; 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 f1c523ce3..68307ffe1 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.Objects; 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 af008d1c9..36011e594 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 com.sk89q.worldedit.extent.Extent; 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 c22d9e6be..817d2c3bf 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 com.sk89q.worldedit.regions.Region; 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 2fc5133c6..9ac018043 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 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * 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 24d440c57..3429a1ba5 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.Set; 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 e2d215c56..d33291d1c 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.fastasyncworldedit.core.util.WEManager; 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 c13c5965b..9e02dba94 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.Collection; 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 4f0ad4960..d4c7dc6aa 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.UUID; 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 5ac2f9d7b..23f0fa603 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 javax.annotation.Nullable; 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 6d74acc16..99ef6d137 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.BaseBlock; 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 5abe90348..e9e90e0f8 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 com.sk89q.worldedit.world.block.BlockTypes; 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 0e3798415..469a27c03 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 java.lang.reflect.Field; 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 1caea9dbc..249b9e84c 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.Collection; 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 6aa07b116..657322ce3 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.AbstractDelegateExtent; 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 6c2230372..389f5b69f 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 com.sk89q.worldedit.world.block.BlockStateHolder; 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 ded96d5aa..4d1b287c5 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.Future; 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 640243d5d..96952de4c 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 @@ import java.util.function.Supplier; /** * 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 ac36bef5a..030fcc290 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.BlockState; 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 91b99b025..000000000 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/TransformExtent.java +++ /dev/null @@ -1,116 +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; - -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/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 52e6cf2c6..3ec751aee 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.Set; 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/SingleThreadQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/SingleThreadQueueExtent.java index 6e06ce348..a27c3dd1a 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 @@ import java.util.concurrent.locks.ReentrantLock; * 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(); From 0db8154546af60ba7c35ea9da627d77051035d65 Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 17 Mar 2024 08:37:03 +0100 Subject: [PATCH 091/114] Drop support for 1.17.1 (#2627) Signed-off-by: Alexander Brandes --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 - build.gradle.kts | 2 +- settings.gradle.kts | 2 +- .../adapters/adapter-1_17_1/build.gradle.kts | 27 - .../adapter/ext/fawe/PaperweightAdapter.java | 1013 ------ .../ext/fawe/PaperweightDataConverters.java | 2961 ----------------- .../ext/fawe/PaperweightFakePlayer.java | 103 - .../fawe/PaperweightWorldNativeAccess.java | 209 -- .../v1_17_R1_2/PaperweightBlockMaterial.java | 189 -- .../v1_17_R1_2/PaperweightFaweAdapter.java | 671 ---- .../PaperweightFaweWorldNativeAccess.java | 286 -- .../fawe/v1_17_R1_2/PaperweightGetBlocks.java | 1096 ------ .../v1_17_R1_2/PaperweightGetBlocks_Copy.java | 251 -- .../v1_17_R1_2/PaperweightMapChunkUtil.java | 32 - .../PaperweightPlatformAdapter.java | 526 --- .../v1_17_R1_2/PaperweightPostProcessor.java | 175 - .../PaperweightStarlightRelighter.java | 113 - .../PaperweightStarlightRelighterFactory.java | 26 - .../nbt/PaperweightLazyCompoundTag.java | 161 - .../v1_17_R1_2/regen/PaperweightRegen.java | 694 ---- worldedit-bukkit/build.gradle.kts | 2 +- .../bukkit/BukkitServerInterface.java | 3 - .../core/configuration/Settings.java | 8 +- 23 files changed, 4 insertions(+), 8547 deletions(-) delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightAdapter.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightDataConverters.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightFakePlayer.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightWorldNativeAccess.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightBlockMaterial.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweWorldNativeAccess.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightMapChunkUtil.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPostProcessor.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightStarlightRelighter.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightStarlightRelighterFactory.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/nbt/PaperweightLazyCompoundTag.java delete mode 100644 worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index b687075ad..bc78076bd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -31,7 +31,6 @@ body: - '1.20' - '1.19.4' - '1.18.2' - - '1.17.1' validations: required: true diff --git a/build.gradle.kts b/build.gradle.kts index c5c27082b..15845439a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -83,7 +83,7 @@ allprojects { } applyCommonConfiguration() -val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.4") +val supportedVersions = listOf("1.18.2", "1.19.4", "1.20", "1.20.4") tasks { supportedVersions.forEach { diff --git a/settings.gradle.kts b/settings.gradle.kts index 6f9b3e8e0..74cba1396 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit" include("worldedit-libs") -listOf("1_17_1", "1_18_2", "1_19_4", "1_20", "1_20_2", "1_20_4").forEach { +listOf("1_18_2", "1_19_4", "1_20", "1_20_2", "1_20_4").forEach { include("worldedit-bukkit:adapters:adapter-$it") } diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts deleted file mode 100644 index 9eb68ea36..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension - -applyPaperweightAdapterConfiguration() - -plugins { - java -} - -repositories { - mavenCentral() - gradlePluginPortal() -} - -java { - toolchain.languageVersion.set(JavaLanguageVersion.of(17)) -} - -configurations.all { - attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) -} - - -dependencies { - // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.17.1-R0.1-SNAPSHOT - the().paperDevBundle("1.17.1-R0.1-20220414.034903-210") - compileOnly(libs.paperlib) -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightAdapter.java deleted file mode 100644 index 7d1fcc84e..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightAdapter.java +++ /dev/null @@ -1,1013 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.bukkit.adapter.ext.fawe; - -import com.google.common.base.Preconditions; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.util.concurrent.Futures; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Lifecycle; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.blocks.BaseItem; -import com.sk89q.worldedit.blocks.BaseItemStack; -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.bukkit.adapter.impl.fawe.v1_17_R1_2.PaperweightFaweAdapter; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.PaperweightPlatformAdapter; -import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.extension.platform.Watchdog; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.internal.Constants; -import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.math.BlockVector2; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.registry.state.BooleanProperty; -import com.sk89q.worldedit.registry.state.DirectionalProperty; -import com.sk89q.worldedit.registry.state.EnumProperty; -import com.sk89q.worldedit.registry.state.IntegerProperty; -import com.sk89q.worldedit.registry.state.Property; -import com.sk89q.worldedit.util.Direction; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.concurrency.LazyReference; -import com.sk89q.worldedit.util.formatting.text.Component; -import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; -import com.sk89q.worldedit.util.io.file.SafeFiles; -import com.sk89q.worldedit.util.nbt.BinaryTag; -import com.sk89q.worldedit.util.nbt.ByteArrayBinaryTag; -import com.sk89q.worldedit.util.nbt.ByteBinaryTag; -import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; -import com.sk89q.worldedit.util.nbt.DoubleBinaryTag; -import com.sk89q.worldedit.util.nbt.EndBinaryTag; -import com.sk89q.worldedit.util.nbt.FloatBinaryTag; -import com.sk89q.worldedit.util.nbt.IntArrayBinaryTag; -import com.sk89q.worldedit.util.nbt.IntBinaryTag; -import com.sk89q.worldedit.util.nbt.ListBinaryTag; -import com.sk89q.worldedit.util.nbt.LongArrayBinaryTag; -import com.sk89q.worldedit.util.nbt.LongBinaryTag; -import com.sk89q.worldedit.util.nbt.ShortBinaryTag; -import com.sk89q.worldedit.util.nbt.StringBinaryTag; -import com.sk89q.worldedit.world.DataFixer; -import com.sk89q.worldedit.world.RegenOptions; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; -import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockStateHolder; -import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldedit.world.item.ItemType; -import net.minecraft.Util; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; -import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ServerChunkCache; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.util.StringRepresentable; -import net.minecraft.util.thread.BlockableEventLoop; -import net.minecraft.world.Clearable; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelSettings; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.DirectionProperty; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkBiomeContainer; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.levelgen.WorldGenSettings; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.PrimaryLevelData; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.Vec3; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World.Environment; -import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_17_R1.CraftServer; -import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; -import org.bukkit.craftbukkit.v1_17_R1.util.CraftMagicNumbers; -import org.bukkit.entity.Player; -import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; -import org.bukkit.generator.ChunkGenerator; -import org.spigotmc.SpigotConfig; -import org.spigotmc.WatchdogThread; - -import javax.annotation.Nullable; -import java.lang.ref.WeakReference; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.OptionalInt; -import java.util.OptionalLong; -import java.util.Set; -import java.util.TreeMap; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ForkJoinPool; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; - -public final class PaperweightAdapter implements BukkitImplAdapter { - - private final Logger LOGGER = Logger.getLogger(getClass().getCanonicalName()); - - private final Field worldsField; - private final Method getChunkFutureMainThreadMethod; - private final Field mainThreadProcessorField; - private final Watchdog watchdog; - - // ------------------------------------------------------------------------ - // Code that may break between versions of Minecraft - // ------------------------------------------------------------------------ - - public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException { - // A simple test - CraftServer.class.cast(Bukkit.getServer()); - - int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion(); - if (dataVersion != 2730) { - throw new UnsupportedClassVersionError("Not 1.17.1!"); - } - - worldsField = CraftServer.class.getDeclaredField("worlds"); - worldsField.setAccessible(true); - - getChunkFutureMainThreadMethod = ServerChunkCache.class.getDeclaredMethod("getChunkFutureMainThread", - int.class, int.class, ChunkStatus.class, boolean.class - ); - getChunkFutureMainThreadMethod.setAccessible(true); - - mainThreadProcessorField = ServerChunkCache.class.getDeclaredField( - Refraction.pickName("mainThreadProcessor", "g") - ); - mainThreadProcessorField.setAccessible(true); - - new PaperweightDataConverters(CraftMagicNumbers.INSTANCE.getDataVersion(), this).build(ForkJoinPool.commonPool()); - - Watchdog watchdog; - try { - Class.forName("org.spigotmc.WatchdogThread"); - watchdog = new SpigotWatchdog(); - } catch (ClassNotFoundException | NoSuchFieldException e) { - try { - watchdog = new MojangWatchdog(((CraftServer) Bukkit.getServer()).getServer()); - } catch (NoSuchFieldException ex) { - watchdog = null; - } - } - this.watchdog = watchdog; - - try { - Class.forName("org.spigotmc.SpigotConfig"); - SpigotConfig.config.set("world-settings.faweregentempworld.verbose", false); - } catch (ClassNotFoundException ignored) { - } - } - - @Override - public DataFixer getDataFixer() { - return PaperweightDataConverters.INSTANCE; - } - - /** - * Read the given NBT data into the given tile entity. - * - * @param tileEntity the tile entity - * @param tag the tag - */ - static void readTagIntoTileEntity(net.minecraft.nbt.CompoundTag tag, BlockEntity tileEntity) { - tileEntity.load(tag); - tileEntity.setChanged(); - } - - /** - * Write the tile entity's NBT data to the given tag. - * - * @param tileEntity the tile entity - * @param tag the tag - */ - private static void readTileEntityIntoTag(BlockEntity tileEntity, net.minecraft.nbt.CompoundTag tag) { - tileEntity.save(tag); - } - - /** - * Get the ID string of the given entity. - * - * @param entity the entity - * @return the entity ID - */ - private static String getEntityId(Entity entity) { - return EntityType.getKey(entity.getType()).toString(); - } - - /** - * Create an entity using the given entity ID. - * - * @param id the entity ID - * @param world the world - * @return an entity or null - */ - @Nullable - private static Entity createEntityFromId(String id, net.minecraft.world.level.Level world) { - return EntityType.byString(id).map(t -> t.create(world)).orElse(null); - } - - /** - * Write the given NBT data into the given entity. - * - * @param entity the entity - * @param tag the tag - */ - private static void readTagIntoEntity(net.minecraft.nbt.CompoundTag tag, Entity entity) { - entity.load(tag); - } - - /** - * Write the entity's NBT data to the given tag. - * - * @param entity the entity - * @param tag the tag - */ - private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag tag) { - //FAWE start - avoid villager async catcher - PaperweightPlatformAdapter.readEntityIntoTag(entity, tag); - //FAWE end - } - - private static Block getBlockFromType(BlockType blockType) { - return Registry.BLOCK.get(ResourceLocation.tryParse(blockType.getId())); - } - - private static Item getItemFromType(ItemType itemType) { - return Registry.ITEM.get(ResourceLocation.tryParse(itemType.getId())); - } - - @Override - public OptionalInt getInternalBlockStateId(BlockData data) { - net.minecraft.world.level.block.state.BlockState state = ((CraftBlockData) data).getState(); - int combinedId = Block.getId(state); - return combinedId == 0 && state.getBlock() != Blocks.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId); - } - - @Override - public OptionalInt getInternalBlockStateId(BlockState state) { - Block mcBlock = getBlockFromType(state.getBlockType()); - net.minecraft.world.level.block.state.BlockState newState = mcBlock.defaultBlockState(); - Map, Object> states = state.getStates(); - newState = applyProperties(mcBlock.getStateDefinition(), newState, states); - final int combinedId = Block.getId(newState); - return combinedId == 0 && state.getBlockType() != BlockTypes.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId); - } - - @Deprecated - @Override - public BlockState getBlock(Location location) { - Preconditions.checkNotNull(location); - - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); - int x = location.getBlockX(); - int y = location.getBlockY(); - int z = location.getBlockZ(); - final ServerLevel handle = craftWorld.getHandle(); - LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); - final BlockPos blockPos = new BlockPos(x, y, z); - final CraftBlockData blockData = chunk.getBlockState(blockPos).createCraftBlockData(); - BlockState state = BukkitAdapter.adapt(blockData); - if (state == null) { - org.bukkit.block.Block bukkitBlock = location.getBlock(); - state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); - } - - return state; - } - - @Override - public BaseBlock getFullBlock(Location location) { - BlockState state = getBlock(location); - - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); - int x = location.getBlockX(); - int y = location.getBlockY(); - int z = location.getBlockZ(); - - final ServerLevel handle = craftWorld.getHandle(); - LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); - final BlockPos blockPos = new BlockPos(x, y, z); - - // Read the NBT data - BlockEntity te = chunk.getBlockEntity(blockPos); - if (te != null) { - net.minecraft.nbt.CompoundTag tag = te.save(new net.minecraft.nbt.CompoundTag()); - //FAWE start - BinaryTag - return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag)); - //FAWE end - } - - return state.toBaseBlock(); - } - - @Override - public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { - return new PaperweightWorldNativeAccess( - this, - new WeakReference<>(((CraftWorld) world).getHandle()) - ); - } - - private static net.minecraft.core.Direction adapt(Direction face) { - switch (face) { - case NORTH: - return net.minecraft.core.Direction.NORTH; - case SOUTH: - return net.minecraft.core.Direction.SOUTH; - case WEST: - return net.minecraft.core.Direction.WEST; - case EAST: - return net.minecraft.core.Direction.EAST; - case DOWN: - return net.minecraft.core.Direction.DOWN; - case UP: - default: - return net.minecraft.core.Direction.UP; - } - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private net.minecraft.world.level.block.state.BlockState applyProperties( - StateDefinition stateContainer, - net.minecraft.world.level.block.state.BlockState newState, - Map, Object> states - ) { - for (Map.Entry, Object> state : states.entrySet()) { - net.minecraft.world.level.block.state.properties.Property property = - stateContainer.getProperty(state.getKey().getName()); - Comparable value = (Comparable) state.getValue(); - // we may need to adapt this value, depending on the source prop - if (property instanceof DirectionProperty) { - Direction dir = (Direction) value; - value = adapt(dir); - } else if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { - String enumName = (String) value; - value = ((net.minecraft.world.level.block.state.properties.EnumProperty) property) - .getValue(enumName).orElseThrow(() -> - new IllegalStateException( - "Enum property " + property.getName() + " does not contain " + enumName - ) - ); - } - - newState = newState.setValue( - (net.minecraft.world.level.block.state.properties.Property) property, - (Comparable) value - ); - } - return newState; - } - - @Override - public BaseEntity getEntity(org.bukkit.entity.Entity entity) { - checkNotNull(entity); - - CraftEntity craftEntity = ((CraftEntity) entity); - Entity mcEntity = craftEntity.getHandle(); - - String id = getEntityId(mcEntity); - - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - readEntityIntoTag(mcEntity, tag); - //FAWE start - BinaryTag - return new BaseEntity( - com.sk89q.worldedit.world.entity.EntityTypes.get(id), - LazyReference.from(() -> (CompoundBinaryTag) toNativeBinary(tag)) - ); - //FAWE end - } - - @Nullable - @Override - public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) { - checkNotNull(location); - checkNotNull(state); - - CraftWorld craftWorld = ((CraftWorld) location.getWorld()); - ServerLevel worldServer = craftWorld.getHandle(); - - Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle()); - - if (createdEntity != null) { - CompoundTag nativeTag = state.getNbtData(); - if (nativeTag != null) { - net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNative(nativeTag); - for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - readTagIntoEntity(tag, createdEntity); - } - - createdEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); - - worldServer.addEntity(createdEntity, SpawnReason.CUSTOM); - return createdEntity.getBukkitEntity(); - } else { - return null; - } - } - - @Override - public Component getRichBlockName(BlockType blockType) { - return TranslatableComponent.of(getBlockFromType(blockType).getDescriptionId()); - } - - @Override - public Component getRichItemName(ItemType itemType) { - return TranslatableComponent.of(getItemFromType(itemType).getDescriptionId()); - } - - @Override - public Component getRichItemName(BaseItemStack itemStack) { - return TranslatableComponent.of(CraftItemStack.asNMSCopy(BukkitAdapter.adapt(itemStack)).getDescriptionId()); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader>() { - @Override - public Property load(net.minecraft.world.level.block.state.properties.Property state) throws Exception { - if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { - return new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); - } else if (state instanceof DirectionProperty) { - return new DirectionalProperty(state.getName(), - (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).collect(Collectors.toList())); - } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { - return new EnumProperty(state.getName(), - (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).collect(Collectors.toList())); - } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { - return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); - } else { - throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state.getClass().getSimpleName()); - } - } - }); - - @SuppressWarnings({ "rawtypes" }) - @Override - public Map> getProperties(BlockType blockType) { - Map> properties = new TreeMap<>(); - Block block = getBlockFromType(blockType); - StateDefinition blockStateList = - block.getStateDefinition(); - for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { - Property property = PROPERTY_CACHE.getUnchecked(state); - properties.put(property.getName(), property); - } - return properties; - } - - @Override - public void sendFakeNBT(final Player player, final BlockVector3 pos, final CompoundBinaryTag nbtData) { - - } - - @Override - public void sendFakeOP(Player player) { - ((CraftPlayer) player).getHandle().networkManager.send(new ClientboundEntityEventPacket( - ((CraftPlayer) player).getHandle(), (byte) 28 - )); - } - - @Override - public org.bukkit.inventory.ItemStack adapt(BaseItemStack item) { - ItemStack stack = new ItemStack(Registry.ITEM.get(ResourceLocation.tryParse(item.getType().getId())), item.getAmount()); - stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData()))); - return CraftItemStack.asCraftMirror(stack); - } - - @Override - public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { - final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount()); - weStack.setNbtData(((CompoundTag) toNative(nmsStack.getTag()))); - return weStack; - } - - private final LoadingCache fakePlayers - = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new)); - - @Override - public boolean simulateItemUse(org.bukkit.World world, BlockVector3 position, BaseItem item, Direction face) { - CraftWorld craftWorld = (CraftWorld) world; - ServerLevel worldServer = craftWorld.getHandle(); - ItemStack stack = CraftItemStack.asNMSCopy(BukkitAdapter.adapt(item instanceof BaseItemStack - ? ((BaseItemStack) item) : new BaseItemStack(item.getType(), item.getNbtData(), 1))); - stack.setTag((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData())); - - PaperweightFakePlayer fakePlayer; - try { - fakePlayer = fakePlayers.get(worldServer); - } catch (ExecutionException ignored) { - return false; - } - fakePlayer.setItemInHand(InteractionHand.MAIN_HAND, stack); - fakePlayer.absMoveTo(position.getBlockX(), position.getBlockY(), position.getBlockZ(), - (float) face.toVector().toYaw(), (float) face.toVector().toPitch() - ); - - final BlockPos blockPos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()); - final Vec3 blockVec = Vec3.atLowerCornerOf(blockPos); - final net.minecraft.core.Direction enumFacing = adapt(face); - BlockHitResult rayTrace = new BlockHitResult(blockVec, enumFacing, blockPos, false); - UseOnContext context = new UseOnContext(fakePlayer, InteractionHand.MAIN_HAND, rayTrace); - InteractionResult result = stack.placeItem(context, InteractionHand.MAIN_HAND); - if (result != InteractionResult.SUCCESS) { - if (worldServer - .getBlockState(blockPos) - .use(worldServer, fakePlayer, InteractionHand.MAIN_HAND, rayTrace) - .consumesAction()) { - result = InteractionResult.SUCCESS; - } else { - result = stack.getItem().use(worldServer, fakePlayer, InteractionHand.MAIN_HAND).getResult(); - } - } - - return result == InteractionResult.SUCCESS; - } - - @Override - public boolean canPlaceAt(org.bukkit.World world, BlockVector3 position, BlockState blockState) { - int internalId = BlockStateIdAccess.getBlockStateId(blockState); - net.minecraft.world.level.block.state.BlockState blockData = Block.stateById(internalId); - return blockData.canSurvive( - ((CraftWorld) world).getHandle(), - new BlockPos(position.getX(), position.getY(), position.getZ()) - ); - } - - @Override - public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) { - try { - doRegen(bukkitWorld, region, extent, options); - } catch (Exception e) { - throw new IllegalStateException("Regen failed.", e); - } - - return true; - } - - private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) throws Exception { - Environment env = bukkitWorld.getEnvironment(); - ChunkGenerator gen = bukkitWorld.getGenerator(); - - Path tempDir = Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - LevelStorageSource levelStorage = LevelStorageSource.createDefault(tempDir); - ResourceKey worldDimKey = getWorldDimKey(env); - try (LevelStorageSource.LevelStorageAccess session = levelStorage.createAccess("faweregentempworld", worldDimKey)) { - ServerLevel originalWorld = ((CraftWorld) bukkitWorld).getHandle(); - PrimaryLevelData levelProperties = (PrimaryLevelData) originalWorld.getServer() - .getWorldData().overworldData(); - WorldGenSettings originalOpts = levelProperties.worldGenSettings(); - - long seed = options.getSeed().orElse(originalWorld.getSeed()); - WorldGenSettings newOpts = options.getSeed().isPresent() - ? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed)) - : originalOpts; - - LevelSettings newWorldSettings = new LevelSettings( - "faweregentempworld", - levelProperties.settings.gameType(), - levelProperties.settings.hardcore(), - levelProperties.settings.difficulty(), - levelProperties.settings.allowCommands(), - levelProperties.settings.gameRules(), - levelProperties.settings.getDataPackConfig() - ); - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable()); - - ServerLevel freshWorld = new ServerLevel( - originalWorld.getServer(), - originalWorld.getServer().executor, - session, newWorldData, - originalWorld.dimension(), - originalWorld.dimensionType(), - new NoOpWorldLoadListener(), - newOpts.dimensions().get(worldDimKey).generator(), - originalWorld.isDebug(), - seed, - ImmutableList.of(), - false, - env, gen, - bukkitWorld.getBiomeProvider() - ); - try { - regenForWorld(region, extent, freshWorld, options); - } finally { - freshWorld.getChunkSource().close(false); - } - } finally { - try { - @SuppressWarnings("unchecked") - Map map = (Map) worldsField.get(Bukkit.getServer()); - map.remove("faweregentempworld"); - } catch (IllegalAccessException ignored) { - } - SafeFiles.tryHardToDeleteDir(tempDir); - } - } - - private BiomeType adapt(ServerLevel serverWorld, Biome origBiome) { - ResourceLocation key = serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getKey(origBiome); - if (key == null) { - return null; - } - return BiomeTypes.get(key.toString()); - } - - @SuppressWarnings("unchecked") - private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld, RegenOptions options) throws - WorldEditException { - List> chunkLoadings = submitChunkLoadTasks(region, serverWorld); - BlockableEventLoop executor; - try { - executor = (BlockableEventLoop) mainThreadProcessorField.get(serverWorld.getChunkSource()); - } catch (IllegalAccessException e) { - throw new IllegalStateException("Couldn't get executor for chunk loading.", e); - } - executor.managedBlock(() -> { - // bail out early if a future fails - if (chunkLoadings.stream().anyMatch(ftr -> - ftr.isDone() && Futures.getUnchecked(ftr) == null - )) { - return false; - } - return chunkLoadings.stream().allMatch(CompletableFuture::isDone); - }); - Map chunks = new HashMap<>(); - for (CompletableFuture future : chunkLoadings) { - @Nullable - ChunkAccess chunk = future.getNow(null); - checkState(chunk != null, "Failed to generate a chunk, regen failed."); - chunks.put(chunk.getPos(), chunk); - } - - for (BlockVector3 vec : region) { - BlockPos pos = new BlockPos(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()); - ChunkAccess chunk = chunks.get(new ChunkPos(pos)); - final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(pos); - BlockStateHolder state = ((PaperweightFaweAdapter) WorldEditPlugin - .getInstance() - .getBukkitImplAdapter()).adapt(blockData); - Objects.requireNonNull(state); - BlockEntity blockEntity = chunk.getBlockEntity(pos); - if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - blockEntity.save(tag); - //FAWE start - BinaryTag - state = state.toBaseBlock(((CompoundBinaryTag) toNativeBinary(tag))); - //FAWE end - } - extent.setBlock(vec, state.toBaseBlock()); - if (options.shouldRegenBiomes()) { - ChunkBiomeContainer biomeIndex = chunk.getBiomes(); - if (biomeIndex != null) { - Biome origBiome = biomeIndex.getNoiseBiome(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()); - BiomeType adaptedBiome = adapt(serverWorld, origBiome); - if (adaptedBiome != null) { - extent.setBiome(vec, adaptedBiome); - } - } - } - } - } - - @SuppressWarnings("unchecked") - private List> submitChunkLoadTasks(Region region, ServerLevel serverWorld) { - ServerChunkCache chunkManager = serverWorld.getChunkSource(); - List> chunkLoadings = new ArrayList<>(); - // Pre-gen all the chunks - for (BlockVector2 chunk : region.getChunks()) { - try { - chunkLoadings.add( - ((CompletableFuture>) - getChunkFutureMainThreadMethod.invoke(chunkManager, chunk.getX(), chunk.getZ(), ChunkStatus.FEATURES, true)) - .thenApply(either -> either.left().orElse(null)) - ); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new IllegalStateException("Couldn't load chunk for regen.", e); - } - } - return chunkLoadings; - } - - private ResourceKey getWorldDimKey(Environment env) { - switch (env) { - case NETHER: - return LevelStem.NETHER; - case THE_END: - return LevelStem.END; - case NORMAL: - default: - return LevelStem.OVERWORLD; - } - } - - private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.NEIGHBORS, - SideEffect.LIGHTING, - SideEffect.VALIDATION, - SideEffect.ENTITY_AI, - SideEffect.EVENTS, - SideEffect.UPDATE - ); - - @Override - public Set getSupportedSideEffects() { - return SUPPORTED_SIDE_EFFECTS; - } - - @Override - public boolean clearContainerBlockContents(org.bukkit.World world, BlockVector3 pt) { - ServerLevel originalWorld = ((CraftWorld) world).getHandle(); - - BlockEntity entity = originalWorld.getBlockEntity(new BlockPos(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ())); - if (entity instanceof Clearable) { - ((Clearable) entity).clearContent(); - return true; - } - return false; - } - - // ------------------------------------------------------------------------ - // Code that is less likely to break - // ------------------------------------------------------------------------ - - /** - * Converts from a non-native NMS NBT structure to a native WorldEdit NBT - * structure. - * - * @param foreign non-native NMS NBT structure - * @return native WorldEdit NBT structure - */ - //FAWE start - BinaryTag - @Override - public BinaryTag toNativeBinary(net.minecraft.nbt.Tag foreign) { - if (foreign == null) { - return null; - } - if (foreign instanceof net.minecraft.nbt.CompoundTag) { - Map values = new HashMap<>(); - Set foreignKeys = ((net.minecraft.nbt.CompoundTag) foreign).getAllKeys(); - - for (String str : foreignKeys) { - net.minecraft.nbt.Tag base = ((net.minecraft.nbt.CompoundTag) foreign).get(str); - values.put(str, toNativeBinary(base)); - } - return CompoundBinaryTag.from(values); - } else if (foreign instanceof net.minecraft.nbt.ByteTag) { - return ByteBinaryTag.of(((net.minecraft.nbt.ByteTag) foreign).getAsByte()); - } else if (foreign instanceof net.minecraft.nbt.ByteArrayTag) { - return ByteArrayBinaryTag.of(((net.minecraft.nbt.ByteArrayTag) foreign).getAsByteArray()); - } else if (foreign instanceof net.minecraft.nbt.DoubleTag) { - return DoubleBinaryTag.of(((net.minecraft.nbt.DoubleTag) foreign).getAsDouble()); - } else if (foreign instanceof net.minecraft.nbt.FloatTag) { - return FloatBinaryTag.of(((net.minecraft.nbt.FloatTag) foreign).getAsFloat()); - } else if (foreign instanceof net.minecraft.nbt.IntTag) { - return IntBinaryTag.of(((net.minecraft.nbt.IntTag) foreign).getAsInt()); - } else if (foreign instanceof net.minecraft.nbt.IntArrayTag) { - return IntArrayBinaryTag.of(((net.minecraft.nbt.IntArrayTag) foreign).getAsIntArray()); - } else if (foreign instanceof net.minecraft.nbt.LongArrayTag) { - return LongArrayBinaryTag.of(((net.minecraft.nbt.LongArrayTag) foreign).getAsLongArray()); - } else if (foreign instanceof net.minecraft.nbt.ListTag) { - try { - return toNativeList((net.minecraft.nbt.ListTag) foreign); - } catch (Throwable e) { - LOGGER.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e); - return ListBinaryTag.empty(); - } - } else if (foreign instanceof net.minecraft.nbt.LongTag) { - return LongBinaryTag.of(((net.minecraft.nbt.LongTag) foreign).getAsLong()); - } else if (foreign instanceof net.minecraft.nbt.ShortTag) { - return ShortBinaryTag.of(((net.minecraft.nbt.ShortTag) foreign).getAsShort()); - } else if (foreign instanceof net.minecraft.nbt.StringTag) { - return StringBinaryTag.of(foreign.getAsString()); - } else if (foreign instanceof net.minecraft.nbt.EndTag) { - return EndBinaryTag.get(); - } else { - throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); - } - } - - /** - * Convert a foreign NBT list tag into a native WorldEdit one. - * - * @param foreign the foreign tag - * @return the converted tag - * @throws SecurityException on error - * @throws IllegalArgumentException on error - */ - private ListBinaryTag toNativeList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException { - ListBinaryTag.Builder values = ListBinaryTag.builder(); - - for (net.minecraft.nbt.Tag tag : foreign) { - values.add(toNativeBinary(tag)); - } - - return values.build(); - } - - /** - * Converts a WorldEdit-native NBT structure to a NMS structure. - * - * @param foreign structure to convert - * @return non-native structure - */ - @Override - public net.minecraft.nbt.Tag fromNativeBinary(BinaryTag foreign) { - if (foreign == null) { - return null; - } - if (foreign instanceof CompoundBinaryTag) { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - for (String key : ((CompoundBinaryTag) foreign).keySet()) { - tag.put(key, fromNativeBinary(((CompoundBinaryTag) foreign).get(key))); - } - return tag; - } else if (foreign instanceof ByteBinaryTag) { - return net.minecraft.nbt.ByteTag.valueOf(((ByteBinaryTag) foreign).value()); - } else if (foreign instanceof ByteArrayBinaryTag) { - return new net.minecraft.nbt.ByteArrayTag(((ByteArrayBinaryTag) foreign).value()); - } else if (foreign instanceof DoubleBinaryTag) { - return net.minecraft.nbt.DoubleTag.valueOf(((DoubleBinaryTag) foreign).value()); - } else if (foreign instanceof FloatBinaryTag) { - return net.minecraft.nbt.FloatTag.valueOf(((FloatBinaryTag) foreign).value()); - } else if (foreign instanceof IntBinaryTag) { - return net.minecraft.nbt.IntTag.valueOf(((IntBinaryTag) foreign).value()); - } else if (foreign instanceof IntArrayBinaryTag) { - return new net.minecraft.nbt.IntArrayTag(((IntArrayBinaryTag) foreign).value()); - } else if (foreign instanceof LongArrayBinaryTag) { - return new net.minecraft.nbt.LongArrayTag(((LongArrayBinaryTag) foreign).value()); - } else if (foreign instanceof ListBinaryTag foreignList) { - net.minecraft.nbt.ListTag tag = new net.minecraft.nbt.ListTag(); - for (BinaryTag t : foreignList) { - tag.add(fromNativeBinary(t)); - } - return tag; - } else if (foreign instanceof LongBinaryTag) { - return net.minecraft.nbt.LongTag.valueOf(((LongBinaryTag) foreign).value()); - } else if (foreign instanceof ShortBinaryTag) { - return net.minecraft.nbt.ShortTag.valueOf(((ShortBinaryTag) foreign).value()); - } else if (foreign instanceof StringBinaryTag) { - return net.minecraft.nbt.StringTag.valueOf(((StringBinaryTag) foreign).value()); - } else if (foreign instanceof EndBinaryTag) { - return net.minecraft.nbt.EndTag.INSTANCE; - } else { - throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName()); - } - } - //FAWE end - - @Override - public boolean supportsWatchdog() { - return watchdog != null; - } - - @Override - public void tickWatchdog() { - watchdog.tick(); - } - - private class SpigotWatchdog implements Watchdog { - - private final Field instanceField; - private final Field lastTickField; - - SpigotWatchdog() throws NoSuchFieldException { - Field instanceField = WatchdogThread.class.getDeclaredField("instance"); - instanceField.setAccessible(true); - this.instanceField = instanceField; - - Field lastTickField = WatchdogThread.class.getDeclaredField("lastTick"); - lastTickField.setAccessible(true); - this.lastTickField = lastTickField; - } - - @Override - public void tick() { - try { - WatchdogThread instance = (WatchdogThread) this.instanceField.get(null); - if ((long) lastTickField.get(instance) != 0) { - WatchdogThread.tick(); - } - } catch (IllegalAccessException e) { - LOGGER.log(Level.WARNING, "Failed to tick watchdog", e); - } - } - - } - - private static class MojangWatchdog implements Watchdog { - - private final DedicatedServer server; - private final Field tickField; - - MojangWatchdog(DedicatedServer server) throws NoSuchFieldException { - this.server = server; - Field tickField = MinecraftServer.class.getDeclaredField( - Refraction.pickName("nextTickTime", "ao") - ); - tickField.setAccessible(true); - this.tickField = tickField; - } - - @Override - public void tick() { - try { - tickField.set(server, Util.getMillis()); - } catch (IllegalAccessException ignored) { - } - } - - } - - private static class NoOpWorldLoadListener implements ChunkProgressListener { - - @Override - public void updateSpawnPos(ChunkPos spawnPos) { - } - - @Override - public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { - } - - @Override - public void start() { - } - - @Override - public void stop() { - } - - @Override - public void setChunkRadius(int radius) { - } - - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightDataConverters.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightDataConverters.java deleted file mode 100644 index 6678bdc52..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightDataConverters.java +++ /dev/null @@ -1,2961 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.bukkit.adapter.ext.fawe; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; -import com.mojang.datafixers.DSL.TypeReference; -import com.mojang.datafixers.DataFixer; -import com.mojang.datafixers.DataFixerBuilder; -import com.mojang.datafixers.schemas.Schema; -import com.mojang.serialization.Dynamic; -import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; -import net.minecraft.core.Direction; -import net.minecraft.nbt.NbtOps; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.TextComponent; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.GsonHelper; -import net.minecraft.util.StringUtil; -import net.minecraft.util.datafix.DataFixers; -import net.minecraft.util.datafix.fixes.References; -import net.minecraft.world.item.DyeColor; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.annotation.Nullable; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Random; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Executor; -import java.util.stream.Collectors; - -/** - * Handles converting all Pre 1.13.2 data using the Legacy DataFix System (ported to 1.13.2) - *

- * We register a DFU Fixer per Legacy Data Version and apply the fixes using legacy strategy - * which is safer, faster and cleaner code. - *

- * The pre DFU code did not fail when the Source version was unknown. - *

- * This class also provides util methods for converting compounds to wrap the update call to - * receive the source version in the compound - */ -@SuppressWarnings({"rawtypes", "unchecked"}) -class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer { - - //FAWE start - BinaryTag - @SuppressWarnings("unchecked") - @Override - public T fixUp(FixType type, T original, int srcVer) { - if (type == FixTypes.CHUNK) { - return (T) fixChunk((CompoundBinaryTag) original, srcVer); - } else if (type == FixTypes.BLOCK_ENTITY) { - return (T) fixBlockEntity((CompoundBinaryTag) original, srcVer); - } else if (type == FixTypes.ENTITY) { - return (T) fixEntity((CompoundBinaryTag) original, srcVer); - } else if (type == FixTypes.BLOCK_STATE) { - return (T) fixBlockState((String) original, srcVer); - } else if (type == FixTypes.ITEM_TYPE) { - return (T) fixItemType((String) original, srcVer); - } else if (type == FixTypes.BIOME) { - return (T) fixBiome((String) original, srcVer); - } - return original; - } - - private CompoundBinaryTag fixChunk(CompoundBinaryTag originalChunk, int srcVer) { - net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(originalChunk); - net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.CHUNK, tag, srcVer); - return (CompoundBinaryTag) adapter.toNativeBinary(fixed); - } - - private CompoundBinaryTag fixBlockEntity(CompoundBinaryTag origTileEnt, int srcVer) { - net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origTileEnt); - net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer); - return (CompoundBinaryTag) adapter.toNativeBinary(fixed); - } - - private CompoundBinaryTag fixEntity(CompoundBinaryTag origEnt, int srcVer) { - net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNativeBinary(origEnt); - net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.ENTITY, tag, srcVer); - return (CompoundBinaryTag) adapter.toNativeBinary(fixed); - } - //FAWE end - - private String fixBlockState(String blockState, int srcVer) { - net.minecraft.nbt.CompoundTag stateNBT = stateToNBT(blockState); - Dynamic dynamic = new Dynamic<>(OPS_NBT, stateNBT); - net.minecraft.nbt.CompoundTag fixed = (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update( - References.BLOCK_STATE, - dynamic, - srcVer, - DATA_VERSION - ).getValue(); - return nbtToState(fixed); - } - - private String nbtToState(net.minecraft.nbt.CompoundTag tagCompound) { - StringBuilder sb = new StringBuilder(); - sb.append(tagCompound.getString("Name")); - if (tagCompound.contains("Properties", 10)) { - sb.append('['); - net.minecraft.nbt.CompoundTag props = tagCompound.getCompound("Properties"); - sb.append(props - .getAllKeys() - .stream() - .map(k -> k + "=" + props.getString(k).replace("\"", "")) - .collect(Collectors.joining(","))); - sb.append(']'); - } - return sb.toString(); - } - - private static net.minecraft.nbt.CompoundTag stateToNBT(String blockState) { - int propIdx = blockState.indexOf('['); - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - if (propIdx < 0) { - tag.putString("Name", blockState); - } else { - tag.putString("Name", blockState.substring(0, propIdx)); - net.minecraft.nbt.CompoundTag propTag = new net.minecraft.nbt.CompoundTag(); - String props = blockState.substring(propIdx + 1, blockState.length() - 1); - String[] propArr = props.split(","); - for (String pair : propArr) { - final String[] split = pair.split("="); - propTag.putString(split[0], split[1]); - } - tag.put("Properties", propTag); - } - return tag; - } - - private String fixBiome(String key, int srcVer) { - return fixName(key, srcVer, References.BIOME); - } - - private String fixItemType(String key, int srcVer) { - return fixName(key, srcVer, References.ITEM_NAME); - } - - private static String fixName(String key, int srcVer, TypeReference type) { - return INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, net.minecraft.nbt.StringTag.valueOf(key)), srcVer, DATA_VERSION) - .getValue().getAsString(); - } - - private final PaperweightAdapter adapter; - - private static final NbtOps OPS_NBT = NbtOps.INSTANCE; - private static final int LEGACY_VERSION = 1343; - private static int DATA_VERSION; - static PaperweightDataConverters INSTANCE; - - private final Map> converters = new EnumMap<>(LegacyType.class); - private final Map> inspectors = new EnumMap<>(LegacyType.class); - - // Set on build - private DataFixer fixer; - private static final Map DFU_TO_LEGACY = new HashMap<>(); - - public enum LegacyType { - LEVEL(References.LEVEL), - PLAYER(References.PLAYER), - CHUNK(References.CHUNK), - BLOCK_ENTITY(References.BLOCK_ENTITY), - ENTITY(References.ENTITY), - ITEM_INSTANCE(References.ITEM_STACK), - OPTIONS(References.OPTIONS), - STRUCTURE(References.STRUCTURE); - - private final TypeReference type; - - LegacyType(TypeReference type) { - this.type = type; - DFU_TO_LEGACY.put(type.typeName(), this); - } - - public TypeReference getDFUType() { - return type; - } - } - - PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) { - super(dataVersion); - DATA_VERSION = dataVersion; - INSTANCE = this; - this.adapter = adapter; - registerConverters(); - registerInspectors(); - } - - - // Called after fixers are built and ready for FIXING - @Override - public DataFixer build(final Executor executor) { - return this.fixer = new WrappedDataFixer(DataFixers.getDataFixer()); - } - - @SuppressWarnings("unchecked") - private class WrappedDataFixer implements DataFixer { - - private final DataFixer realFixer; - - WrappedDataFixer(DataFixer realFixer) { - this.realFixer = realFixer; - } - - @Override - public Dynamic update(TypeReference type, Dynamic dynamic, int sourceVer, int targetVer) { - LegacyType legacyType = DFU_TO_LEGACY.get(type.typeName()); - if (sourceVer < LEGACY_VERSION && legacyType != null) { - net.minecraft.nbt.CompoundTag cmp = (net.minecraft.nbt.CompoundTag) dynamic.getValue(); - int desiredVersion = Math.min(targetVer, LEGACY_VERSION); - - cmp = convert(legacyType, cmp, sourceVer, desiredVersion); - sourceVer = desiredVersion; - dynamic = new Dynamic(OPS_NBT, cmp); - } - return realFixer.update(type, dynamic, sourceVer, targetVer); - } - - private net.minecraft.nbt.CompoundTag convert( - LegacyType type, - net.minecraft.nbt.CompoundTag cmp, - int sourceVer, - int desiredVersion - ) { - List converters = PaperweightDataConverters.this.converters.get(type); - if (converters != null && !converters.isEmpty()) { - for (DataConverter converter : converters) { - int dataVersion = converter.getDataVersion(); - if (dataVersion > sourceVer && dataVersion <= desiredVersion) { - cmp = converter.convert(cmp); - } - } - } - - List inspectors = PaperweightDataConverters.this.inspectors.get(type); - if (inspectors != null && !inspectors.isEmpty()) { - for (DataInspector inspector : inspectors) { - cmp = inspector.inspect(cmp, sourceVer, desiredVersion); - } - } - - return cmp; - } - - @Override - public Schema getSchema(int i) { - return realFixer.getSchema(i); - } - - } - - public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp) { - return convert(type.getDFUType(), cmp); - } - - public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) { - return convert(type.getDFUType(), cmp, sourceVer); - } - - public static net.minecraft.nbt.CompoundTag convert( - LegacyType type, - net.minecraft.nbt.CompoundTag cmp, - int sourceVer, - int targetVer - ) { - return convert(type.getDFUType(), cmp, sourceVer, targetVer); - } - - public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp) { - int i = cmp.contains("DataVersion", 99) ? cmp.getInt("DataVersion") : -1; - return convert(type, cmp, i); - } - - public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) { - return convert(type, cmp, sourceVer, DATA_VERSION); - } - - public static net.minecraft.nbt.CompoundTag convert( - TypeReference type, - net.minecraft.nbt.CompoundTag cmp, - int sourceVer, - int targetVer - ) { - if (sourceVer >= targetVer) { - return cmp; - } - return (net.minecraft.nbt.CompoundTag) INSTANCE.fixer - .update(type, new Dynamic<>(OPS_NBT, cmp), sourceVer, targetVer) - .getValue(); - } - - - public interface DataInspector { - - net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer); - - } - - public interface DataConverter { - - int getDataVersion(); - - net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp); - - } - - - private void registerInspector(LegacyType type, DataInspector inspector) { - this.inspectors.computeIfAbsent(type, k -> new ArrayList<>()).add(inspector); - } - - private void registerConverter(LegacyType type, DataConverter converter) { - int version = converter.getDataVersion(); - - List list = this.converters.computeIfAbsent(type, k -> new ArrayList<>()); - if (!list.isEmpty() && list.get(list.size() - 1).getDataVersion() > version) { - for (int j = 0; j < list.size(); ++j) { - if (list.get(j).getDataVersion() > version) { - list.add(j, converter); - break; - } - } - } else { - list.add(converter); - } - } - - private void registerInspectors() { - registerEntityItemList("EntityHorseDonkey", "SaddleItem", "Items"); - registerEntityItemList("EntityHorseMule", "Items"); - registerEntityItemList("EntityMinecartChest", "Items"); - registerEntityItemList("EntityMinecartHopper", "Items"); - registerEntityItemList("EntityVillager", "Inventory"); - registerEntityItemListEquipment("EntityArmorStand"); - registerEntityItemListEquipment("EntityBat"); - registerEntityItemListEquipment("EntityBlaze"); - registerEntityItemListEquipment("EntityCaveSpider"); - registerEntityItemListEquipment("EntityChicken"); - registerEntityItemListEquipment("EntityCow"); - registerEntityItemListEquipment("EntityCreeper"); - registerEntityItemListEquipment("EntityEnderDragon"); - registerEntityItemListEquipment("EntityEnderman"); - registerEntityItemListEquipment("EntityEndermite"); - registerEntityItemListEquipment("EntityEvoker"); - registerEntityItemListEquipment("EntityGhast"); - registerEntityItemListEquipment("EntityGiantZombie"); - registerEntityItemListEquipment("EntityGuardian"); - registerEntityItemListEquipment("EntityGuardianElder"); - registerEntityItemListEquipment("EntityHorse"); - registerEntityItemListEquipment("EntityHorseDonkey"); - registerEntityItemListEquipment("EntityHorseMule"); - registerEntityItemListEquipment("EntityHorseSkeleton"); - registerEntityItemListEquipment("EntityHorseZombie"); - registerEntityItemListEquipment("EntityIronGolem"); - registerEntityItemListEquipment("EntityMagmaCube"); - registerEntityItemListEquipment("EntityMushroomCow"); - registerEntityItemListEquipment("EntityOcelot"); - registerEntityItemListEquipment("EntityPig"); - registerEntityItemListEquipment("EntityPigZombie"); - registerEntityItemListEquipment("EntityRabbit"); - registerEntityItemListEquipment("EntitySheep"); - registerEntityItemListEquipment("EntityShulker"); - registerEntityItemListEquipment("EntitySilverfish"); - registerEntityItemListEquipment("EntitySkeleton"); - registerEntityItemListEquipment("EntitySkeletonStray"); - registerEntityItemListEquipment("EntitySkeletonWither"); - registerEntityItemListEquipment("EntitySlime"); - registerEntityItemListEquipment("EntitySnowman"); - registerEntityItemListEquipment("EntitySpider"); - registerEntityItemListEquipment("EntitySquid"); - registerEntityItemListEquipment("EntityVex"); - registerEntityItemListEquipment("EntityVillager"); - registerEntityItemListEquipment("EntityVindicator"); - registerEntityItemListEquipment("EntityWitch"); - registerEntityItemListEquipment("EntityWither"); - registerEntityItemListEquipment("EntityWolf"); - registerEntityItemListEquipment("EntityZombie"); - registerEntityItemListEquipment("EntityZombieHusk"); - registerEntityItemListEquipment("EntityZombieVillager"); - registerEntityItemSingle("EntityFireworks", "FireworksItem"); - registerEntityItemSingle("EntityHorse", "ArmorItem"); - registerEntityItemSingle("EntityHorse", "SaddleItem"); - registerEntityItemSingle("EntityHorseMule", "SaddleItem"); - registerEntityItemSingle("EntityHorseSkeleton", "SaddleItem"); - registerEntityItemSingle("EntityHorseZombie", "SaddleItem"); - registerEntityItemSingle("EntityItem", "Item"); - registerEntityItemSingle("EntityItemFrame", "Item"); - registerEntityItemSingle("EntityPotion", "Potion"); - - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItem("TileEntityRecordPlayer", "RecordItem")); - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityBrewingStand", "Items")); - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityChest", "Items")); - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDispenser", "Items")); - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDropper", "Items")); - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityFurnace", "Items")); - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityHopper", "Items")); - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityShulkerBox", "Items")); - registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorMobSpawnerMobs()); - registerInspector(LegacyType.CHUNK, new DataInspectorChunks()); - registerInspector(LegacyType.ENTITY, new DataInspectorCommandBlock()); - registerInspector(LegacyType.ENTITY, new DataInspectorEntityPassengers()); - registerInspector(LegacyType.ENTITY, new DataInspectorMobSpawnerMinecart()); - registerInspector(LegacyType.ENTITY, new DataInspectorVillagers()); - registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorBlockEntity()); - registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorEntity()); - registerInspector(LegacyType.LEVEL, new DataInspectorLevelPlayer()); - registerInspector(LegacyType.PLAYER, new DataInspectorPlayer()); - registerInspector(LegacyType.PLAYER, new DataInspectorPlayerVehicle()); - registerInspector(LegacyType.STRUCTURE, new DataInspectorStructure()); - } - - private void registerConverters() { - registerConverter(LegacyType.ENTITY, new DataConverterEquipment()); - registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterSignText()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterMaterialId()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionId()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterSpawnEgg()); - registerConverter(LegacyType.ENTITY, new DataConverterMinecart()); - registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterMobSpawner()); - registerConverter(LegacyType.ENTITY, new DataConverterUUID()); - registerConverter(LegacyType.ENTITY, new DataConverterHealth()); - registerConverter(LegacyType.ENTITY, new DataConverterSaddle()); - registerConverter(LegacyType.ENTITY, new DataConverterHanging()); - registerConverter(LegacyType.ENTITY, new DataConverterDropChances()); - registerConverter(LegacyType.ENTITY, new DataConverterRiding()); - registerConverter(LegacyType.ENTITY, new DataConverterArmorStand()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBook()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterCookedFish()); - registerConverter(LegacyType.ENTITY, new DataConverterZombie()); - registerConverter(LegacyType.OPTIONS, new DataConverterVBO()); - registerConverter(LegacyType.ENTITY, new DataConverterGuardian()); - registerConverter(LegacyType.ENTITY, new DataConverterSkeleton()); - registerConverter(LegacyType.ENTITY, new DataConverterZombieType()); - registerConverter(LegacyType.ENTITY, new DataConverterHorse()); - registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterTileEntity()); - registerConverter(LegacyType.ENTITY, new DataConverterEntity()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBanner()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionWater()); - registerConverter(LegacyType.ENTITY, new DataConverterShulker()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterShulkerBoxItem()); - registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterShulkerBoxBlock()); - registerConverter(LegacyType.OPTIONS, new DataConverterLang()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterTotem()); - registerConverter(LegacyType.CHUNK, new DataConverterBedBlock()); - registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBedItem()); - } - - private void registerEntityItemList(String type, String... keys) { - registerInspector(LegacyType.ENTITY, new DataInspectorItemList(type, keys)); - } - - private void registerEntityItemSingle(String type, String key) { - registerInspector(LegacyType.ENTITY, new DataInspectorItem(type, key)); - } - - private void registerEntityItemListEquipment(String type) { - registerEntityItemList(type, "ArmorItems", "HandItems"); - } - - private static final Map OLD_ID_TO_KEY_MAP = new HashMap<>(); - - static { - final Map map = OLD_ID_TO_KEY_MAP; - map.put("EntityItem", new ResourceLocation("item")); - map.put("EntityExperienceOrb", new ResourceLocation("xp_orb")); - map.put("EntityAreaEffectCloud", new ResourceLocation("area_effect_cloud")); - map.put("EntityGuardianElder", new ResourceLocation("elder_guardian")); - map.put("EntitySkeletonWither", new ResourceLocation("wither_skeleton")); - map.put("EntitySkeletonStray", new ResourceLocation("stray")); - map.put("EntityEgg", new ResourceLocation("egg")); - map.put("EntityLeash", new ResourceLocation("leash_knot")); - map.put("EntityPainting", new ResourceLocation("painting")); - map.put("EntityTippedArrow", new ResourceLocation("arrow")); - map.put("EntitySnowball", new ResourceLocation("snowball")); - map.put("EntityLargeFireball", new ResourceLocation("fireball")); - map.put("EntitySmallFireball", new ResourceLocation("small_fireball")); - map.put("EntityEnderPearl", new ResourceLocation("ender_pearl")); - map.put("EntityEnderSignal", new ResourceLocation("eye_of_ender_signal")); - map.put("EntityPotion", new ResourceLocation("potion")); - map.put("EntityThrownExpBottle", new ResourceLocation("xp_bottle")); - map.put("EntityItemFrame", new ResourceLocation("item_frame")); - map.put("EntityWitherSkull", new ResourceLocation("wither_skull")); - map.put("EntityTNTPrimed", new ResourceLocation("tnt")); - map.put("EntityFallingBlock", new ResourceLocation("falling_block")); - map.put("EntityFireworks", new ResourceLocation("fireworks_rocket")); - map.put("EntityZombieHusk", new ResourceLocation("husk")); - map.put("EntitySpectralArrow", new ResourceLocation("spectral_arrow")); - map.put("EntityShulkerBullet", new ResourceLocation("shulker_bullet")); - map.put("EntityDragonFireball", new ResourceLocation("dragon_fireball")); - map.put("EntityZombieVillager", new ResourceLocation("zombie_villager")); - map.put("EntityHorseSkeleton", new ResourceLocation("skeleton_horse")); - map.put("EntityHorseZombie", new ResourceLocation("zombie_horse")); - map.put("EntityArmorStand", new ResourceLocation("armor_stand")); - map.put("EntityHorseDonkey", new ResourceLocation("donkey")); - map.put("EntityHorseMule", new ResourceLocation("mule")); - map.put("EntityEvokerFangs", new ResourceLocation("evocation_fangs")); - map.put("EntityEvoker", new ResourceLocation("evocation_illager")); - map.put("EntityVex", new ResourceLocation("vex")); - map.put("EntityVindicator", new ResourceLocation("vindication_illager")); - map.put("EntityIllagerIllusioner", new ResourceLocation("illusion_illager")); - map.put("EntityMinecartCommandBlock", new ResourceLocation("commandblock_minecart")); - map.put("EntityBoat", new ResourceLocation("boat")); - map.put("EntityMinecartRideable", new ResourceLocation("minecart")); - map.put("EntityMinecartChest", new ResourceLocation("chest_minecart")); - map.put("EntityMinecartFurnace", new ResourceLocation("furnace_minecart")); - map.put("EntityMinecartTNT", new ResourceLocation("tnt_minecart")); - map.put("EntityMinecartHopper", new ResourceLocation("hopper_minecart")); - map.put("EntityMinecartMobSpawner", new ResourceLocation("spawner_minecart")); - map.put("EntityCreeper", new ResourceLocation("creeper")); - map.put("EntitySkeleton", new ResourceLocation("skeleton")); - map.put("EntitySpider", new ResourceLocation("spider")); - map.put("EntityGiantZombie", new ResourceLocation("giant")); - map.put("EntityZombie", new ResourceLocation("zombie")); - map.put("EntitySlime", new ResourceLocation("slime")); - map.put("EntityGhast", new ResourceLocation("ghast")); - map.put("EntityPigZombie", new ResourceLocation("zombie_pigman")); - map.put("EntityEnderman", new ResourceLocation("enderman")); - map.put("EntityCaveSpider", new ResourceLocation("cave_spider")); - map.put("EntitySilverfish", new ResourceLocation("silverfish")); - map.put("EntityBlaze", new ResourceLocation("blaze")); - map.put("EntityMagmaCube", new ResourceLocation("magma_cube")); - map.put("EntityEnderDragon", new ResourceLocation("ender_dragon")); - map.put("EntityWither", new ResourceLocation("wither")); - map.put("EntityBat", new ResourceLocation("bat")); - map.put("EntityWitch", new ResourceLocation("witch")); - map.put("EntityEndermite", new ResourceLocation("endermite")); - map.put("EntityGuardian", new ResourceLocation("guardian")); - map.put("EntityShulker", new ResourceLocation("shulker")); - map.put("EntityPig", new ResourceLocation("pig")); - map.put("EntitySheep", new ResourceLocation("sheep")); - map.put("EntityCow", new ResourceLocation("cow")); - map.put("EntityChicken", new ResourceLocation("chicken")); - map.put("EntitySquid", new ResourceLocation("squid")); - map.put("EntityWolf", new ResourceLocation("wolf")); - map.put("EntityMushroomCow", new ResourceLocation("mooshroom")); - map.put("EntitySnowman", new ResourceLocation("snowman")); - map.put("EntityOcelot", new ResourceLocation("ocelot")); - map.put("EntityIronGolem", new ResourceLocation("villager_golem")); - map.put("EntityHorse", new ResourceLocation("horse")); - map.put("EntityRabbit", new ResourceLocation("rabbit")); - map.put("EntityPolarBear", new ResourceLocation("polar_bear")); - map.put("EntityLlama", new ResourceLocation("llama")); - map.put("EntityLlamaSpit", new ResourceLocation("llama_spit")); - map.put("EntityParrot", new ResourceLocation("parrot")); - map.put("EntityVillager", new ResourceLocation("villager")); - map.put("EntityEnderCrystal", new ResourceLocation("ender_crystal")); - map.put("TileEntityFurnace", new ResourceLocation("furnace")); - map.put("TileEntityChest", new ResourceLocation("chest")); - map.put("TileEntityEnderChest", new ResourceLocation("ender_chest")); - map.put("TileEntityRecordPlayer", new ResourceLocation("jukebox")); - map.put("TileEntityDispenser", new ResourceLocation("dispenser")); - map.put("TileEntityDropper", new ResourceLocation("dropper")); - map.put("TileEntitySign", new ResourceLocation("sign")); - map.put("TileEntityMobSpawner", new ResourceLocation("mob_spawner")); - map.put("TileEntityNote", new ResourceLocation("noteblock")); - map.put("TileEntityPiston", new ResourceLocation("piston")); - map.put("TileEntityBrewingStand", new ResourceLocation("brewing_stand")); - map.put("TileEntityEnchantTable", new ResourceLocation("enchanting_table")); - map.put("TileEntityEnderPortal", new ResourceLocation("end_portal")); - map.put("TileEntityBeacon", new ResourceLocation("beacon")); - map.put("TileEntitySkull", new ResourceLocation("skull")); - map.put("TileEntityLightDetector", new ResourceLocation("daylight_detector")); - map.put("TileEntityHopper", new ResourceLocation("hopper")); - map.put("TileEntityComparator", new ResourceLocation("comparator")); - map.put("TileEntityFlowerPot", new ResourceLocation("flower_pot")); - map.put("TileEntityBanner", new ResourceLocation("banner")); - map.put("TileEntityStructure", new ResourceLocation("structure_block")); - map.put("TileEntityEndGateway", new ResourceLocation("end_gateway")); - map.put("TileEntityCommand", new ResourceLocation("command_block")); - map.put("TileEntityShulkerBox", new ResourceLocation("shulker_box")); - map.put("TileEntityBed", new ResourceLocation("bed")); - } - - private static ResourceLocation getKey(String type) { - final ResourceLocation key = OLD_ID_TO_KEY_MAP.get(type); - if (key == null) { - throw new IllegalArgumentException("Unknown mapping for " + type); - } - return key; - } - - private static void convertCompound( - LegacyType type, - net.minecraft.nbt.CompoundTag cmp, - String key, - int sourceVer, - int targetVer - ) { - cmp.put(key, convert(type, cmp.getCompound(key), sourceVer, targetVer)); - } - - private static void convertItem(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) { - if (nbttagcompound.contains(key, 10)) { - convertCompound(LegacyType.ITEM_INSTANCE, nbttagcompound, key, sourceVer, targetVer); - } - } - - private static void convertItems(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) { - if (nbttagcompound.contains(key, 9)) { - net.minecraft.nbt.ListTag nbttaglist = nbttagcompound.getList(key, 10); - - for (int j = 0; j < nbttaglist.size(); ++j) { - nbttaglist.set(j, convert(LegacyType.ITEM_INSTANCE, nbttaglist.getCompound(j), sourceVer, targetVer)); - } - } - - } - - private static class DataConverterEquipment implements DataConverter { - - DataConverterEquipment() { - } - - public int getDataVersion() { - return 100; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - net.minecraft.nbt.ListTag nbttaglist = cmp.getList("Equipment", 10); - net.minecraft.nbt.ListTag nbttaglist1; - - if (!nbttaglist.isEmpty() && !cmp.contains("HandItems", 10)) { - nbttaglist1 = new net.minecraft.nbt.ListTag(); - nbttaglist1.add(nbttaglist.get(0)); - nbttaglist1.add(new net.minecraft.nbt.CompoundTag()); - cmp.put("HandItems", nbttaglist1); - } - - if (nbttaglist.size() > 1 && !cmp.contains("ArmorItem", 10)) { - nbttaglist1 = new net.minecraft.nbt.ListTag(); - nbttaglist1.add(nbttaglist.get(1)); - nbttaglist1.add(nbttaglist.get(2)); - nbttaglist1.add(nbttaglist.get(3)); - nbttaglist1.add(nbttaglist.get(4)); - cmp.put("ArmorItems", nbttaglist1); - } - - cmp.remove("Equipment"); - if (cmp.contains("DropChances", 9)) { - nbttaglist1 = cmp.getList("DropChances", 5); - net.minecraft.nbt.ListTag nbttaglist2; - - if (!cmp.contains("HandDropChances", 10)) { - nbttaglist2 = new net.minecraft.nbt.ListTag(); - nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(0))); - nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(0.0F)); - cmp.put("HandDropChances", nbttaglist2); - } - - if (!cmp.contains("ArmorDropChances", 10)) { - nbttaglist2 = new net.minecraft.nbt.ListTag(); - nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(1))); - nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(2))); - nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(3))); - nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(4))); - cmp.put("ArmorDropChances", nbttaglist2); - } - - cmp.remove("DropChances"); - } - - return cmp; - } - - } - - private static class DataInspectorBlockEntity implements DataInspector { - - private static final Map b = Maps.newHashMap(); - private static final Map c = Maps.newHashMap(); - - DataInspectorBlockEntity() { - } - - @Nullable - private static String convertEntityId(int i, String s) { - String key = new ResourceLocation(s).toString(); - if (i < 515 && DataInspectorBlockEntity.b.containsKey(key)) { - return DataInspectorBlockEntity.b.get(key); - } else { - return DataInspectorBlockEntity.c.get(key); - } - } - - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (!cmp.contains("tag", 10)) { - return cmp; - } else { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); - - if (nbttagcompound1.contains("BlockEntityTag", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); - String s = cmp.getString("id"); - String s1 = convertEntityId(sourceVer, s); - boolean flag; - - if (s1 == null) { - // CraftBukkit - Remove unnecessary warning (occurs when deserializing a Shulker Box item) - // DataInspectorBlockEntity.a.warn("Unable to resolve BlockEntity for ItemInstance: {}", s); - flag = false; - } else { - flag = !nbttagcompound2.contains("id"); - nbttagcompound2.putString("id", s1); - } - - convert(LegacyType.BLOCK_ENTITY, nbttagcompound2, sourceVer, targetVer); - if (flag) { - nbttagcompound2.remove("id"); - } - } - - return cmp; - } - } - - static { - Map map = DataInspectorBlockEntity.b; - - map.put("minecraft:furnace", "Furnace"); - map.put("minecraft:lit_furnace", "Furnace"); - map.put("minecraft:chest", "Chest"); - map.put("minecraft:trapped_chest", "Chest"); - map.put("minecraft:ender_chest", "EnderChest"); - map.put("minecraft:jukebox", "RecordPlayer"); - map.put("minecraft:dispenser", "Trap"); - map.put("minecraft:dropper", "Dropper"); - map.put("minecraft:sign", "Sign"); - map.put("minecraft:mob_spawner", "MobSpawner"); - map.put("minecraft:noteblock", "Music"); - map.put("minecraft:brewing_stand", "Cauldron"); - map.put("minecraft:enhanting_table", "EnchantTable"); - map.put("minecraft:command_block", "CommandBlock"); - map.put("minecraft:beacon", "Beacon"); - map.put("minecraft:skull", "Skull"); - map.put("minecraft:daylight_detector", "DLDetector"); - map.put("minecraft:hopper", "Hopper"); - map.put("minecraft:banner", "Banner"); - map.put("minecraft:flower_pot", "FlowerPot"); - map.put("minecraft:repeating_command_block", "CommandBlock"); - map.put("minecraft:chain_command_block", "CommandBlock"); - map.put("minecraft:standing_sign", "Sign"); - map.put("minecraft:wall_sign", "Sign"); - map.put("minecraft:piston_head", "Piston"); - map.put("minecraft:daylight_detector_inverted", "DLDetector"); - map.put("minecraft:unpowered_comparator", "Comparator"); - map.put("minecraft:powered_comparator", "Comparator"); - map.put("minecraft:wall_banner", "Banner"); - map.put("minecraft:standing_banner", "Banner"); - map.put("minecraft:structure_block", "Structure"); - map.put("minecraft:end_portal", "Airportal"); - map.put("minecraft:end_gateway", "EndGateway"); - map.put("minecraft:shield", "Shield"); - map = DataInspectorBlockEntity.c; - map.put("minecraft:furnace", "minecraft:furnace"); - map.put("minecraft:lit_furnace", "minecraft:furnace"); - map.put("minecraft:chest", "minecraft:chest"); - map.put("minecraft:trapped_chest", "minecraft:chest"); - map.put("minecraft:ender_chest", "minecraft:enderchest"); - map.put("minecraft:jukebox", "minecraft:jukebox"); - map.put("minecraft:dispenser", "minecraft:dispenser"); - map.put("minecraft:dropper", "minecraft:dropper"); - map.put("minecraft:sign", "minecraft:sign"); - map.put("minecraft:mob_spawner", "minecraft:mob_spawner"); - map.put("minecraft:noteblock", "minecraft:noteblock"); - map.put("minecraft:brewing_stand", "minecraft:brewing_stand"); - map.put("minecraft:enhanting_table", "minecraft:enchanting_table"); - map.put("minecraft:command_block", "minecraft:command_block"); - map.put("minecraft:beacon", "minecraft:beacon"); - map.put("minecraft:skull", "minecraft:skull"); - map.put("minecraft:daylight_detector", "minecraft:daylight_detector"); - map.put("minecraft:hopper", "minecraft:hopper"); - map.put("minecraft:banner", "minecraft:banner"); - map.put("minecraft:flower_pot", "minecraft:flower_pot"); - map.put("minecraft:repeating_command_block", "minecraft:command_block"); - map.put("minecraft:chain_command_block", "minecraft:command_block"); - map.put("minecraft:shulker_box", "minecraft:shulker_box"); - map.put("minecraft:white_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:orange_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:magenta_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:light_blue_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:yellow_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:lime_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:pink_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:gray_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:silver_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:cyan_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:purple_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:blue_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:brown_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:green_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:red_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:black_shulker_box", "minecraft:shulker_box"); - map.put("minecraft:bed", "minecraft:bed"); - map.put("minecraft:standing_sign", "minecraft:sign"); - map.put("minecraft:wall_sign", "minecraft:sign"); - map.put("minecraft:piston_head", "minecraft:piston"); - map.put("minecraft:daylight_detector_inverted", "minecraft:daylight_detector"); - map.put("minecraft:unpowered_comparator", "minecraft:comparator"); - map.put("minecraft:powered_comparator", "minecraft:comparator"); - map.put("minecraft:wall_banner", "minecraft:banner"); - map.put("minecraft:standing_banner", "minecraft:banner"); - map.put("minecraft:structure_block", "minecraft:structure_block"); - map.put("minecraft:end_portal", "minecraft:end_portal"); - map.put("minecraft:end_gateway", "minecraft:end_gateway"); - map.put("minecraft:shield", "minecraft:shield"); - } - } - - private static class DataInspectorEntity implements DataInspector { - - private static final Logger a = LogManager.getLogger(PaperweightDataConverters.class); - - DataInspectorEntity() { - } - - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); - - if (nbttagcompound1.contains("EntityTag", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag"); - String s = cmp.getString("id"); - String s1; - - if ("minecraft:armor_stand".equals(s)) { - s1 = sourceVer < 515 ? "ArmorStand" : "minecraft:armor_stand"; - } else { - if (!"minecraft:spawn_egg".equals(s)) { - return cmp; - } - - s1 = nbttagcompound2.getString("id"); - } - - boolean flag; - - flag = !nbttagcompound2.contains("id", 8); - nbttagcompound2.putString("id", s1); - - convert(LegacyType.ENTITY, nbttagcompound2, sourceVer, targetVer); - if (flag) { - nbttagcompound2.remove("id"); - } - } - - return cmp; - } - - } - - - private abstract static class DataInspectorTagged implements DataInspector { - - private final ResourceLocation key; - - DataInspectorTagged(String type) { - this.key = getKey(type); - } - - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (this.key.equals(new ResourceLocation(cmp.getString("id")))) { - cmp = this.inspectChecked(cmp, sourceVer, targetVer); - } - - return cmp; - } - - abstract net.minecraft.nbt.CompoundTag inspectChecked( - net.minecraft.nbt.CompoundTag nbttagcompound, - int sourceVer, - int targetVer - ); - - } - - private static class DataInspectorItemList extends DataInspectorTagged { - - private final String[] keys; - - DataInspectorItemList(String oclass, String... astring) { - super(oclass); - this.keys = astring; - } - - net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) { - for (String s : this.keys) { - PaperweightDataConverters.convertItems(nbttagcompound, s, sourceVer, targetVer); - } - - return nbttagcompound; - } - - } - - private static class DataInspectorItem extends DataInspectorTagged { - - private final String[] keys; - - DataInspectorItem(String oclass, String... astring) { - super(oclass); - this.keys = astring; - } - - net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) { - for (String key : this.keys) { - PaperweightDataConverters.convertItem(nbttagcompound, key, sourceVer, targetVer); - } - - return nbttagcompound; - } - - } - - private static class DataConverterMaterialId implements DataConverter { - - private static final String[] materials = new String[2268]; - - DataConverterMaterialId() { - } - - public int getDataVersion() { - return 102; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if (cmp.contains("id", 99)) { - short short0 = cmp.getShort("id"); - - if (short0 > 0 && short0 < materials.length && materials[short0] != null) { - cmp.putString("id", materials[short0]); - } - } - - return cmp; - } - - static { - materials[1] = "minecraft:stone"; - materials[2] = "minecraft:grass"; - materials[3] = "minecraft:dirt"; - materials[4] = "minecraft:cobblestone"; - materials[5] = "minecraft:planks"; - materials[6] = "minecraft:sapling"; - materials[7] = "minecraft:bedrock"; - materials[8] = "minecraft:flowing_water"; - materials[9] = "minecraft:water"; - materials[10] = "minecraft:flowing_lava"; - materials[11] = "minecraft:lava"; - materials[12] = "minecraft:sand"; - materials[13] = "minecraft:gravel"; - materials[14] = "minecraft:gold_ore"; - materials[15] = "minecraft:iron_ore"; - materials[16] = "minecraft:coal_ore"; - materials[17] = "minecraft:log"; - materials[18] = "minecraft:leaves"; - materials[19] = "minecraft:sponge"; - materials[20] = "minecraft:glass"; - materials[21] = "minecraft:lapis_ore"; - materials[22] = "minecraft:lapis_block"; - materials[23] = "minecraft:dispenser"; - materials[24] = "minecraft:sandstone"; - materials[25] = "minecraft:noteblock"; - materials[27] = "minecraft:golden_rail"; - materials[28] = "minecraft:detector_rail"; - materials[29] = "minecraft:sticky_piston"; - materials[30] = "minecraft:web"; - materials[31] = "minecraft:tallgrass"; - materials[32] = "minecraft:deadbush"; - materials[33] = "minecraft:piston"; - materials[35] = "minecraft:wool"; - materials[37] = "minecraft:yellow_flower"; - materials[38] = "minecraft:red_flower"; - materials[39] = "minecraft:brown_mushroom"; - materials[40] = "minecraft:red_mushroom"; - materials[41] = "minecraft:gold_block"; - materials[42] = "minecraft:iron_block"; - materials[43] = "minecraft:double_stone_slab"; - materials[44] = "minecraft:stone_slab"; - materials[45] = "minecraft:brick_block"; - materials[46] = "minecraft:tnt"; - materials[47] = "minecraft:bookshelf"; - materials[48] = "minecraft:mossy_cobblestone"; - materials[49] = "minecraft:obsidian"; - materials[50] = "minecraft:torch"; - materials[51] = "minecraft:fire"; - materials[52] = "minecraft:mob_spawner"; - materials[53] = "minecraft:oak_stairs"; - materials[54] = "minecraft:chest"; - materials[56] = "minecraft:diamond_ore"; - materials[57] = "minecraft:diamond_block"; - materials[58] = "minecraft:crafting_table"; - materials[60] = "minecraft:farmland"; - materials[61] = "minecraft:furnace"; - materials[62] = "minecraft:lit_furnace"; - materials[65] = "minecraft:ladder"; - materials[66] = "minecraft:rail"; - materials[67] = "minecraft:stone_stairs"; - materials[69] = "minecraft:lever"; - materials[70] = "minecraft:stone_pressure_plate"; - materials[72] = "minecraft:wooden_pressure_plate"; - materials[73] = "minecraft:redstone_ore"; - materials[76] = "minecraft:redstone_torch"; - materials[77] = "minecraft:stone_button"; - materials[78] = "minecraft:snow_layer"; - materials[79] = "minecraft:ice"; - materials[80] = "minecraft:snow"; - materials[81] = "minecraft:cactus"; - materials[82] = "minecraft:clay"; - materials[84] = "minecraft:jukebox"; - materials[85] = "minecraft:fence"; - materials[86] = "minecraft:pumpkin"; - materials[87] = "minecraft:netherrack"; - materials[88] = "minecraft:soul_sand"; - materials[89] = "minecraft:glowstone"; - materials[90] = "minecraft:portal"; - materials[91] = "minecraft:lit_pumpkin"; - materials[95] = "minecraft:stained_glass"; - materials[96] = "minecraft:trapdoor"; - materials[97] = "minecraft:monster_egg"; - materials[98] = "minecraft:stonebrick"; - materials[99] = "minecraft:brown_mushroom_block"; - materials[100] = "minecraft:red_mushroom_block"; - materials[101] = "minecraft:iron_bars"; - materials[102] = "minecraft:glass_pane"; - materials[103] = "minecraft:melon_block"; - materials[106] = "minecraft:vine"; - materials[107] = "minecraft:fence_gate"; - materials[108] = "minecraft:brick_stairs"; - materials[109] = "minecraft:stone_brick_stairs"; - materials[110] = "minecraft:mycelium"; - materials[111] = "minecraft:waterlily"; - materials[112] = "minecraft:nether_brick"; - materials[113] = "minecraft:nether_brick_fence"; - materials[114] = "minecraft:nether_brick_stairs"; - materials[116] = "minecraft:enchanting_table"; - materials[119] = "minecraft:end_portal"; - materials[120] = "minecraft:end_portal_frame"; - materials[121] = "minecraft:end_stone"; - materials[122] = "minecraft:dragon_egg"; - materials[123] = "minecraft:redstone_lamp"; - materials[125] = "minecraft:double_wooden_slab"; - materials[126] = "minecraft:wooden_slab"; - materials[127] = "minecraft:cocoa"; - materials[128] = "minecraft:sandstone_stairs"; - materials[129] = "minecraft:emerald_ore"; - materials[130] = "minecraft:ender_chest"; - materials[131] = "minecraft:tripwire_hook"; - materials[133] = "minecraft:emerald_block"; - materials[134] = "minecraft:spruce_stairs"; - materials[135] = "minecraft:birch_stairs"; - materials[136] = "minecraft:jungle_stairs"; - materials[137] = "minecraft:command_block"; - materials[138] = "minecraft:beacon"; - materials[139] = "minecraft:cobblestone_wall"; - materials[141] = "minecraft:carrots"; - materials[142] = "minecraft:potatoes"; - materials[143] = "minecraft:wooden_button"; - materials[145] = "minecraft:anvil"; - materials[146] = "minecraft:trapped_chest"; - materials[147] = "minecraft:light_weighted_pressure_plate"; - materials[148] = "minecraft:heavy_weighted_pressure_plate"; - materials[151] = "minecraft:daylight_detector"; - materials[152] = "minecraft:redstone_block"; - materials[153] = "minecraft:quartz_ore"; - materials[154] = "minecraft:hopper"; - materials[155] = "minecraft:quartz_block"; - materials[156] = "minecraft:quartz_stairs"; - materials[157] = "minecraft:activator_rail"; - materials[158] = "minecraft:dropper"; - materials[159] = "minecraft:stained_hardened_clay"; - materials[160] = "minecraft:stained_glass_pane"; - materials[161] = "minecraft:leaves2"; - materials[162] = "minecraft:log2"; - materials[163] = "minecraft:acacia_stairs"; - materials[164] = "minecraft:dark_oak_stairs"; - materials[170] = "minecraft:hay_block"; - materials[171] = "minecraft:carpet"; - materials[172] = "minecraft:hardened_clay"; - materials[173] = "minecraft:coal_block"; - materials[174] = "minecraft:packed_ice"; - materials[175] = "minecraft:double_plant"; - materials[256] = "minecraft:iron_shovel"; - materials[257] = "minecraft:iron_pickaxe"; - materials[258] = "minecraft:iron_axe"; - materials[259] = "minecraft:flint_and_steel"; - materials[260] = "minecraft:apple"; - materials[261] = "minecraft:bow"; - materials[262] = "minecraft:arrow"; - materials[263] = "minecraft:coal"; - materials[264] = "minecraft:diamond"; - materials[265] = "minecraft:iron_ingot"; - materials[266] = "minecraft:gold_ingot"; - materials[267] = "minecraft:iron_sword"; - materials[268] = "minecraft:wooden_sword"; - materials[269] = "minecraft:wooden_shovel"; - materials[270] = "minecraft:wooden_pickaxe"; - materials[271] = "minecraft:wooden_axe"; - materials[272] = "minecraft:stone_sword"; - materials[273] = "minecraft:stone_shovel"; - materials[274] = "minecraft:stone_pickaxe"; - materials[275] = "minecraft:stone_axe"; - materials[276] = "minecraft:diamond_sword"; - materials[277] = "minecraft:diamond_shovel"; - materials[278] = "minecraft:diamond_pickaxe"; - materials[279] = "minecraft:diamond_axe"; - materials[280] = "minecraft:stick"; - materials[281] = "minecraft:bowl"; - materials[282] = "minecraft:mushroom_stew"; - materials[283] = "minecraft:golden_sword"; - materials[284] = "minecraft:golden_shovel"; - materials[285] = "minecraft:golden_pickaxe"; - materials[286] = "minecraft:golden_axe"; - materials[287] = "minecraft:string"; - materials[288] = "minecraft:feather"; - materials[289] = "minecraft:gunpowder"; - materials[290] = "minecraft:wooden_hoe"; - materials[291] = "minecraft:stone_hoe"; - materials[292] = "minecraft:iron_hoe"; - materials[293] = "minecraft:diamond_hoe"; - materials[294] = "minecraft:golden_hoe"; - materials[295] = "minecraft:wheat_seeds"; - materials[296] = "minecraft:wheat"; - materials[297] = "minecraft:bread"; - materials[298] = "minecraft:leather_helmet"; - materials[299] = "minecraft:leather_chestplate"; - materials[300] = "minecraft:leather_leggings"; - materials[301] = "minecraft:leather_boots"; - materials[302] = "minecraft:chainmail_helmet"; - materials[303] = "minecraft:chainmail_chestplate"; - materials[304] = "minecraft:chainmail_leggings"; - materials[305] = "minecraft:chainmail_boots"; - materials[306] = "minecraft:iron_helmet"; - materials[307] = "minecraft:iron_chestplate"; - materials[308] = "minecraft:iron_leggings"; - materials[309] = "minecraft:iron_boots"; - materials[310] = "minecraft:diamond_helmet"; - materials[311] = "minecraft:diamond_chestplate"; - materials[312] = "minecraft:diamond_leggings"; - materials[313] = "minecraft:diamond_boots"; - materials[314] = "minecraft:golden_helmet"; - materials[315] = "minecraft:golden_chestplate"; - materials[316] = "minecraft:golden_leggings"; - materials[317] = "minecraft:golden_boots"; - materials[318] = "minecraft:flint"; - materials[319] = "minecraft:porkchop"; - materials[320] = "minecraft:cooked_porkchop"; - materials[321] = "minecraft:painting"; - materials[322] = "minecraft:golden_apple"; - materials[323] = "minecraft:sign"; - materials[324] = "minecraft:wooden_door"; - materials[325] = "minecraft:bucket"; - materials[326] = "minecraft:water_bucket"; - materials[327] = "minecraft:lava_bucket"; - materials[328] = "minecraft:minecart"; - materials[329] = "minecraft:saddle"; - materials[330] = "minecraft:iron_door"; - materials[331] = "minecraft:redstone"; - materials[332] = "minecraft:snowball"; - materials[333] = "minecraft:boat"; - materials[334] = "minecraft:leather"; - materials[335] = "minecraft:milk_bucket"; - materials[336] = "minecraft:brick"; - materials[337] = "minecraft:clay_ball"; - materials[338] = "minecraft:reeds"; - materials[339] = "minecraft:paper"; - materials[340] = "minecraft:book"; - materials[341] = "minecraft:slime_ball"; - materials[342] = "minecraft:chest_minecart"; - materials[343] = "minecraft:furnace_minecart"; - materials[344] = "minecraft:egg"; - materials[345] = "minecraft:compass"; - materials[346] = "minecraft:fishing_rod"; - materials[347] = "minecraft:clock"; - materials[348] = "minecraft:glowstone_dust"; - materials[349] = "minecraft:fish"; - materials[350] = "minecraft:cooked_fish"; // Paper - cooked_fished -> cooked_fish - materials[351] = "minecraft:dye"; - materials[352] = "minecraft:bone"; - materials[353] = "minecraft:sugar"; - materials[354] = "minecraft:cake"; - materials[355] = "minecraft:bed"; - materials[356] = "minecraft:repeater"; - materials[357] = "minecraft:cookie"; - materials[358] = "minecraft:filled_map"; - materials[359] = "minecraft:shears"; - materials[360] = "minecraft:melon"; - materials[361] = "minecraft:pumpkin_seeds"; - materials[362] = "minecraft:melon_seeds"; - materials[363] = "minecraft:beef"; - materials[364] = "minecraft:cooked_beef"; - materials[365] = "minecraft:chicken"; - materials[366] = "minecraft:cooked_chicken"; - materials[367] = "minecraft:rotten_flesh"; - materials[368] = "minecraft:ender_pearl"; - materials[369] = "minecraft:blaze_rod"; - materials[370] = "minecraft:ghast_tear"; - materials[371] = "minecraft:gold_nugget"; - materials[372] = "minecraft:nether_wart"; - materials[373] = "minecraft:potion"; - materials[374] = "minecraft:glass_bottle"; - materials[375] = "minecraft:spider_eye"; - materials[376] = "minecraft:fermented_spider_eye"; - materials[377] = "minecraft:blaze_powder"; - materials[378] = "minecraft:magma_cream"; - materials[379] = "minecraft:brewing_stand"; - materials[380] = "minecraft:cauldron"; - materials[381] = "minecraft:ender_eye"; - materials[382] = "minecraft:speckled_melon"; - materials[383] = "minecraft:spawn_egg"; - materials[384] = "minecraft:experience_bottle"; - materials[385] = "minecraft:fire_charge"; - materials[386] = "minecraft:writable_book"; - materials[387] = "minecraft:written_book"; - materials[388] = "minecraft:emerald"; - materials[389] = "minecraft:item_frame"; - materials[390] = "minecraft:flower_pot"; - materials[391] = "minecraft:carrot"; - materials[392] = "minecraft:potato"; - materials[393] = "minecraft:baked_potato"; - materials[394] = "minecraft:poisonous_potato"; - materials[395] = "minecraft:map"; - materials[396] = "minecraft:golden_carrot"; - materials[397] = "minecraft:skull"; - materials[398] = "minecraft:carrot_on_a_stick"; - materials[399] = "minecraft:nether_star"; - materials[400] = "minecraft:pumpkin_pie"; - materials[401] = "minecraft:fireworks"; - materials[402] = "minecraft:firework_charge"; - materials[403] = "minecraft:enchanted_book"; - materials[404] = "minecraft:comparator"; - materials[405] = "minecraft:netherbrick"; - materials[406] = "minecraft:quartz"; - materials[407] = "minecraft:tnt_minecart"; - materials[408] = "minecraft:hopper_minecart"; - materials[417] = "minecraft:iron_horse_armor"; - materials[418] = "minecraft:golden_horse_armor"; - materials[419] = "minecraft:diamond_horse_armor"; - materials[420] = "minecraft:lead"; - materials[421] = "minecraft:name_tag"; - materials[422] = "minecraft:command_block_minecart"; - materials[2256] = "minecraft:record_13"; - materials[2257] = "minecraft:record_cat"; - materials[2258] = "minecraft:record_blocks"; - materials[2259] = "minecraft:record_chirp"; - materials[2260] = "minecraft:record_far"; - materials[2261] = "minecraft:record_mall"; - materials[2262] = "minecraft:record_mellohi"; - materials[2263] = "minecraft:record_stal"; - materials[2264] = "minecraft:record_strad"; - materials[2265] = "minecraft:record_ward"; - materials[2266] = "minecraft:record_11"; - materials[2267] = "minecraft:record_wait"; - // Paper start - materials[409] = "minecraft:prismarine_shard"; - materials[410] = "minecraft:prismarine_crystals"; - materials[411] = "minecraft:rabbit"; - materials[412] = "minecraft:cooked_rabbit"; - materials[413] = "minecraft:rabbit_stew"; - materials[414] = "minecraft:rabbit_foot"; - materials[415] = "minecraft:rabbit_hide"; - materials[416] = "minecraft:armor_stand"; - materials[423] = "minecraft:mutton"; - materials[424] = "minecraft:cooked_mutton"; - materials[425] = "minecraft:banner"; - materials[426] = "minecraft:end_crystal"; - materials[427] = "minecraft:spruce_door"; - materials[428] = "minecraft:birch_door"; - materials[429] = "minecraft:jungle_door"; - materials[430] = "minecraft:acacia_door"; - materials[431] = "minecraft:dark_oak_door"; - materials[432] = "minecraft:chorus_fruit"; - materials[433] = "minecraft:chorus_fruit_popped"; - materials[434] = "minecraft:beetroot"; - materials[435] = "minecraft:beetroot_seeds"; - materials[436] = "minecraft:beetroot_soup"; - materials[437] = "minecraft:dragon_breath"; - materials[438] = "minecraft:splash_potion"; - materials[439] = "minecraft:spectral_arrow"; - materials[440] = "minecraft:tipped_arrow"; - materials[441] = "minecraft:lingering_potion"; - materials[442] = "minecraft:shield"; - materials[443] = "minecraft:elytra"; - materials[444] = "minecraft:spruce_boat"; - materials[445] = "minecraft:birch_boat"; - materials[446] = "minecraft:jungle_boat"; - materials[447] = "minecraft:acacia_boat"; - materials[448] = "minecraft:dark_oak_boat"; - materials[449] = "minecraft:totem_of_undying"; - materials[450] = "minecraft:shulker_shell"; - materials[452] = "minecraft:iron_nugget"; - materials[453] = "minecraft:knowledge_book"; - // Paper end - } - } - - private static class DataConverterArmorStand implements DataConverter { - - DataConverterArmorStand() { - } - - public int getDataVersion() { - return 147; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("ArmorStand".equals(cmp.getString("id")) && cmp.getBoolean("Silent") && !cmp.getBoolean("Marker")) { - cmp.remove("Silent"); - } - - return cmp; - } - - } - - private static class DataConverterBanner implements DataConverter { - - DataConverterBanner() { - } - - public int getDataVersion() { - return 804; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:banner".equals(cmp.getString("id")) && cmp.contains("tag", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); - - if (nbttagcompound1.contains("BlockEntityTag", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); - - if (nbttagcompound2.contains("Base", 99)) { - cmp.putShort("Damage", (short) (nbttagcompound2.getShort("Base") & 15)); - if (nbttagcompound1.contains("display", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound1.getCompound("display"); - - if (nbttagcompound3.contains("Lore", 9)) { - net.minecraft.nbt.ListTag nbttaglist = nbttagcompound3.getList("Lore", 8); - - if (nbttaglist.size() == 1 && "(+NBT)".equals(nbttaglist.getString(0))) { - return cmp; - } - } - } - - nbttagcompound2.remove("Base"); - if (nbttagcompound2.isEmpty()) { - nbttagcompound1.remove("BlockEntityTag"); - } - - if (nbttagcompound1.isEmpty()) { - cmp.remove("tag"); - } - } - } - } - - return cmp; - } - - } - - private static class DataConverterPotionId implements DataConverter { - - private static final String[] potions = new String[128]; - - DataConverterPotionId() { - } - - public int getDataVersion() { - return 102; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:potion".equals(cmp.getString("id"))) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); - short short0 = cmp.getShort("Damage"); - - if (!nbttagcompound1.contains("Potion", 8)) { - String s = DataConverterPotionId.potions[short0 & 127]; - - nbttagcompound1.putString("Potion", s == null ? "minecraft:water" : s); - cmp.put("tag", nbttagcompound1); - if ((short0 & 16384) == 16384) { - cmp.putString("id", "minecraft:splash_potion"); - } - } - - if (short0 != 0) { - cmp.putShort("Damage", (short) 0); - } - } - - return cmp; - } - - static { - DataConverterPotionId.potions[0] = "minecraft:water"; - DataConverterPotionId.potions[1] = "minecraft:regeneration"; - DataConverterPotionId.potions[2] = "minecraft:swiftness"; - DataConverterPotionId.potions[3] = "minecraft:fire_resistance"; - DataConverterPotionId.potions[4] = "minecraft:poison"; - DataConverterPotionId.potions[5] = "minecraft:healing"; - DataConverterPotionId.potions[6] = "minecraft:night_vision"; - DataConverterPotionId.potions[7] = null; - DataConverterPotionId.potions[8] = "minecraft:weakness"; - DataConverterPotionId.potions[9] = "minecraft:strength"; - DataConverterPotionId.potions[10] = "minecraft:slowness"; - DataConverterPotionId.potions[11] = "minecraft:leaping"; - DataConverterPotionId.potions[12] = "minecraft:harming"; - DataConverterPotionId.potions[13] = "minecraft:water_breathing"; - DataConverterPotionId.potions[14] = "minecraft:invisibility"; - DataConverterPotionId.potions[15] = null; - DataConverterPotionId.potions[16] = "minecraft:awkward"; - DataConverterPotionId.potions[17] = "minecraft:regeneration"; - DataConverterPotionId.potions[18] = "minecraft:swiftness"; - DataConverterPotionId.potions[19] = "minecraft:fire_resistance"; - DataConverterPotionId.potions[20] = "minecraft:poison"; - DataConverterPotionId.potions[21] = "minecraft:healing"; - DataConverterPotionId.potions[22] = "minecraft:night_vision"; - DataConverterPotionId.potions[23] = null; - DataConverterPotionId.potions[24] = "minecraft:weakness"; - DataConverterPotionId.potions[25] = "minecraft:strength"; - DataConverterPotionId.potions[26] = "minecraft:slowness"; - DataConverterPotionId.potions[27] = "minecraft:leaping"; - DataConverterPotionId.potions[28] = "minecraft:harming"; - DataConverterPotionId.potions[29] = "minecraft:water_breathing"; - DataConverterPotionId.potions[30] = "minecraft:invisibility"; - DataConverterPotionId.potions[31] = null; - DataConverterPotionId.potions[32] = "minecraft:thick"; - DataConverterPotionId.potions[33] = "minecraft:strong_regeneration"; - DataConverterPotionId.potions[34] = "minecraft:strong_swiftness"; - DataConverterPotionId.potions[35] = "minecraft:fire_resistance"; - DataConverterPotionId.potions[36] = "minecraft:strong_poison"; - DataConverterPotionId.potions[37] = "minecraft:strong_healing"; - DataConverterPotionId.potions[38] = "minecraft:night_vision"; - DataConverterPotionId.potions[39] = null; - DataConverterPotionId.potions[40] = "minecraft:weakness"; - DataConverterPotionId.potions[41] = "minecraft:strong_strength"; - DataConverterPotionId.potions[42] = "minecraft:slowness"; - DataConverterPotionId.potions[43] = "minecraft:strong_leaping"; - DataConverterPotionId.potions[44] = "minecraft:strong_harming"; - DataConverterPotionId.potions[45] = "minecraft:water_breathing"; - DataConverterPotionId.potions[46] = "minecraft:invisibility"; - DataConverterPotionId.potions[47] = null; - DataConverterPotionId.potions[48] = null; - DataConverterPotionId.potions[49] = "minecraft:strong_regeneration"; - DataConverterPotionId.potions[50] = "minecraft:strong_swiftness"; - DataConverterPotionId.potions[51] = "minecraft:fire_resistance"; - DataConverterPotionId.potions[52] = "minecraft:strong_poison"; - DataConverterPotionId.potions[53] = "minecraft:strong_healing"; - DataConverterPotionId.potions[54] = "minecraft:night_vision"; - DataConverterPotionId.potions[55] = null; - DataConverterPotionId.potions[56] = "minecraft:weakness"; - DataConverterPotionId.potions[57] = "minecraft:strong_strength"; - DataConverterPotionId.potions[58] = "minecraft:slowness"; - DataConverterPotionId.potions[59] = "minecraft:strong_leaping"; - DataConverterPotionId.potions[60] = "minecraft:strong_harming"; - DataConverterPotionId.potions[61] = "minecraft:water_breathing"; - DataConverterPotionId.potions[62] = "minecraft:invisibility"; - DataConverterPotionId.potions[63] = null; - DataConverterPotionId.potions[64] = "minecraft:mundane"; - DataConverterPotionId.potions[65] = "minecraft:long_regeneration"; - DataConverterPotionId.potions[66] = "minecraft:long_swiftness"; - DataConverterPotionId.potions[67] = "minecraft:long_fire_resistance"; - DataConverterPotionId.potions[68] = "minecraft:long_poison"; - DataConverterPotionId.potions[69] = "minecraft:healing"; - DataConverterPotionId.potions[70] = "minecraft:long_night_vision"; - DataConverterPotionId.potions[71] = null; - DataConverterPotionId.potions[72] = "minecraft:long_weakness"; - DataConverterPotionId.potions[73] = "minecraft:long_strength"; - DataConverterPotionId.potions[74] = "minecraft:long_slowness"; - DataConverterPotionId.potions[75] = "minecraft:long_leaping"; - DataConverterPotionId.potions[76] = "minecraft:harming"; - DataConverterPotionId.potions[77] = "minecraft:long_water_breathing"; - DataConverterPotionId.potions[78] = "minecraft:long_invisibility"; - DataConverterPotionId.potions[79] = null; - DataConverterPotionId.potions[80] = "minecraft:awkward"; - DataConverterPotionId.potions[81] = "minecraft:long_regeneration"; - DataConverterPotionId.potions[82] = "minecraft:long_swiftness"; - DataConverterPotionId.potions[83] = "minecraft:long_fire_resistance"; - DataConverterPotionId.potions[84] = "minecraft:long_poison"; - DataConverterPotionId.potions[85] = "minecraft:healing"; - DataConverterPotionId.potions[86] = "minecraft:long_night_vision"; - DataConverterPotionId.potions[87] = null; - DataConverterPotionId.potions[88] = "minecraft:long_weakness"; - DataConverterPotionId.potions[89] = "minecraft:long_strength"; - DataConverterPotionId.potions[90] = "minecraft:long_slowness"; - DataConverterPotionId.potions[91] = "minecraft:long_leaping"; - DataConverterPotionId.potions[92] = "minecraft:harming"; - DataConverterPotionId.potions[93] = "minecraft:long_water_breathing"; - DataConverterPotionId.potions[94] = "minecraft:long_invisibility"; - DataConverterPotionId.potions[95] = null; - DataConverterPotionId.potions[96] = "minecraft:thick"; - DataConverterPotionId.potions[97] = "minecraft:regeneration"; - DataConverterPotionId.potions[98] = "minecraft:swiftness"; - DataConverterPotionId.potions[99] = "minecraft:long_fire_resistance"; - DataConverterPotionId.potions[100] = "minecraft:poison"; - DataConverterPotionId.potions[101] = "minecraft:strong_healing"; - DataConverterPotionId.potions[102] = "minecraft:long_night_vision"; - DataConverterPotionId.potions[103] = null; - DataConverterPotionId.potions[104] = "minecraft:long_weakness"; - DataConverterPotionId.potions[105] = "minecraft:strength"; - DataConverterPotionId.potions[106] = "minecraft:long_slowness"; - DataConverterPotionId.potions[107] = "minecraft:leaping"; - DataConverterPotionId.potions[108] = "minecraft:strong_harming"; - DataConverterPotionId.potions[109] = "minecraft:long_water_breathing"; - DataConverterPotionId.potions[110] = "minecraft:long_invisibility"; - DataConverterPotionId.potions[111] = null; - DataConverterPotionId.potions[112] = null; - DataConverterPotionId.potions[113] = "minecraft:regeneration"; - DataConverterPotionId.potions[114] = "minecraft:swiftness"; - DataConverterPotionId.potions[115] = "minecraft:long_fire_resistance"; - DataConverterPotionId.potions[116] = "minecraft:poison"; - DataConverterPotionId.potions[117] = "minecraft:strong_healing"; - DataConverterPotionId.potions[118] = "minecraft:long_night_vision"; - DataConverterPotionId.potions[119] = null; - DataConverterPotionId.potions[120] = "minecraft:long_weakness"; - DataConverterPotionId.potions[121] = "minecraft:strength"; - DataConverterPotionId.potions[122] = "minecraft:long_slowness"; - DataConverterPotionId.potions[123] = "minecraft:leaping"; - DataConverterPotionId.potions[124] = "minecraft:strong_harming"; - DataConverterPotionId.potions[125] = "minecraft:long_water_breathing"; - DataConverterPotionId.potions[126] = "minecraft:long_invisibility"; - DataConverterPotionId.potions[127] = null; - } - } - - private static class DataConverterSpawnEgg implements DataConverter { - - private static final String[] eggs = new String[256]; - - DataConverterSpawnEgg() { - } - - public int getDataVersion() { - return 105; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:spawn_egg".equals(cmp.getString("id"))) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); - net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag"); - short short0 = cmp.getShort("Damage"); - - if (!nbttagcompound2.contains("id", 8)) { - String s = DataConverterSpawnEgg.eggs[short0 & 255]; - - if (s != null) { - nbttagcompound2.putString("id", s); - nbttagcompound1.put("EntityTag", nbttagcompound2); - cmp.put("tag", nbttagcompound1); - } - } - - if (short0 != 0) { - cmp.putShort("Damage", (short) 0); - } - } - - return cmp; - } - - static { - - DataConverterSpawnEgg.eggs[1] = "Item"; - DataConverterSpawnEgg.eggs[2] = "XPOrb"; - DataConverterSpawnEgg.eggs[7] = "ThrownEgg"; - DataConverterSpawnEgg.eggs[8] = "LeashKnot"; - DataConverterSpawnEgg.eggs[9] = "Painting"; - DataConverterSpawnEgg.eggs[10] = "Arrow"; - DataConverterSpawnEgg.eggs[11] = "Snowball"; - DataConverterSpawnEgg.eggs[12] = "Fireball"; - DataConverterSpawnEgg.eggs[13] = "SmallFireball"; - DataConverterSpawnEgg.eggs[14] = "ThrownEnderpearl"; - DataConverterSpawnEgg.eggs[15] = "EyeOfEnderSignal"; - DataConverterSpawnEgg.eggs[16] = "ThrownPotion"; - DataConverterSpawnEgg.eggs[17] = "ThrownExpBottle"; - DataConverterSpawnEgg.eggs[18] = "ItemFrame"; - DataConverterSpawnEgg.eggs[19] = "WitherSkull"; - DataConverterSpawnEgg.eggs[20] = "PrimedTnt"; - DataConverterSpawnEgg.eggs[21] = "FallingSand"; - DataConverterSpawnEgg.eggs[22] = "FireworksRocketEntity"; - DataConverterSpawnEgg.eggs[23] = "TippedArrow"; - DataConverterSpawnEgg.eggs[24] = "SpectralArrow"; - DataConverterSpawnEgg.eggs[25] = "ShulkerBullet"; - DataConverterSpawnEgg.eggs[26] = "DragonFireball"; - DataConverterSpawnEgg.eggs[30] = "ArmorStand"; - DataConverterSpawnEgg.eggs[41] = "Boat"; - DataConverterSpawnEgg.eggs[42] = "MinecartRideable"; - DataConverterSpawnEgg.eggs[43] = "MinecartChest"; - DataConverterSpawnEgg.eggs[44] = "MinecartFurnace"; - DataConverterSpawnEgg.eggs[45] = "MinecartTNT"; - DataConverterSpawnEgg.eggs[46] = "MinecartHopper"; - DataConverterSpawnEgg.eggs[47] = "MinecartSpawner"; - DataConverterSpawnEgg.eggs[40] = "MinecartCommandBlock"; - DataConverterSpawnEgg.eggs[48] = "Mob"; - DataConverterSpawnEgg.eggs[49] = "Monster"; - DataConverterSpawnEgg.eggs[50] = "Creeper"; - DataConverterSpawnEgg.eggs[51] = "Skeleton"; - DataConverterSpawnEgg.eggs[52] = "Spider"; - DataConverterSpawnEgg.eggs[53] = "Giant"; - DataConverterSpawnEgg.eggs[54] = "Zombie"; - DataConverterSpawnEgg.eggs[55] = "Slime"; - DataConverterSpawnEgg.eggs[56] = "Ghast"; - DataConverterSpawnEgg.eggs[57] = "PigZombie"; - DataConverterSpawnEgg.eggs[58] = "Enderman"; - DataConverterSpawnEgg.eggs[59] = "CaveSpider"; - DataConverterSpawnEgg.eggs[60] = "Silverfish"; - DataConverterSpawnEgg.eggs[61] = "Blaze"; - DataConverterSpawnEgg.eggs[62] = "LavaSlime"; - DataConverterSpawnEgg.eggs[63] = "EnderDragon"; - DataConverterSpawnEgg.eggs[64] = "WitherBoss"; - DataConverterSpawnEgg.eggs[65] = "Bat"; - DataConverterSpawnEgg.eggs[66] = "Witch"; - DataConverterSpawnEgg.eggs[67] = "Endermite"; - DataConverterSpawnEgg.eggs[68] = "Guardian"; - DataConverterSpawnEgg.eggs[69] = "Shulker"; - DataConverterSpawnEgg.eggs[90] = "Pig"; - DataConverterSpawnEgg.eggs[91] = "Sheep"; - DataConverterSpawnEgg.eggs[92] = "Cow"; - DataConverterSpawnEgg.eggs[93] = "Chicken"; - DataConverterSpawnEgg.eggs[94] = "Squid"; - DataConverterSpawnEgg.eggs[95] = "Wolf"; - DataConverterSpawnEgg.eggs[96] = "MushroomCow"; - DataConverterSpawnEgg.eggs[97] = "SnowMan"; - DataConverterSpawnEgg.eggs[98] = "Ozelot"; - DataConverterSpawnEgg.eggs[99] = "VillagerGolem"; - DataConverterSpawnEgg.eggs[100] = "EntityHorse"; - DataConverterSpawnEgg.eggs[101] = "Rabbit"; - DataConverterSpawnEgg.eggs[120] = "Villager"; - DataConverterSpawnEgg.eggs[200] = "EnderCrystal"; - } - } - - private static class DataConverterMinecart implements DataConverter { - - private static final List a = Lists.newArrayList( - "MinecartRideable", - "MinecartChest", - "MinecartFurnace", - "MinecartTNT", - "MinecartSpawner", - "MinecartHopper", - "MinecartCommandBlock" - ); - - DataConverterMinecart() { - } - - public int getDataVersion() { - return 106; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("Minecart".equals(cmp.getString("id"))) { - String s = "MinecartRideable"; - int i = cmp.getInt("Type"); - - if (i > 0 && i < DataConverterMinecart.a.size()) { - s = DataConverterMinecart.a.get(i); - } - - cmp.putString("id", s); - cmp.remove("Type"); - } - - return cmp; - } - - } - - private static class DataConverterMobSpawner implements DataConverter { - - DataConverterMobSpawner() { - } - - public int getDataVersion() { - return 107; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if (!"MobSpawner".equals(cmp.getString("id"))) { - return cmp; - } else { - if (cmp.contains("EntityId", 8)) { - String s = cmp.getString("EntityId"); - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("SpawnData"); - - nbttagcompound1.putString("id", s.isEmpty() ? "Pig" : s); - cmp.put("SpawnData", nbttagcompound1); - cmp.remove("EntityId"); - } - - if (cmp.contains("SpawnPotentials", 9)) { - net.minecraft.nbt.ListTag nbttaglist = cmp.getList("SpawnPotentials", 10); - - for (int i = 0; i < nbttaglist.size(); ++i) { - net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompound(i); - - if (nbttagcompound2.contains("Type", 8)) { - net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound2.getCompound("Properties"); - - nbttagcompound3.putString("id", nbttagcompound2.getString("Type")); - nbttagcompound2.put("Entity", nbttagcompound3); - nbttagcompound2.remove("Type"); - nbttagcompound2.remove("Properties"); - } - } - } - - return cmp; - } - } - - } - - private static class DataConverterUUID implements DataConverter { - - DataConverterUUID() { - } - - public int getDataVersion() { - return 108; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if (cmp.contains("UUID", 8)) { - cmp.putUUID("UUID", UUID.fromString(cmp.getString("UUID"))); - } - - return cmp; - } - - } - - private static class DataConverterHealth implements DataConverter { - - private static final Set a = Sets.newHashSet( - "ArmorStand", - "Bat", - "Blaze", - "CaveSpider", - "Chicken", - "Cow", - "Creeper", - "EnderDragon", - "Enderman", - "Endermite", - "EntityHorse", - "Ghast", - "Giant", - "Guardian", - "LavaSlime", - "MushroomCow", - "Ozelot", - "Pig", - "PigZombie", - "Rabbit", - "Sheep", - "Shulker", - "Silverfish", - "Skeleton", - "Slime", - "SnowMan", - "Spider", - "Squid", - "Villager", - "VillagerGolem", - "Witch", - "WitherBoss", - "Wolf", - "Zombie" - ); - - DataConverterHealth() { - } - - public int getDataVersion() { - return 109; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if (DataConverterHealth.a.contains(cmp.getString("id"))) { - float f; - - if (cmp.contains("HealF", 99)) { - f = cmp.getFloat("HealF"); - cmp.remove("HealF"); - } else { - if (!cmp.contains("Health", 99)) { - return cmp; - } - - f = cmp.getFloat("Health"); - } - - cmp.putFloat("Health", f); - } - - return cmp; - } - - } - - private static class DataConverterSaddle implements DataConverter { - - DataConverterSaddle() { - } - - public int getDataVersion() { - return 110; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("EntityHorse".equals(cmp.getString("id")) && !cmp.contains("SaddleItem", 10) && cmp.getBoolean("Saddle")) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = new net.minecraft.nbt.CompoundTag(); - - nbttagcompound1.putString("id", "minecraft:saddle"); - nbttagcompound1.putByte("Count", (byte) 1); - nbttagcompound1.putShort("Damage", (short) 0); - cmp.put("SaddleItem", nbttagcompound1); - cmp.remove("Saddle"); - } - - return cmp; - } - - } - - private static class DataConverterHanging implements DataConverter { - - DataConverterHanging() { - } - - public int getDataVersion() { - return 111; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - String s = cmp.getString("id"); - boolean flag = "Painting".equals(s); - boolean flag1 = "ItemFrame".equals(s); - - if ((flag || flag1) && !cmp.contains("Facing", 99)) { - Direction enumdirection; - - if (cmp.contains("Direction", 99)) { - enumdirection = Direction.from2DDataValue(cmp.getByte("Direction")); - cmp.putInt("TileX", cmp.getInt("TileX") + enumdirection.getStepX()); - cmp.putInt("TileY", cmp.getInt("TileY") + enumdirection.getStepY()); - cmp.putInt("TileZ", cmp.getInt("TileZ") + enumdirection.getStepZ()); - cmp.remove("Direction"); - if (flag1 && cmp.contains("ItemRotation", 99)) { - cmp.putByte("ItemRotation", (byte) (cmp.getByte("ItemRotation") * 2)); - } - } else { - enumdirection = Direction.from2DDataValue(cmp.getByte("Dir")); - cmp.remove("Dir"); - } - - cmp.putByte("Facing", (byte) enumdirection.get2DDataValue()); - } - - return cmp; - } - - } - - private static class DataConverterDropChances implements DataConverter { - - DataConverterDropChances() { - } - - public int getDataVersion() { - return 113; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - net.minecraft.nbt.ListTag nbttaglist; - - if (cmp.contains("HandDropChances", 9)) { - nbttaglist = cmp.getList("HandDropChances", 5); - if (nbttaglist.size() == 2 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F) { - cmp.remove("HandDropChances"); - } - } - - if (cmp.contains("ArmorDropChances", 9)) { - nbttaglist = cmp.getList("ArmorDropChances", 5); - if (nbttaglist.size() == 4 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F && nbttaglist.getFloat( - 2) == 0.0F && nbttaglist.getFloat(3) == 0.0F) { - cmp.remove("ArmorDropChances"); - } - } - - return cmp; - } - - } - - private static class DataConverterRiding implements DataConverter { - - DataConverterRiding() { - } - - public int getDataVersion() { - return 135; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - while (cmp.contains("Riding", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = this.b(cmp); - - this.convert(cmp, nbttagcompound1); - cmp = nbttagcompound1; - } - - return cmp; - } - - protected void convert(net.minecraft.nbt.CompoundTag nbttagcompound, net.minecraft.nbt.CompoundTag nbttagcompound1) { - net.minecraft.nbt.ListTag nbttaglist = new net.minecraft.nbt.ListTag(); - - nbttaglist.add(nbttagcompound); - nbttagcompound1.put("Passengers", nbttaglist); - } - - protected net.minecraft.nbt.CompoundTag b(net.minecraft.nbt.CompoundTag nbttagcompound) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Riding"); - - nbttagcompound.remove("Riding"); - return nbttagcompound1; - } - - } - - private static class DataConverterBook implements DataConverter { - - DataConverterBook() { - } - - public int getDataVersion() { - return 165; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:written_book".equals(cmp.getString("id"))) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); - - if (nbttagcompound1.contains("pages", 9)) { - net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("pages", 8); - - for (int i = 0; i < nbttaglist.size(); ++i) { - String s = nbttaglist.getString(i); - Component object = null; - - if (!"null".equals(s) && !StringUtil.isNullOrEmpty(s)) { - if ((s.charAt(0) != 34 || s.charAt(s.length() - 1) != 34) && (s.charAt(0) != 123 || s.charAt(s.length() - 1) != 125)) { - object = new TextComponent(s); - } else { - try { - object = GsonHelper.fromJson(DataConverterSignText.a, s, Component.class, true); - if (object == null) { - object = new TextComponent(""); - } - } catch (JsonParseException jsonparseexception) { - ; - } - - if (object == null) { - try { - object = Component.Serializer.fromJson(s); - } catch (JsonParseException jsonparseexception1) { - ; - } - } - - if (object == null) { - try { - object = Component.Serializer.fromJsonLenient(s); - } catch (JsonParseException jsonparseexception2) { - ; - } - } - - if (object == null) { - object = new TextComponent(s); - } - } - } else { - object = new TextComponent(""); - } - - nbttaglist.set(i, net.minecraft.nbt.StringTag.valueOf(Component.Serializer.toJson(object))); - } - - nbttagcompound1.put("pages", nbttaglist); - } - } - - return cmp; - } - - } - - private static class DataConverterCookedFish implements DataConverter { - - private static final ResourceLocation a = new ResourceLocation("cooked_fished"); - - DataConverterCookedFish() { - } - - public int getDataVersion() { - return 502; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if (cmp.contains("id", 8) && DataConverterCookedFish.a.equals(new ResourceLocation(cmp.getString("id")))) { - cmp.putString("id", "minecraft:cooked_fish"); - } - - return cmp; - } - - } - - private static class DataConverterZombie implements DataConverter { - - private static final Random a = new Random(); - - DataConverterZombie() { - } - - public int getDataVersion() { - return 502; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("Zombie".equals(cmp.getString("id")) && cmp.getBoolean("IsVillager")) { - if (!cmp.contains("ZombieType", 99)) { - int i = -1; - - if (cmp.contains("VillagerProfession", 99)) { - try { - i = this.convert(cmp.getInt("VillagerProfession")); - } catch (RuntimeException runtimeexception) { - ; - } - } - - if (i == -1) { - i = this.convert(DataConverterZombie.a.nextInt(6)); - } - - cmp.putInt("ZombieType", i); - } - - cmp.remove("IsVillager"); - } - - return cmp; - } - - private int convert(int i) { - return i >= 0 && i < 6 ? i : -1; - } - - } - - private static class DataConverterVBO implements DataConverter { - - DataConverterVBO() { - } - - public int getDataVersion() { - return 505; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - cmp.putString("useVbo", "true"); - return cmp; - } - - } - - private static class DataConverterGuardian implements DataConverter { - - DataConverterGuardian() { - } - - public int getDataVersion() { - return 700; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("Guardian".equals(cmp.getString("id"))) { - if (cmp.getBoolean("Elder")) { - cmp.putString("id", "ElderGuardian"); - } - - cmp.remove("Elder"); - } - - return cmp; - } - - } - - private static class DataConverterSkeleton implements DataConverter { - - DataConverterSkeleton() { - } - - public int getDataVersion() { - return 701; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - String s = cmp.getString("id"); - - if ("Skeleton".equals(s)) { - int i = cmp.getInt("SkeletonType"); - - if (i == 1) { - cmp.putString("id", "WitherSkeleton"); - } else if (i == 2) { - cmp.putString("id", "Stray"); - } - - cmp.remove("SkeletonType"); - } - - return cmp; - } - - } - - private static class DataConverterZombieType implements DataConverter { - - DataConverterZombieType() { - } - - public int getDataVersion() { - return 702; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("Zombie".equals(cmp.getString("id"))) { - int i = cmp.getInt("ZombieType"); - - switch (i) { - case 0: - default: - break; - - case 1: - case 2: - case 3: - case 4: - case 5: - cmp.putString("id", "ZombieVillager"); - cmp.putInt("Profession", i - 1); - break; - - case 6: - cmp.putString("id", "Husk"); - } - - cmp.remove("ZombieType"); - } - - return cmp; - } - - } - - private static class DataConverterHorse implements DataConverter { - - DataConverterHorse() { - } - - public int getDataVersion() { - return 703; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("EntityHorse".equals(cmp.getString("id"))) { - int i = cmp.getInt("Type"); - - switch (i) { - case 0: - default: - cmp.putString("id", "Horse"); - break; - - case 1: - cmp.putString("id", "Donkey"); - break; - - case 2: - cmp.putString("id", "Mule"); - break; - - case 3: - cmp.putString("id", "ZombieHorse"); - break; - - case 4: - cmp.putString("id", "SkeletonHorse"); - } - - cmp.remove("Type"); - } - - return cmp; - } - - } - - private static class DataConverterTileEntity implements DataConverter { - - private static final Map a = Maps.newHashMap(); - - DataConverterTileEntity() { - } - - public int getDataVersion() { - return 704; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - String s = DataConverterTileEntity.a.get(cmp.getString("id")); - - if (s != null) { - cmp.putString("id", s); - } - - return cmp; - } - - static { - DataConverterTileEntity.a.put("Airportal", "minecraft:end_portal"); - DataConverterTileEntity.a.put("Banner", "minecraft:banner"); - DataConverterTileEntity.a.put("Beacon", "minecraft:beacon"); - DataConverterTileEntity.a.put("Cauldron", "minecraft:brewing_stand"); - DataConverterTileEntity.a.put("Chest", "minecraft:chest"); - DataConverterTileEntity.a.put("Comparator", "minecraft:comparator"); - DataConverterTileEntity.a.put("Control", "minecraft:command_block"); - DataConverterTileEntity.a.put("DLDetector", "minecraft:daylight_detector"); - DataConverterTileEntity.a.put("Dropper", "minecraft:dropper"); - DataConverterTileEntity.a.put("EnchantTable", "minecraft:enchanting_table"); - DataConverterTileEntity.a.put("EndGateway", "minecraft:end_gateway"); - DataConverterTileEntity.a.put("EnderChest", "minecraft:ender_chest"); - DataConverterTileEntity.a.put("FlowerPot", "minecraft:flower_pot"); - DataConverterTileEntity.a.put("Furnace", "minecraft:furnace"); - DataConverterTileEntity.a.put("Hopper", "minecraft:hopper"); - DataConverterTileEntity.a.put("MobSpawner", "minecraft:mob_spawner"); - DataConverterTileEntity.a.put("Music", "minecraft:noteblock"); - DataConverterTileEntity.a.put("Piston", "minecraft:piston"); - DataConverterTileEntity.a.put("RecordPlayer", "minecraft:jukebox"); - DataConverterTileEntity.a.put("Sign", "minecraft:sign"); - DataConverterTileEntity.a.put("Skull", "minecraft:skull"); - DataConverterTileEntity.a.put("Structure", "minecraft:structure_block"); - DataConverterTileEntity.a.put("Trap", "minecraft:dispenser"); - } - } - - private static class DataConverterEntity implements DataConverter { - - private static final Map a = Maps.newHashMap(); - - DataConverterEntity() { - } - - public int getDataVersion() { - return 704; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - String s = DataConverterEntity.a.get(cmp.getString("id")); - - if (s != null) { - cmp.putString("id", s); - } - - return cmp; - } - - static { - DataConverterEntity.a.put("AreaEffectCloud", "minecraft:area_effect_cloud"); - DataConverterEntity.a.put("ArmorStand", "minecraft:armor_stand"); - DataConverterEntity.a.put("Arrow", "minecraft:arrow"); - DataConverterEntity.a.put("Bat", "minecraft:bat"); - DataConverterEntity.a.put("Blaze", "minecraft:blaze"); - DataConverterEntity.a.put("Boat", "minecraft:boat"); - DataConverterEntity.a.put("CaveSpider", "minecraft:cave_spider"); - DataConverterEntity.a.put("Chicken", "minecraft:chicken"); - DataConverterEntity.a.put("Cow", "minecraft:cow"); - DataConverterEntity.a.put("Creeper", "minecraft:creeper"); - DataConverterEntity.a.put("Donkey", "minecraft:donkey"); - DataConverterEntity.a.put("DragonFireball", "minecraft:dragon_fireball"); - DataConverterEntity.a.put("ElderGuardian", "minecraft:elder_guardian"); - DataConverterEntity.a.put("EnderCrystal", "minecraft:ender_crystal"); - DataConverterEntity.a.put("EnderDragon", "minecraft:ender_dragon"); - DataConverterEntity.a.put("Enderman", "minecraft:enderman"); - DataConverterEntity.a.put("Endermite", "minecraft:endermite"); - DataConverterEntity.a.put("EyeOfEnderSignal", "minecraft:eye_of_ender_signal"); - DataConverterEntity.a.put("FallingSand", "minecraft:falling_block"); - DataConverterEntity.a.put("Fireball", "minecraft:fireball"); - DataConverterEntity.a.put("FireworksRocketEntity", "minecraft:fireworks_rocket"); - DataConverterEntity.a.put("Ghast", "minecraft:ghast"); - DataConverterEntity.a.put("Giant", "minecraft:giant"); - DataConverterEntity.a.put("Guardian", "minecraft:guardian"); - DataConverterEntity.a.put("Horse", "minecraft:horse"); - DataConverterEntity.a.put("Husk", "minecraft:husk"); - DataConverterEntity.a.put("Item", "minecraft:item"); - DataConverterEntity.a.put("ItemFrame", "minecraft:item_frame"); - DataConverterEntity.a.put("LavaSlime", "minecraft:magma_cube"); - DataConverterEntity.a.put("LeashKnot", "minecraft:leash_knot"); - DataConverterEntity.a.put("MinecartChest", "minecraft:chest_minecart"); - DataConverterEntity.a.put("MinecartCommandBlock", "minecraft:commandblock_minecart"); - DataConverterEntity.a.put("MinecartFurnace", "minecraft:furnace_minecart"); - DataConverterEntity.a.put("MinecartHopper", "minecraft:hopper_minecart"); - DataConverterEntity.a.put("MinecartRideable", "minecraft:minecart"); - DataConverterEntity.a.put("MinecartSpawner", "minecraft:spawner_minecart"); - DataConverterEntity.a.put("MinecartTNT", "minecraft:tnt_minecart"); - DataConverterEntity.a.put("Mule", "minecraft:mule"); - DataConverterEntity.a.put("MushroomCow", "minecraft:mooshroom"); - DataConverterEntity.a.put("Ozelot", "minecraft:ocelot"); - DataConverterEntity.a.put("Painting", "minecraft:painting"); - DataConverterEntity.a.put("Pig", "minecraft:pig"); - DataConverterEntity.a.put("PigZombie", "minecraft:zombie_pigman"); - DataConverterEntity.a.put("PolarBear", "minecraft:polar_bear"); - DataConverterEntity.a.put("PrimedTnt", "minecraft:tnt"); - DataConverterEntity.a.put("Rabbit", "minecraft:rabbit"); - DataConverterEntity.a.put("Sheep", "minecraft:sheep"); - DataConverterEntity.a.put("Shulker", "minecraft:shulker"); - DataConverterEntity.a.put("ShulkerBullet", "minecraft:shulker_bullet"); - DataConverterEntity.a.put("Silverfish", "minecraft:silverfish"); - DataConverterEntity.a.put("Skeleton", "minecraft:skeleton"); - DataConverterEntity.a.put("SkeletonHorse", "minecraft:skeleton_horse"); - DataConverterEntity.a.put("Slime", "minecraft:slime"); - DataConverterEntity.a.put("SmallFireball", "minecraft:small_fireball"); - DataConverterEntity.a.put("SnowMan", "minecraft:snowman"); - DataConverterEntity.a.put("Snowball", "minecraft:snowball"); - DataConverterEntity.a.put("SpectralArrow", "minecraft:spectral_arrow"); - DataConverterEntity.a.put("Spider", "minecraft:spider"); - DataConverterEntity.a.put("Squid", "minecraft:squid"); - DataConverterEntity.a.put("Stray", "minecraft:stray"); - DataConverterEntity.a.put("ThrownEgg", "minecraft:egg"); - DataConverterEntity.a.put("ThrownEnderpearl", "minecraft:ender_pearl"); - DataConverterEntity.a.put("ThrownExpBottle", "minecraft:xp_bottle"); - DataConverterEntity.a.put("ThrownPotion", "minecraft:potion"); - DataConverterEntity.a.put("Villager", "minecraft:villager"); - DataConverterEntity.a.put("VillagerGolem", "minecraft:villager_golem"); - DataConverterEntity.a.put("Witch", "minecraft:witch"); - DataConverterEntity.a.put("WitherBoss", "minecraft:wither"); - DataConverterEntity.a.put("WitherSkeleton", "minecraft:wither_skeleton"); - DataConverterEntity.a.put("WitherSkull", "minecraft:wither_skull"); - DataConverterEntity.a.put("Wolf", "minecraft:wolf"); - DataConverterEntity.a.put("XPOrb", "minecraft:xp_orb"); - DataConverterEntity.a.put("Zombie", "minecraft:zombie"); - DataConverterEntity.a.put("ZombieHorse", "minecraft:zombie_horse"); - DataConverterEntity.a.put("ZombieVillager", "minecraft:zombie_villager"); - } - } - - private static class DataConverterPotionWater implements DataConverter { - - DataConverterPotionWater() { - } - - public int getDataVersion() { - return 806; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - String s = cmp.getString("id"); - - if ("minecraft:potion".equals(s) || "minecraft:splash_potion".equals(s) || "minecraft:lingering_potion".equals(s) || "minecraft:tipped_arrow".equals( - s)) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); - - if (!nbttagcompound1.contains("Potion", 8)) { - nbttagcompound1.putString("Potion", "minecraft:water"); - } - - if (!cmp.contains("tag", 10)) { - cmp.put("tag", nbttagcompound1); - } - } - - return cmp; - } - - } - - private static class DataConverterShulker implements DataConverter { - - DataConverterShulker() { - } - - public int getDataVersion() { - return 808; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:shulker".equals(cmp.getString("id")) && !cmp.contains("Color", 99)) { - cmp.putByte("Color", (byte) 10); - } - - return cmp; - } - - } - - private static class DataConverterShulkerBoxItem implements DataConverter { - - public static final String[] a = new String[]{"minecraft:white_shulker_box", "minecraft:orange_shulker_box", "minecraft:magenta_shulker_box", "minecraft:light_blue_shulker_box", "minecraft:yellow_shulker_box", "minecraft:lime_shulker_box", "minecraft:pink_shulker_box", "minecraft:gray_shulker_box", "minecraft:silver_shulker_box", "minecraft:cyan_shulker_box", "minecraft:purple_shulker_box", "minecraft:blue_shulker_box", "minecraft:brown_shulker_box", "minecraft:green_shulker_box", "minecraft:red_shulker_box", "minecraft:black_shulker_box"}; - - DataConverterShulkerBoxItem() { - } - - public int getDataVersion() { - return 813; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:shulker_box".equals(cmp.getString("id")) && cmp.contains("tag", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); - - if (nbttagcompound1.contains("BlockEntityTag", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); - - if (nbttagcompound2.getList("Items", 10).isEmpty()) { - nbttagcompound2.remove("Items"); - } - - int i = nbttagcompound2.getInt("Color"); - - nbttagcompound2.remove("Color"); - if (nbttagcompound2.isEmpty()) { - nbttagcompound1.remove("BlockEntityTag"); - } - - if (nbttagcompound1.isEmpty()) { - cmp.remove("tag"); - } - - cmp.putString("id", DataConverterShulkerBoxItem.a[i % 16]); - } - } - - return cmp; - } - - } - - private static class DataConverterShulkerBoxBlock implements DataConverter { - - DataConverterShulkerBoxBlock() { - } - - public int getDataVersion() { - return 813; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:shulker".equals(cmp.getString("id"))) { - cmp.remove("Color"); - } - - return cmp; - } - - } - - private static class DataConverterLang implements DataConverter { - - DataConverterLang() { - } - - public int getDataVersion() { - return 816; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if (cmp.contains("lang", 8)) { - cmp.putString("lang", cmp.getString("lang").toLowerCase(Locale.ROOT)); - } - - return cmp; - } - - } - - private static class DataConverterTotem implements DataConverter { - - DataConverterTotem() { - } - - public int getDataVersion() { - return 820; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:totem".equals(cmp.getString("id"))) { - cmp.putString("id", "minecraft:totem_of_undying"); - } - - return cmp; - } - - } - - private static class DataConverterBedBlock implements DataConverter { - - private static final Logger a = LogManager.getLogger(PaperweightDataConverters.class); - - DataConverterBedBlock() { - } - - public int getDataVersion() { - return 1125; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - try { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level"); - int i = nbttagcompound1.getInt("xPos"); - int j = nbttagcompound1.getInt("zPos"); - net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("TileEntities", 10); - net.minecraft.nbt.ListTag nbttaglist1 = nbttagcompound1.getList("Sections", 10); - - for (int k = 0; k < nbttaglist1.size(); ++k) { - net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist1.getCompound(k); - byte b0 = nbttagcompound2.getByte("Y"); - byte[] abyte = nbttagcompound2.getByteArray("Blocks"); - - for (int l = 0; l < abyte.length; ++l) { - if (416 == (abyte[l] & 255) << 4) { - int i1 = l & 15; - int j1 = l >> 8 & 15; - int k1 = l >> 4 & 15; - net.minecraft.nbt.CompoundTag nbttagcompound3 = new net.minecraft.nbt.CompoundTag(); - - nbttagcompound3.putString("id", "bed"); - nbttagcompound3.putInt("x", i1 + (i << 4)); - nbttagcompound3.putInt("y", j1 + (b0 << 4)); - nbttagcompound3.putInt("z", k1 + (j << 4)); - nbttaglist.add(nbttagcompound3); - } - } - } - } catch (Exception exception) { - DataConverterBedBlock.a.warn("Unable to datafix Bed blocks, level format may be missing tags."); - } - - return cmp; - } - - } - - private static class DataConverterBedItem implements DataConverter { - - DataConverterBedItem() { - } - - public int getDataVersion() { - return 1125; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("minecraft:bed".equals(cmp.getString("id")) && cmp.getShort("Damage") == 0) { - cmp.putShort("Damage", (short) DyeColor.RED.getId()); - } - - return cmp; - } - - } - - private static class DataConverterSignText implements DataConverter { - - public static final Gson a = new GsonBuilder().registerTypeAdapter(Component.class, new JsonDeserializer() { - MutableComponent a(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws - JsonParseException { - if (jsonelement.isJsonPrimitive()) { - return new TextComponent(jsonelement.getAsString()); - } else if (jsonelement.isJsonArray()) { - JsonArray jsonarray = jsonelement.getAsJsonArray(); - MutableComponent ichatbasecomponent = null; - Iterator iterator = jsonarray.iterator(); - - while (iterator.hasNext()) { - JsonElement jsonelement1 = (JsonElement) iterator.next(); - MutableComponent ichatbasecomponent1 = this.a( - jsonelement1, - jsonelement1.getClass(), - jsondeserializationcontext - ); - - if (ichatbasecomponent == null) { - ichatbasecomponent = ichatbasecomponent1; - } else { - ichatbasecomponent.append(ichatbasecomponent1); - } - } - - return ichatbasecomponent; - } else { - throw new JsonParseException("Don't know how to turn " + jsonelement + " into a Component"); - } - } - - public Object deserialize( - JsonElement jsonelement, - Type type, - JsonDeserializationContext jsondeserializationcontext - ) throws JsonParseException { - return this.a(jsonelement, type, jsondeserializationcontext); - } - }).create(); - - DataConverterSignText() { - } - - public int getDataVersion() { - return 101; - } - - public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { - if ("Sign".equals(cmp.getString("id"))) { - this.convert(cmp, "Text1"); - this.convert(cmp, "Text2"); - this.convert(cmp, "Text3"); - this.convert(cmp, "Text4"); - } - - return cmp; - } - - private void convert(net.minecraft.nbt.CompoundTag nbttagcompound, String s) { - String s1 = nbttagcompound.getString(s); - Component object = null; - - if (!"null".equals(s1) && !StringUtil.isNullOrEmpty(s1)) { - if ((s1.charAt(0) != 34 || s1.charAt(s1.length() - 1) != 34) && (s1.charAt(0) != 123 || s1.charAt(s1.length() - 1) != 125)) { - object = new TextComponent(s1); - } else { - try { - object = GsonHelper.fromJson(DataConverterSignText.a, s1, Component.class, true); - if (object == null) { - object = new TextComponent(""); - } - } catch (JsonParseException jsonparseexception) { - ; - } - - if (object == null) { - try { - object = Component.Serializer.fromJson(s1); - } catch (JsonParseException jsonparseexception1) { - ; - } - } - - if (object == null) { - try { - object = Component.Serializer.fromJsonLenient(s1); - } catch (JsonParseException jsonparseexception2) { - ; - } - } - - if (object == null) { - object = new TextComponent(s1); - } - } - } else { - object = new TextComponent(""); - } - - nbttagcompound.putString(s, Component.Serializer.toJson(object)); - } - - } - - private static class DataInspectorPlayerVehicle implements DataInspector { - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (cmp.contains("RootVehicle", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("RootVehicle"); - - if (nbttagcompound1.contains("Entity", 10)) { - convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); - } - } - - return cmp; - } - - } - - private static class DataInspectorLevelPlayer implements DataInspector { - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (cmp.contains("Player", 10)) { - convertCompound(LegacyType.PLAYER, cmp, "Player", sourceVer, targetVer); - } - - return cmp; - } - - } - - private static class DataInspectorStructure implements DataInspector { - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - net.minecraft.nbt.ListTag nbttaglist; - int j; - net.minecraft.nbt.CompoundTag nbttagcompound1; - - if (cmp.contains("entities", 9)) { - nbttaglist = cmp.getList("entities", 10); - - for (j = 0; j < nbttaglist.size(); ++j) { - nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j); - if (nbttagcompound1.contains("nbt", 10)) { - convertCompound(LegacyType.ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); - } - } - } - - if (cmp.contains("blocks", 9)) { - nbttaglist = cmp.getList("blocks", 10); - - for (j = 0; j < nbttaglist.size(); ++j) { - nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j); - if (nbttagcompound1.contains("nbt", 10)) { - convertCompound(LegacyType.BLOCK_ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); - } - } - } - - return cmp; - } - - } - - private static class DataInspectorChunks implements DataInspector { - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (cmp.contains("Level", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level"); - net.minecraft.nbt.ListTag nbttaglist; - int j; - - if (nbttagcompound1.contains("Entities", 9)) { - nbttaglist = nbttagcompound1.getList("Entities", 10); - - for (j = 0; j < nbttaglist.size(); ++j) { - nbttaglist.set( - j, - convert( - LegacyType.ENTITY, - (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), - sourceVer, - targetVer - ) - ); - } - } - - if (nbttagcompound1.contains("TileEntities", 9)) { - nbttaglist = nbttagcompound1.getList("TileEntities", 10); - - for (j = 0; j < nbttaglist.size(); ++j) { - nbttaglist.set( - j, - convert( - LegacyType.BLOCK_ENTITY, - (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), - sourceVer, - targetVer - ) - ); - } - } - } - - return cmp; - } - - } - - private static class DataInspectorEntityPassengers implements DataInspector { - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (cmp.contains("Passengers", 9)) { - net.minecraft.nbt.ListTag nbttaglist = cmp.getList("Passengers", 10); - - for (int j = 0; j < nbttaglist.size(); ++j) { - nbttaglist.set(j, convert(LegacyType.ENTITY, nbttaglist.getCompound(j), sourceVer, targetVer)); - } - } - - return cmp; - } - - } - - private static class DataInspectorPlayer implements DataInspector { - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - convertItems(cmp, "Inventory", sourceVer, targetVer); - convertItems(cmp, "EnderItems", sourceVer, targetVer); - if (cmp.contains("ShoulderEntityLeft", 10)) { - convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityLeft", sourceVer, targetVer); - } - - if (cmp.contains("ShoulderEntityRight", 10)) { - convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityRight", sourceVer, targetVer); - } - - return cmp; - } - - } - - private static class DataInspectorVillagers implements DataInspector { - - ResourceLocation entityVillager = getKey("EntityVillager"); - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (entityVillager.equals(new ResourceLocation(cmp.getString("id"))) && cmp.contains("Offers", 10)) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Offers"); - - if (nbttagcompound1.contains("Recipes", 9)) { - net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("Recipes", 10); - - for (int j = 0; j < nbttaglist.size(); ++j) { - net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompound(j); - - convertItem(nbttagcompound2, "buy", sourceVer, targetVer); - convertItem(nbttagcompound2, "buyB", sourceVer, targetVer); - convertItem(nbttagcompound2, "sell", sourceVer, targetVer); - nbttaglist.set(j, nbttagcompound2); - } - } - } - - return cmp; - } - - } - - private static class DataInspectorMobSpawnerMinecart implements DataInspector { - - ResourceLocation entityMinecartMobSpawner = getKey("EntityMinecartMobSpawner"); - ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner"); - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - String s = cmp.getString("id"); - if (entityMinecartMobSpawner.equals(new ResourceLocation(s))) { - cmp.putString("id", tileEntityMobSpawner.toString()); - convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer); - cmp.putString("id", s); - } - - return cmp; - } - - } - - private static class DataInspectorMobSpawnerMobs implements DataInspector { - - ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner"); - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (tileEntityMobSpawner.equals(new ResourceLocation(cmp.getString("id")))) { - if (cmp.contains("SpawnPotentials", 9)) { - net.minecraft.nbt.ListTag nbttaglist = cmp.getList("SpawnPotentials", 10); - - for (int j = 0; j < nbttaglist.size(); ++j) { - net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttaglist.getCompound(j); - - convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); - } - } - - convertCompound(LegacyType.ENTITY, cmp, "SpawnData", sourceVer, targetVer); - } - - return cmp; - } - - } - - private static class DataInspectorCommandBlock implements DataInspector { - - ResourceLocation tileEntityCommand = getKey("TileEntityCommand"); - - @Override - public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { - if (tileEntityCommand.equals(new ResourceLocation(cmp.getString("id")))) { - cmp.putString("id", "Control"); - convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer); - cmp.putString("id", "MinecartCommandBlock"); - } - - return cmp; - } - - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightFakePlayer.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightFakePlayer.java deleted file mode 100644 index 9f081b05e..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightFakePlayer.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.bukkit.adapter.ext.fawe; - -import com.mojang.authlib.GameProfile; -import net.minecraft.network.chat.ChatType; -import net.minecraft.network.chat.Component; -import net.minecraft.network.protocol.game.ServerboundClientInformationPacket; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.stats.Stat; -import net.minecraft.world.MenuProvider; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.block.entity.SignBlockEntity; -import net.minecraft.world.phys.Vec3; -import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; - -import java.util.OptionalInt; -import java.util.UUID; - -class PaperweightFakePlayer extends ServerPlayer { - - private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile( - UUID.nameUUIDFromBytes("worldedit".getBytes()), - "[WorldEdit]" - ); - private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D); - - PaperweightFakePlayer(ServerLevel world) { - super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE); - } - - @Override - public Vec3 position() { - return ORIGIN; - } - - @Override - public void tick() { - } - - @Override - public void die(DamageSource damagesource) { - } - - @Override - public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) { - return this; - } - - @Override - public OptionalInt openMenu(MenuProvider factory) { - return OptionalInt.empty(); - } - - @Override - public void updateOptions(ServerboundClientInformationPacket packet) { - } - - @Override - public void displayClientMessage(Component message, boolean actionBar) { - } - - @Override - public void sendMessage(Component message, ChatType type, UUID sender) { - } - - @Override - public void awardStat(Stat stat, int amount) { - } - - @Override - public void awardStat(Stat stat) { - } - - @Override - public boolean isInvulnerableTo(DamageSource damageSource) { - return true; - } - - @Override - public void openTextEdit(SignBlockEntity sign) { - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightWorldNativeAccess.java deleted file mode 100644 index 701e40b12..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/PaperweightWorldNativeAccess.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.bukkit.adapter.ext.fawe; - -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; -import com.sk89q.worldedit.world.block.BlockState; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.chunk.LevelChunk; -import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData; -import org.bukkit.event.block.BlockPhysicsEvent; - -import javax.annotation.Nullable; -import java.lang.ref.WeakReference; -import java.util.Objects; - -public class PaperweightWorldNativeAccess implements - WorldNativeAccess { - - private static final int UPDATE = 1; - private static final int NOTIFY = 2; - - private final PaperweightAdapter adapter; - private final WeakReference world; - private SideEffectSet sideEffectSet; - - public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference world) { - this.adapter = adapter; - this.world = world; - } - - private ServerLevel getWorld() { - return Objects.requireNonNull(world.get(), "The reference to the world was lost"); - } - - @Override - public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { - this.sideEffectSet = sideEffectSet; - } - - @Override - public LevelChunk getChunk(int x, int z) { - return getWorld().getChunk(x, z); - } - - @Override - public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) { - int stateId = BlockStateIdAccess.getBlockStateId(state); - return BlockStateIdAccess.isValidInternalId(stateId) - ? Block.stateById(stateId) - : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); - } - - @Override - public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) { - return chunk.getBlockState(position); - } - - @Nullable - @Override - public net.minecraft.world.level.block.state.BlockState setBlockState( - LevelChunk chunk, - BlockPos position, - net.minecraft.world.level.block.state.BlockState state - ) { - return chunk.setType(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE)); - } - - @Override - public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition( - net.minecraft.world.level.block.state.BlockState block, - BlockPos position - ) { - return Block.updateFromNeighbourShapes(block, getWorld(), position); - } - - @Override - public BlockPos getPosition(int x, int y, int z) { - return new BlockPos(x, y, z); - } - - @Override - public void updateLightingForBlock(BlockPos position) { - getWorld().getChunkSource().getLightEngine().checkBlock(position); - } - - @Override - public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) { - return false; - } - - @Override - public void notifyBlockUpdate( - LevelChunk chunk, - BlockPos position, - net.minecraft.world.level.block.state.BlockState oldState, - net.minecraft.world.level.block.state.BlockState newState - ) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); - } - } - - @Override - public boolean isChunkTicking(LevelChunk chunk) { - return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING); - } - - @Override - public void markBlockChanged(LevelChunk chunk, BlockPos position) { - if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { - getWorld().getChunkSource().blockChanged(position); - } - } - - @Override - public void notifyNeighbors( - BlockPos pos, - net.minecraft.world.level.block.state.BlockState oldState, - net.minecraft.world.level.block.state.BlockState newState - ) { - ServerLevel world = getWorld(); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - world.updateNeighborsAt(pos, oldState.getBlock()); - } else { - // When we don't want events, manually run the physics without them. - Block block = oldState.getBlock(); - fireNeighborChanged(pos, world, block, pos.west()); - fireNeighborChanged(pos, world, block, pos.east()); - fireNeighborChanged(pos, world, block, pos.below()); - fireNeighborChanged(pos, world, block, pos.above()); - fireNeighborChanged(pos, world, block, pos.north()); - fireNeighborChanged(pos, world, block, pos.south()); - } - if (newState.hasAnalogOutputSignal()) { - world.updateNeighbourForOutputSignal(pos, newState.getBlock()); - } - } - - private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { - world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false); - } - - @Override - public void updateNeighbors( - BlockPos pos, - net.minecraft.world.level.block.state.BlockState oldState, - net.minecraft.world.level.block.state.BlockState newState, - int recursionLimit - ) { - ServerLevel world = getWorld(); - // a == updateNeighbors - // b == updateDiagonalNeighbors - oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - CraftWorld craftWorld = world.getWorld(); - BlockPhysicsEvent event = new BlockPhysicsEvent( - craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), - CraftBlockData.fromData(newState) - ); - world.getCraftServer().getPluginManager().callEvent(event); - if (event.isCancelled()) { - return; - } - } - newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); - newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); - } - - @Override - public void onBlockStateChange( - BlockPos pos, - net.minecraft.world.level.block.state.BlockState oldState, - net.minecraft.world.level.block.state.BlockState newState - ) { - getWorld().onBlockStateChange(pos, oldState, newState); - } - - @Override - public void flush() { - - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightBlockMaterial.java deleted file mode 100644 index aac664459..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightBlockMaterial.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.util.ReflectionUtil; -import com.sk89q.worldedit.bukkit.adapter.Refraction; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag; -import com.sk89q.worldedit.world.registry.BlockMaterial; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.EmptyBlockGetter; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Material; -import net.minecraft.world.level.material.PushReaction; -import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData; - -public class PaperweightBlockMaterial implements BlockMaterial { - - private final Block block; - private final BlockState blockState; - private final Material material; - private final boolean isTranslucent; - private final CraftBlockData craftBlockData; - private final org.bukkit.Material craftMaterial; - private final int opacity; - private final CompoundTag tile; - - public PaperweightBlockMaterial(Block block) { - this(block, block.defaultBlockState()); - } - - public PaperweightBlockMaterial(Block block, BlockState blockState) { - this.block = block; - this.blockState = blockState; - this.material = blockState.getMaterial(); - this.craftBlockData = CraftBlockData.fromData(blockState); - this.craftMaterial = craftBlockData.getMaterial(); - BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block, Refraction.pickName( - "properties", "aP")); - this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo, - Refraction.pickName("canOcclude", "n") - ); - opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); - BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity( - BlockPos.ZERO, - blockState - ); - tile = tileEntity == null - ? null - : new PaperweightLazyCompoundTag(Suppliers.memoize(() -> tileEntity.save(new net.minecraft.nbt.CompoundTag()))); - } - - public Block getBlock() { - return block; - } - - public BlockState getState() { - return blockState; - } - - public CraftBlockData getCraftBlockData() { - return craftBlockData; - } - - public Material getMaterial() { - return material; - } - - @Override - public boolean isAir() { - return blockState.isAir(); - } - - @Override - public boolean isFullCube() { - return craftMaterial.isOccluding(); - } - - @Override - public boolean isOpaque() { - return material.isSolidBlocking(); - } - - @Override - public boolean isPowerSource() { - return blockState.isSignalSource(); - } - - @Override - public boolean isLiquid() { - return material.isLiquid(); - } - - @Override - public boolean isSolid() { - return material.isSolid(); - } - - @Override - public float getHardness() { - return craftBlockData.getState().destroySpeed; - } - - @Override - public float getResistance() { - return block.getExplosionResistance(); - } - - @Override - public float getSlipperiness() { - return block.getFriction(); - } - - @Override - public int getLightValue() { - return blockState.getLightEmission(); - } - - @Override - public int getLightOpacity() { - return opacity; - } - - @Override - public boolean isFragileWhenPushed() { - return material.getPushReaction() == PushReaction.DESTROY; - } - - @Override - public boolean isUnpushable() { - return material.getPushReaction() == PushReaction.BLOCK; - } - - @Override - public boolean isTicksRandomly() { - return block.isRandomlyTicking(blockState); - } - - @Override - public boolean isMovementBlocker() { - return material.isSolid(); - } - - @Override - public boolean isBurnable() { - return material.isFlammable(); - } - - @Override - public boolean isToolRequired() { - // Removed in 1.16.1, this is not present in higher versions - return false; - } - - @Override - public boolean isReplacedDuringPlacement() { - return material.isReplaceable(); - } - - @Override - public boolean isTranslucent() { - return isTranslucent; - } - - @Override - public boolean hasContainer() { - return block instanceof EntityBlock; - } - - @Override - public boolean isTile() { - return block instanceof EntityBlock; - } - - @Override - public CompoundTag getDefaultTile() { - return tile; - } - - @Override - public int getMapColor() { - // rgb field - return material.getColor().col; - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java deleted file mode 100644 index ad6e6ae80..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweAdapter.java +++ /dev/null @@ -1,671 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.bukkit.adapter.FaweAdapter; -import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory; -import com.fastasyncworldedit.core.FaweCache; -import com.fastasyncworldedit.core.entity.LazyBaseEntity; -import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; -import com.fastasyncworldedit.core.queue.IBatchProcessor; -import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket; -import com.fastasyncworldedit.core.util.NbtUtils; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.blocks.BaseItemStack; -import com.sk89q.worldedit.blocks.TileEntityBlock; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; -import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.regen.PaperweightRegen; -import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.util.LogManagerCompat; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.registry.state.BooleanProperty; -import com.sk89q.worldedit.registry.state.DirectionalProperty; -import com.sk89q.worldedit.registry.state.EnumProperty; -import com.sk89q.worldedit.registry.state.IntegerProperty; -import com.sk89q.worldedit.registry.state.Property; -import com.sk89q.worldedit.util.Direction; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.formatting.text.Component; -import com.sk89q.worldedit.util.nbt.BinaryTag; -import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; -import com.sk89q.worldedit.util.nbt.StringBinaryTag; -import com.sk89q.worldedit.world.RegenOptions; -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; -import com.sk89q.worldedit.world.block.BlockType; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldedit.world.block.BlockTypesCache; -import com.sk89q.worldedit.world.entity.EntityType; -import com.sk89q.worldedit.world.item.ItemType; -import com.sk89q.worldedit.world.registry.BlockMaterial; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; -import net.minecraft.core.WritableRegistry; -import net.minecraft.nbt.IntTag; -import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.StringRepresentable; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.block.state.properties.DirectionProperty; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import org.apache.logging.log4j.Logger; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_17_R1.CraftChunk; -import org.bukkit.craftbukkit.v1_17_R1.CraftServer; -import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity; -import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; -import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack; -import org.bukkit.craftbukkit.v1_17_R1.util.CraftNamespacedKey; -import org.bukkit.entity.Player; - -import javax.annotation.Nullable; -import java.lang.ref.WeakReference; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.OptionalInt; -import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public final class PaperweightFaweAdapter extends FaweAdapter { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); - - private final PaperweightAdapter parent; - // ------------------------------------------------------------------------ - // Code that may break between versions of Minecraft - // ------------------------------------------------------------------------ - private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil(); - private char[] ibdToStateOrdinal = null; - private int[] ordinalToIbdID = null; - private boolean initialised = false; - private Map>> allBlockProperties = null; - - public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException { - this.parent = new PaperweightAdapter(); - } - - @Nullable - private static String getEntityId(Entity entity) { - ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType()); - return resourceLocation == null ? null : resourceLocation.toString(); - } - - @Override - public BukkitImplAdapter getParent() { - return parent; - } - - @SuppressWarnings("unchecked") - private synchronized boolean init() { - if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) { - return false; - } - ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size - ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size - for (int i = 0; i < ibdToStateOrdinal.length; i++) { - BlockState blockState = BlockTypesCache.states[i]; - PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial(); - int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState()); - char ordinal = blockState.getOrdinalChar(); - ibdToStateOrdinal[id] = ordinal; - ordinalToIbdID[ordinal] = id; - } - Map>> properties = new HashMap<>(); - try { - for (Field field : BlockStateProperties.class.getDeclaredFields()) { - Object obj = field.get(null); - if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property state)) { - continue; - } - Property property; - if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { - property = new BooleanProperty( - state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); - } else if (state instanceof DirectionProperty) { - property = new DirectionalProperty( - state.getName(), - state - .getPossibleValues() - .stream() - .map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase())) - .collect(Collectors.toList()) - ); - } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { - property = new EnumProperty( - state.getName(), - state - .getPossibleValues() - .stream() - .map(e -> ((StringRepresentable) e).getSerializedName()) - .collect(Collectors.toList()) - ); - } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { - property = new IntegerProperty( - state.getName(), - (List) ImmutableList.copyOf(state.getPossibleValues()) - ); - } else { - throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state - .getClass() - .getSimpleName()); - } - properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> { - if (v == null) { - v = new ArrayList<>(Collections.singletonList(property)); - } else { - v.add(property); - } - return v; - }); - } - } catch (IllegalAccessException e) { - e.printStackTrace(); - } finally { - allBlockProperties = ImmutableMap.copyOf(properties); - } - initialised = true; - return true; - } - - @Override - public BlockMaterial getMaterial(BlockType blockType) { - Block block = getBlock(blockType); - return new PaperweightBlockMaterial(block); - } - - @Override - public synchronized BlockMaterial getMaterial(BlockState state) { - net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); - return new PaperweightBlockMaterial(blockState.getBlock(), blockState); - } - - public Block getBlock(BlockType blockType) { - return Registry.BLOCK.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource())); - } - - @Deprecated - @Override - public BlockState getBlock(Location location) { - Preconditions.checkNotNull(location); - - int x = location.getBlockX(); - int y = location.getBlockY(); - int z = location.getBlockZ(); - final ServerLevel handle = getServerLevel(location.getWorld()); - LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); - final BlockPos blockPos = new BlockPos(x, y, z); - final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); - BlockState state = adapt(blockData); - if (state == null) { - org.bukkit.block.Block bukkitBlock = location.getBlock(); - state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); - } - return state; - } - - @Override - public BaseBlock getFullBlock(final Location location) { - Preconditions.checkNotNull(location); - - int x = location.getBlockX(); - int y = location.getBlockY(); - int z = location.getBlockZ(); - - final ServerLevel handle = getServerLevel(location.getWorld()); - LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); - final BlockPos blockPos = new BlockPos(x, y, z); - final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); - BlockState state = adapt(blockData); - if (state == null) { - org.bukkit.block.Block bukkitBlock = location.getBlock(); - state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); - } - if (state.getBlockType().getMaterial().hasContainer()) { - - // Read the NBT data - BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK); - if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = blockEntity.save(new net.minecraft.nbt.CompoundTag()); - return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag)); - } - } - - return state.toBaseBlock(); - } - - @Override - public Set getSupportedSideEffects() { - return SideEffectSet.defaults().getSideEffectsToApply(); - } - - @SuppressWarnings("rawtypes") - public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) { - CraftChunk craftChunk = (CraftChunk) chunk; - LevelChunk levelChunk = craftChunk.getHandle(); - Level level = levelChunk.getLevel(); - - BlockPos blockPos = new BlockPos(x, y, z); - net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState(); - LevelChunkSection[] levelChunkSections = levelChunk.getSections(); - int y4 = y >> 4; - LevelChunkSection section = levelChunkSections[y4]; - - net.minecraft.world.level.block.state.BlockState existing; - if (section == null) { - existing = ((PaperweightBlockMaterial) BlockTypes.AIR.getDefaultState().getMaterial()).getState(); - } else { - existing = section.getBlockState(x & 15, y & 15, z & 15); - } - - levelChunk.removeBlockEntity(blockPos); // Force delete the old tile entity - - CompoundBinaryTag compoundTag = state instanceof BaseBlock ? state.getNbt() : null; - if (compoundTag != null || existing instanceof TileEntityBlock) { - level.setBlock(blockPos, blockState, 0); - // remove tile - if (compoundTag != null) { - // We will assume that the tile entity was created for us, - // though we do not do this on the Forge version - BlockEntity blockEntity = level.getBlockEntity(blockPos); - if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(compoundTag); - tag.put("x", IntTag.valueOf(x)); - tag.put("y", IntTag.valueOf(y)); - tag.put("z", IntTag.valueOf(z)); - blockEntity.load(tag); // readTagIntoTileEntity - load data - } - } - } else { - if (existing == blockState) { - return true; - } - if (section == null) { - if (blockState.isAir()) { - return true; - } - levelChunkSections[y4] = section = new LevelChunkSection(y4 << 4); - } - levelChunk.setBlockState(blockPos, blockState, false); - } - if (update) { - level.getMinecraftWorld().sendBlockUpdated(blockPos, existing, blockState, 0); - } - return true; - } - - @Override - public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { - return new PaperweightFaweWorldNativeAccess(this, new WeakReference<>(getServerLevel(world))); - } - - @Override - public BaseEntity getEntity(org.bukkit.entity.Entity entity) { - Preconditions.checkNotNull(entity); - - CraftEntity craftEntity = ((CraftEntity) entity); - Entity mcEntity = craftEntity.getHandle(); - - String id = getEntityId(mcEntity); - - if (id != null) { - EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); - Supplier saveTag = () -> { - final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag(); - PaperweightPlatformAdapter.readEntityIntoTag(mcEntity, minecraftTag); - //add Id for AbstractChangeSet to work - final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag); - final Map tags = NbtUtils.getCompoundBinaryTagValues(tag); - tags.put("Id", StringBinaryTag.of(id)); - return CompoundBinaryTag.from(tags); - }; - return new LazyBaseEntity(type, saveTag); - } else { - return null; - } - } - - @Override - public Component getRichBlockName(BlockType blockType) { - return parent.getRichBlockName(blockType); - } - - @Override - public Component getRichItemName(ItemType itemType) { - return parent.getRichItemName(itemType); - } - - @Override - public Component getRichItemName(BaseItemStack itemStack) { - return parent.getRichItemName(itemStack); - } - - @Override - public OptionalInt getInternalBlockStateId(BlockState state) { - PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial(); - net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState(); - return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState)); - } - - @Override - public BlockState adapt(BlockData blockData) { - CraftBlockData cbd = ((CraftBlockData) blockData); - net.minecraft.world.level.block.state.BlockState ibd = cbd.getState(); - return adapt(ibd); - } - - public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) { - return BlockTypesCache.states[adaptToChar(blockState)]; - } - - public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) { - int id = Block.BLOCK_STATE_REGISTRY.getId(blockState); - if (initialised) { - return ibdToStateOrdinal[id]; - } - synchronized (this) { - if (initialised) { - return ibdToStateOrdinal[id]; - } - try { - init(); - return ibdToStateOrdinal[id]; - } catch (ArrayIndexOutOfBoundsException e1) { - LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!", - blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1 - ); - return BlockTypesCache.ReservedIDs.AIR; - } - } - } - - public char ibdIDToOrdinal(int id) { - if (initialised) { - return ibdToStateOrdinal[id]; - } - synchronized (this) { - if (initialised) { - return ibdToStateOrdinal[id]; - } - init(); - return ibdToStateOrdinal[id]; - } - } - - @Override - public char[] getIbdToStateOrdinal() { - if (initialised) { - return ibdToStateOrdinal; - } - synchronized (this) { - if (initialised) { - return ibdToStateOrdinal; - } - init(); - return ibdToStateOrdinal; - } - } - - public int ordinalToIbdID(char ordinal) { - if (initialised) { - return ordinalToIbdID[ordinal]; - } - synchronized (this) { - if (initialised) { - return ordinalToIbdID[ordinal]; - } - init(); - return ordinalToIbdID[ordinal]; - } - } - - @Override - public int[] getOrdinalToIbdID() { - if (initialised) { - return ordinalToIbdID; - } - synchronized (this) { - if (initialised) { - return ordinalToIbdID; - } - init(); - return ordinalToIbdID; - } - } - - @Override - public > BlockData adapt(B state) { - PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial(); - return material.getCraftBlockData(); - } - - @Override - public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { - ServerLevel nmsWorld = getServerLevel(world); - ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); - if (map != null && map.wasAccessibleSinceLastSave()) { - boolean flag = false; - // PlayerChunk.d players = map.players; - Stream stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) - */ Stream.empty(); - - ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle(); - stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer) - .forEach(entityPlayer -> { - synchronized (chunkPacket) { - ClientboundLevelChunkPacket nmsPacket = (ClientboundLevelChunkPacket) chunkPacket.getNativePacket(); - if (nmsPacket == null) { - nmsPacket = mapUtil.create(this, chunkPacket); - chunkPacket.setNativePacket(nmsPacket); - } - try { - FaweCache.INSTANCE.CHUNK_FLAG.get().set(true); - entityPlayer.connection.send(nmsPacket); - } finally { - FaweCache.INSTANCE.CHUNK_FLAG.get().set(false); - } - } - }); - } - } - - @Override - public Map> getProperties(BlockType blockType) { - return getParent().getProperties(blockType); - } - - @Override - public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) { - int internalId = BlockStateIdAccess.getBlockStateId(blockState); - net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId); - return blockState1.hasPostProcess( - getServerLevel(world), - new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ()) - ); - } - - @Override - public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) { - ItemStack stack = new ItemStack( - Registry.ITEM.get(ResourceLocation.tryParse(baseItemStack.getType().getId())), - baseItemStack.getAmount() - ); - stack.setTag(((net.minecraft.nbt.CompoundTag) fromNativeBinary(baseItemStack.getNbt()))); - return CraftItemStack.asCraftMirror(stack); - } - - @Override - protected void preCaptureStates(final ServerLevel serverLevel) { - serverLevel.captureTreeGeneration = true; - serverLevel.captureBlockStates = true; - } - - @Override - protected List getCapturedBlockStatesCopy(final ServerLevel serverLevel) { - return new ArrayList<>(serverLevel.capturedBlockStates.values()); - } - - @Override - protected void postCaptureBlockStates(final ServerLevel serverLevel) { - serverLevel.captureBlockStates = false; - serverLevel.captureTreeGeneration = false; - serverLevel.capturedBlockStates.clear(); - } - - @Override - protected ServerLevel getServerLevel(final World world) { - return ((CraftWorld) world).getHandle(); - } - - @Override - public List getEntities(org.bukkit.World world) { - // Quickly add each entity to a list copy. - List mcEntities = new ArrayList<>(); - getServerLevel(world).entityManager.getEntityGetter().getAll().forEach(mcEntities::add); - - List list = new ArrayList<>(); - mcEntities.forEach((mcEnt) -> { - org.bukkit.entity.Entity bukkitEntity = mcEnt.getBukkitEntity(); - if (bukkitEntity.isValid()) { - list.add(bukkitEntity); - } - - }); - return list; - } - - @Override - public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { - final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); - final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount()); - weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag()))); - return weStack; - } - - @Override - public Tag toNative(net.minecraft.nbt.Tag foreign) { - return parent.toNative(foreign); - } - - @Override - public net.minecraft.nbt.Tag fromNative(Tag foreign) { - if (foreign instanceof PaperweightLazyCompoundTag) { - return ((PaperweightLazyCompoundTag) foreign).get(); - } - return parent.fromNative(foreign); - } - - @Override - public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception { - return new PaperweightRegen(bukkitWorld, region, target, options).regenerate(); - } - - @Override - public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) { - return new PaperweightGetBlocks(world, chunkX, chunkZ); - } - - @Override - public int getInternalBiomeId(BiomeType biomeType) { - final Registry registry = MinecraftServer - .getServer() - .registryAccess() - .ownedRegistryOrThrow(Registry.BIOME_REGISTRY); - ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId()); - Biome biome = registry.get(resourceLocation); - return registry.getId(biome); - } - - @Override - public Iterable getRegisteredBiomes() { - WritableRegistry biomeRegistry = ((CraftServer) Bukkit.getServer()) - .getServer() - .registryAccess() - .ownedRegistryOrThrow( - Registry.BIOME_REGISTRY); - List keys = biomeRegistry.stream() - .map(biomeRegistry::getKey).filter(Objects::nonNull).toList(); - List namespacedKeys = new ArrayList<>(); - for (ResourceLocation key : keys) { - try { - namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key)); - } catch (IllegalArgumentException e) { - LOGGER.error("Error converting biome key {}", key.toString(), e); - } - } - return namespacedKeys; - } - - @Override - public RelighterFactory getRelighterFactory() { - try { - Class.forName("ca.spottedleaf.starlight.light.StarLightEngine"); - if (PaperweightStarlightRelighter.isUsable()) { - return new PaperweightStarlightRelighterFactory(); - } - } catch (ThreadDeath td) { - throw td; - } catch (Throwable ignored) { - - } - return new NMSRelighterFactory(); - } - - @Override - public Map>> getAllProperties() { - if (initialised) { - return allBlockProperties; - } - synchronized (this) { - if (initialised) { - return allBlockProperties; - } - init(); - return allBlockProperties; - } - } - - @Override - public IBatchProcessor getTickingPostProcessor() { - return new PaperweightPostProcessor(); - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweWorldNativeAccess.java deleted file mode 100644 index 282a6b505..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightFaweWorldNativeAccess.java +++ /dev/null @@ -1,286 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.core.Fawe; -import com.fastasyncworldedit.core.math.IntPair; -import com.fastasyncworldedit.core.util.TaskManager; -import com.fastasyncworldedit.core.util.task.RunnableVal; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.internal.block.BlockStateIdAccess; -import com.sk89q.worldedit.internal.wna.WorldNativeAccess; -import com.sk89q.worldedit.util.SideEffect; -import com.sk89q.worldedit.util.SideEffectSet; -import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; -import com.sk89q.worldedit.world.block.BlockState; -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.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.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_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; -import java.util.concurrent.atomic.AtomicInteger; - -public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess { - - private static final int UPDATE = 1; - private static final int NOTIFY = 2; - private static final Direction[] NEIGHBOUR_ORDER = { - Direction.EAST, - Direction.WEST, - Direction.DOWN, - Direction.UP, - Direction.NORTH, - Direction.SOUTH - }; - private final PaperweightFaweAdapter paperweightFaweAdapter; - private final WeakReference level; - private final AtomicInteger lastTick; - private final Set cachedChanges = new HashSet<>(); - private final Set cachedChunksToSend = new HashSet<>(); - private SideEffectSet sideEffectSet; - - public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference level) { - this.paperweightFaweAdapter = paperweightFaweAdapter; - 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); - } - - private Level getLevel() { - return Objects.requireNonNull(level.get(), "The reference to the world was lost"); - } - - @Override - public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { - this.sideEffectSet = sideEffectSet; - } - - @Override - public LevelChunk getChunk(int x, int z) { - return getLevel().getChunk(x, z); - } - - @Override - public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) { - int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar()); - return BlockStateIdAccess.isValidInternalId(stateId) - ? Block.stateById(stateId) - : ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState(); - } - - @Override - public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) { - return levelChunk.getBlockState(blockPos); - } - - @Nullable - @Override - public synchronized net.minecraft.world.level.block.state.BlockState setBlockState( - LevelChunk levelChunk, BlockPos blockPos, - net.minecraft.world.level.block.state.BlockState blockState - ) { - int currentTick = MinecraftServer.currentTick; - if (Fawe.isMainThread()) { - 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.bukkitChunk.getX(), levelChunk.bukkitChunk.getZ())); - boolean nextTick = lastTick.get() > currentTick; - if (nextTick || cachedChanges.size() >= 1024) { - if (nextTick) { - lastTick.set(currentTick); - } - flushAsync(nextTick); - } - return blockState; - } - - @Override - public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition( - net.minecraft.world.level.block.state.BlockState blockState, - BlockPos blockPos - ) { - return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos); - } - - @Override - public BlockPos getPosition(int x, int y, int z) { - return new BlockPos(x, y, z); - } - - @Override - public void updateLightingForBlock(BlockPos blockPos) { - getLevel().getChunkSource().getLightEngine().checkBlock(blockPos); - } - - @Override - public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) { - // We will assume that the tile entity was created for us, - // though we do not do this on the other versions - BlockEntity blockEntity = getLevel().getBlockEntity(blockPos); - if (blockEntity == null) { - return false; - } - net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeBinary(tag); - blockEntity.load((CompoundTag) nativeTag); - return true; - } - - @Override - public void notifyBlockUpdate( - LevelChunk levelChunk, BlockPos blockPos, - net.minecraft.world.level.block.state.BlockState oldState, - net.minecraft.world.level.block.state.BlockState newState - ) { - if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) { - getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY); - } - } - - @Override - public boolean isChunkTicking(LevelChunk levelChunk) { - return levelChunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING); - } - - @Override - public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) { - if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) { - ((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos); - } - } - - @Override - public void notifyNeighbors( - BlockPos blockPos, - net.minecraft.world.level.block.state.BlockState oldState, - net.minecraft.world.level.block.state.BlockState newState - ) { - Level level = getLevel(); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - level.blockUpdated(blockPos, oldState.getBlock()); - } else { - // When we don't want events, manually run the physics without them. - // Un-nest neighbour updating - for (Direction direction : NEIGHBOUR_ORDER) { - BlockPos shifted = blockPos.relative(direction); - level.getBlockState(shifted).neighborChanged(level, shifted, oldState.getBlock(), blockPos, false); - } - } - if (newState.hasAnalogOutputSignal()) { - level.updateNeighbourForOutputSignal(blockPos, newState.getBlock()); - } - } - - @Override - public void updateNeighbors( - BlockPos blockPos, - net.minecraft.world.level.block.state.BlockState oldState, - net.minecraft.world.level.block.state.BlockState newState, - int recursionLimit - ) { - Level level = getLevel(); - // a == updateNeighbors - // b == updateDiagonalNeighbors - oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit); - if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { - CraftWorld craftWorld = level.getWorld(); - if (craftWorld != null) { - BlockPhysicsEvent event = new BlockPhysicsEvent( - craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()), - CraftBlockData.fromData(newState) - ); - level.getCraftServer().getPluginManager().callEvent(event); - if (event.isCancelled()) { - return; - } - } - } - newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit); - newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit); - } - - @Override - public void onBlockStateChange( - BlockPos blockPos, - net.minecraft.world.level.block.state.BlockState oldState, - net.minecraft.world.level.block.state.BlockState newState - ) { - 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(); - } - RunnableVal runnableVal = new RunnableVal<>() { - @Override - public void run(Object value) { - changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState, - sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE) - )); - if (!sendChunks) { - return; - } - for (IntPair chunk : toSend) { - PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false); - } - } - }; - TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal)); - } - - @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(); - } - - private record CachedChange( - LevelChunk levelChunk, - BlockPos blockPos, - net.minecraft.world.level.block.state.BlockState blockState - ) { - - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java deleted file mode 100644 index 214a39bc6..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks.java +++ /dev/null @@ -1,1096 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.bukkit.adapter.BukkitGetBlocks; -import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; -import com.fastasyncworldedit.core.Fawe; -import com.fastasyncworldedit.core.FaweCache; -import com.fastasyncworldedit.core.configuration.Settings; -import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; -import com.fastasyncworldedit.core.math.BitArrayUnstretched; -import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.queue.IChunkSet; -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.collection.AdaptedMap; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.bukkit.WorldEditPlugin; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag; -import com.sk89q.worldedit.internal.Constants; -import com.sk89q.worldedit.internal.util.LogManagerCompat; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.block.BlockTypesCache; -import io.papermc.lib.PaperLib; -import io.papermc.paper.event.block.BeaconDeactivatedEvent; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; -import net.minecraft.nbt.IntTag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.util.BitStorage; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.entity.BeaconBlockEntity; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkBiomeContainer; -import net.minecraft.world.level.chunk.DataLayer; -import net.minecraft.world.level.chunk.HashMapPalette; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.LinearPalette; -import net.minecraft.world.level.chunk.Palette; -import net.minecraft.world.level.chunk.PalettedContainer; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.lighting.LevelLightEngine; -import org.apache.logging.log4j.Logger; -import org.bukkit.World; -import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlock; -import org.bukkit.event.entity.CreatureSpawnEvent; - -import javax.annotation.Nonnull; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); - - private static final Function posNms2We = v -> BlockVector3.at(v.getX(), v.getY(), v.getZ()); - private static final Function nmsTile2We = - tileEntity -> new PaperweightLazyCompoundTag(Suppliers.memoize( - () -> tileEntity.save(new net.minecraft.nbt.CompoundTag()))); - private final PaperweightFaweAdapter adapter = ((PaperweightFaweAdapter) WorldEditPlugin - .getInstance() - .getBukkitImplAdapter()); - private final ReadWriteLock sectionLock = new ReentrantReadWriteLock(); - private final ReentrantLock callLock = new ReentrantLock(); - private final ServerLevel serverLevel; - private final int chunkX; - private final int chunkZ; - private final int minHeight; - private final int maxHeight; - private final int minSectionPosition; - private final int maxSectionPosition; - private final ConcurrentHashMap copies = new ConcurrentHashMap<>(); - private final Object sendLock = new Object(); - private LevelChunkSection[] sections; - private LevelChunk levelChunk; - private DataLayer[] blockLight; - private DataLayer[] skyLight; - private boolean createCopy = false; - private boolean forceLoadSections = true; - private boolean lightUpdate = false; - private int copyKey = 0; - - public PaperweightGetBlocks(World world, int chunkX, int chunkZ) { - this(((CraftWorld) world).getHandle(), chunkX, chunkZ); - } - - public PaperweightGetBlocks(ServerLevel serverLevel, int chunkX, int chunkZ) { - super(serverLevel.getMinBuildHeight() >> 4, (serverLevel.getMaxBuildHeight() - 1) >> 4); - this.serverLevel = serverLevel; - this.chunkX = chunkX; - this.chunkZ = chunkZ; - this.minHeight = serverLevel.getMinBuildHeight(); - this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.minSectionPosition = minHeight >> 4; - this.maxSectionPosition = maxHeight >> 4; - this.skyLight = new DataLayer[getSectionCount()]; - this.blockLight = new DataLayer[getSectionCount()]; - } - - public int getChunkX() { - return chunkX; - } - - public int getChunkZ() { - return chunkZ; - } - - @Override - public boolean isCreateCopy() { - return createCopy; - } - - @Override - public int setCreateCopy(boolean createCopy) { - if (!callLock.isHeldByCurrentThread()) { - throw new IllegalStateException("Attempting to set if chunk GET should create copy, but it is not call-locked."); - } - this.createCopy = createCopy; - // Increment regardless of whether copy will be created or not to return null from getCopy() - return ++this.copyKey; - } - - @Override - public IChunkGet getCopy(final int key) { - return copies.remove(key); - } - - @Override - public void lockCall() { - this.callLock.lock(); - } - - @Override - public void unlockCall() { - this.callLock.unlock(); - } - - @Override - public void setLightingToGet(char[][] light, int minSectionPosition, int maxSectionPosition) { - if (light != null) { - lightUpdate = true; - try { - fillLightNibble(light, LightLayer.BLOCK, minSectionPosition, maxSectionPosition); - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - - @Override - public void setSkyLightingToGet(char[][] light, int minSectionPosition, int maxSectionPosition) { - if (light != null) { - lightUpdate = true; - try { - fillLightNibble(light, LightLayer.SKY, minSectionPosition, maxSectionPosition); - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - - @Override - public void setHeightmapToGet(HeightMapType type, int[] data) { - // height + 1 to match server internal - BitArrayUnstretched bitArray = new BitArrayUnstretched(MathMan.log2nlz(getChunk().getHeight() + 1), 256); - bitArray.fromRaw(data); - Heightmap.Types nativeType = Heightmap.Types.valueOf(type.name()); - Heightmap heightMap = getChunk().heightmaps.get(nativeType); - heightMap.setRawData(getChunk(), nativeType, bitArray.getData()); - } - - @Override - public int getMaxY() { - return maxHeight; - } - - @Override - public int getMinY() { - return minHeight; - } - - @Override - public BiomeType getBiomeType(int x, int y, int z) { - ChunkBiomeContainer index = getChunk().getBiomes(); - Biome biomes = null; - if (y == -1) { - for (y = serverLevel.getMinBuildHeight(); y < serverLevel.getMaxBuildHeight(); y += 4) { - biomes = index.getNoiseBiome(x >> 2, y >> 2, z >> 2); - if (biomes != null) { - break; - } - } - } else { - biomes = index.getNoiseBiome(x >> 2, y >> 2, z >> 2); - } - return biomes != null ? PaperweightPlatformAdapter.adapt(biomes, serverLevel) : null; - } - - @Override - public void removeSectionLighting(int layer, boolean sky) { - SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getDataLayerData( - sectionPos); - if (dataLayer != null) { - lightUpdate = true; - synchronized (dataLayer) { - byte[] bytes = PaperLib.isPaper() ? dataLayer.getIfSet() : dataLayer.getData(); - if (!PaperLib.isPaper() || bytes != DataLayer.EMPTY_NIBBLE) { - Arrays.fill(bytes, (byte) 0); - } - } - } - if (sky) { - SectionPos sectionPos1 = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer1 = serverLevel - .getChunkSource() - .getLightEngine() - .getLayerListener(LightLayer.SKY) - .getDataLayerData(sectionPos1); - if (dataLayer1 != null) { - lightUpdate = true; - synchronized (dataLayer1) { - byte[] bytes = PaperLib.isPaper() ? dataLayer1.getIfSet() : dataLayer1.getData(); - if (!PaperLib.isPaper() || bytes != DataLayer.EMPTY_NIBBLE) { - Arrays.fill(bytes, (byte) 0); - } - } - } - } - } - - @Override - public CompoundTag getTile(int x, int y, int z) { - BlockEntity blockEntity = getChunk().getBlockEntity(new BlockPos((x & 15) + ( - chunkX << 4), y, (z & 15) + ( - chunkZ << 4))); - if (blockEntity == null) { - return null; - } - return new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.save(new net.minecraft.nbt.CompoundTag()))); - } - - @Override - public Map getTiles() { - Map nmsTiles = getChunk().getBlockEntities(); - if (nmsTiles.isEmpty()) { - return Collections.emptyMap(); - } - return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); - } - - @Override - public int getSkyLight(int x, int y, int z) { - int layer = y >> 4; - int alayer = layer - getMinSectionPosition(); - if (skyLight[alayer] == null) { - SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = - serverLevel.getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getDataLayerData(sectionPos); - // If the server hasn't generated the section's NibbleArray yet, it will be null - if (dataLayer == null) { - byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. - Arrays.fill(LAYER_COUNT, (byte) 15); - dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( - LightLayer.BLOCK, - sectionPos, - dataLayer, - true - ); - } - skyLight[alayer] = dataLayer; - } - return skyLight[alayer].get(x & 15, y & 15, z & 15); - } - - @Override - public int getEmittedLight(int x, int y, int z) { - int layer = y >> 4; - int alayer = layer - getMinSectionPosition(); - if (blockLight[alayer] == null) { - serverLevel.getRawBrightness(new BlockPos(1, 1, 1), 5); - SectionPos sectionPos = SectionPos.of(getChunk().getPos(), layer); - DataLayer dataLayer = serverLevel - .getChunkSource() - .getLightEngine() - .getLayerListener(LightLayer.BLOCK) - .getDataLayerData(sectionPos); - // If the server hasn't generated the section's DataLayer yet, it will be null - if (dataLayer == null) { - byte[] LAYER_COUNT = new byte[2048]; - // Safe enough to assume if it's not created, it's under the sky. Unlikely to be created before lighting is fixed anyway. - Arrays.fill(LAYER_COUNT, (byte) 15); - dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData(LightLayer.BLOCK, sectionPos, - dataLayer, true - ); - } - blockLight[alayer] = dataLayer; - } - return blockLight[alayer].get(x & 15, y & 15, z & 15); - } - - @Override - public int[] getHeightMap(HeightMapType type) { - long[] longArray = getChunk().heightmaps.get(Heightmap.Types.valueOf(type.name())).getRawData(); - BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256, longArray); - return bitArray.toRaw(new int[256]); - } - - @Override - public CompoundTag getEntity(UUID uuid) { - Entity entity = serverLevel.getEntity(uuid); - if (entity != null) { - org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); - return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); - } - for (CompoundTag tag : getEntities()) { - if (uuid.equals(tag.getUUID())) { - return tag; - } - } - return null; - } - - @Override - public Set getEntities() { - List entities = PaperweightPlatformAdapter.getEntities(getChunk()); - if (entities.isEmpty()) { - return Collections.emptySet(); - } - int size = entities.size(); - return new AbstractSet<>() { - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public boolean contains(Object get) { - if (!(get instanceof CompoundTag getTag)) { - return false; - } - UUID getUUID = getTag.getUUID(); - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (uuid.equals(getUUID)) { - return true; - } - } - return false; - } - - @Nonnull - @Override - public Iterator iterator() { - Iterable result = entities.stream().map(input -> { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - PaperweightPlatformAdapter.readEntityIntoTag(input, tag); - return (CompoundTag) adapter.toNative(tag); - }).collect(Collectors.toList()); - return result.iterator(); - } - }; - } - - private void removeEntity(Entity entity) { - entity.discard(); - } - - public LevelChunk ensureLoaded(ServerLevel nmsWorld, int chunkX, int chunkZ) { - return PaperweightPlatformAdapter.ensureLoaded(nmsWorld, chunkX, chunkZ); - } - - @Override - @SuppressWarnings("rawtypes") - public synchronized > T call(IChunkSet set, Runnable finalizer) { - if (!callLock.isHeldByCurrentThread()) { - throw new IllegalStateException("Attempted to call chunk GET but chunk was not call-locked."); - } - forceLoadSections = false; - PaperweightGetBlocks_Copy copy = createCopy ? new PaperweightGetBlocks_Copy(levelChunk) : null; - if (createCopy) { - if (copies.containsKey(copyKey)) { - throw new IllegalStateException("Copy key already used."); - } - copies.put(copyKey, copy); - } - try { - ServerLevel nmsWorld = serverLevel; - LevelChunk nmsChunk = ensureLoaded(nmsWorld, chunkX, chunkZ); - boolean fastmode = set.isFastMode() && Settings.settings().QUEUE.NO_TICK_FASTMODE; - - // Remove existing tiles. Create a copy so that we can remove blocks - Map chunkTiles = new HashMap<>(nmsChunk.getBlockEntities()); - List beacons = null; - if (!chunkTiles.isEmpty()) { - for (Map.Entry entry : chunkTiles.entrySet()) { - final BlockPos pos = entry.getKey(); - final int lx = pos.getX() & 15; - final int ly = pos.getY(); - final int lz = pos.getZ() & 15; - final int layer = ly >> 4; - if (!set.hasSection(layer)) { - continue; - } - - int ordinal = set.getBlock(lx, ly, lz).getOrdinal(); - if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) { - BlockEntity tile = entry.getValue(); - if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) { - if (beacons == null) { - beacons = new ArrayList<>(); - } - beacons.add(tile); - PaperweightPlatformAdapter.removeBeacon(tile, nmsChunk); - continue; - } - nmsChunk.removeBlockEntity(tile.getBlockPos()); - if (createCopy) { - copy.storeTile(tile); - } - } - } - } - - int bitMask = 0; - synchronized (nmsChunk) { - LevelChunkSection[] levelChunkSections = nmsChunk.getSections(); - - for (int layerNo = getMinSectionPosition(); layerNo <= getMaxSectionPosition(); layerNo++) { - if (!set.hasSection(layerNo)) { - continue; - } - int layer = layerNo - getMinSectionPosition(); - - bitMask |= 1 << layer; - - // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to - // this chunk GET when #updateGet is called. Future dords, please listen this time. - char[] tmp = set.load(layerNo); - char[] setArr = new char[tmp.length]; - System.arraycopy(tmp, 0, setArr, 0, tmp.length); - - // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was - // submitted to keep loaded internal chunks to queue target size. - synchronized (super.sectionLocks[layer]) { - if (createCopy) { - char[] tmpLoad = loadPrivately(layerNo); - char[] copyArr = new char[4096]; - System.arraycopy(tmpLoad, 0, copyArr, 0, 4096); - copy.storeSection(layer, copyArr); - } - - LevelChunkSection newSection; - LevelChunkSection existingSection = levelChunkSections[layer]; - // Don't attempt to tick section whilst we're editing - if (existingSection != null) { - PaperweightPlatformAdapter.clearCounts(existingSection); - if (PaperLib.isPaper()) { - existingSection.tickingList.clear(); - } - } - - if (existingSection == null) { - newSection = PaperweightPlatformAdapter.newChunkSection(layerNo, setArr, fastmode, adapter); - if (PaperweightPlatformAdapter.setSectionAtomic(levelChunkSections, null, newSection, layer)) { - updateGet(nmsChunk, levelChunkSections, newSection, setArr, layer); - continue; - } else { - existingSection = levelChunkSections[layer]; - if (existingSection == null) { - LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - +layer - ); - continue; - } - } - } - - //ensure that the server doesn't try to tick the chunksection while we're editing it (again). - PaperweightPlatformAdapter.clearCounts(existingSection); - if (PaperLib.isPaper()) { - existingSection.tickingList.clear(); - } - DelegateSemaphore lock = PaperweightPlatformAdapter.applyLock(existingSection); - - // Synchronize to prevent further acquisitions - synchronized (lock) { - lock.acquire(); // Wait until we have the lock - lock.release(); - try { - sectionLock.writeLock().lock(); - if (this.getChunk() != nmsChunk) { - this.levelChunk = nmsChunk; - this.sections = null; - this.reset(); - } else if (existingSection != getSections(false)[layer]) { - this.sections[layer] = existingSection; - this.reset(); - } else if (!Arrays.equals(update(layer, new char[4096], true), loadPrivately(layerNo))) { - this.reset(layerNo); - /*} else if (lock.isModified()) { - this.reset(layerNo);*/ - } - } finally { - sectionLock.writeLock().unlock(); - } - newSection = - PaperweightPlatformAdapter.newChunkSection( - layerNo, - this::loadPrivately, - setArr, - fastmode, - adapter - ); - if (!PaperweightPlatformAdapter.setSectionAtomic( - levelChunkSections, - existingSection, - newSection, - layer - )) { - LOGGER.error("Skipping invalid null section. chunk: {}, {} layer: {}", chunkX, chunkZ, - +layer - ); - } else { - updateGet(nmsChunk, levelChunkSections, newSection, setArr, layer); - } - } - } - } - - // Biomes - BiomeType[][] biomes = set.getBiomes(); - if (biomes != null) { - // set biomes - ChunkBiomeContainer currentBiomes = nmsChunk.getBiomes(); - if (createCopy) { - copy.storeBiomes(currentBiomes); - } - for (int layer = 0; layer < 16; layer++) { - if (biomes[layer] == null) { - continue; - } - for (int y = 0, i = 0; y < 4; y++) { - for (int z = 0; z < 4; z++) { - for (int x = 0; x < 4; x++, i++) { - final BiomeType biome = biomes[layer][i]; - if (biome != null) { - Biome nmsBiome = - nmsWorld.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).get( - ResourceLocation.tryParse(biome.getId())); - if (nmsBiome == null) { - throw new NullPointerException("BiomeBase null for BiomeType " + biome.getId()); - } - currentBiomes.setBiome(x, (layer << 2) + y, z, nmsBiome); - } - } - } - } - } - } - - Map heightMaps = set.getHeightMaps(); - for (Map.Entry entry : heightMaps.entrySet()) { - PaperweightGetBlocks.this.setHeightmapToGet(entry.getKey(), entry.getValue()); - } - PaperweightGetBlocks.this.setLightingToGet( - set.getLight(), - set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); - PaperweightGetBlocks.this.setSkyLightingToGet( - set.getSkyLight(), - set.getMinSectionPosition(), - set.getMaxSectionPosition() - ); - - Runnable[] syncTasks = null; - - int bx = chunkX << 4; - int bz = chunkZ << 4; - - // Call beacon deactivate events here synchronously - // list will be null on spigot, so this is an implicit isPaper check - if (beacons != null && !beacons.isEmpty()) { - final List finalBeacons = beacons; - - syncTasks = new Runnable[4]; - - syncTasks[3] = () -> { - for (BlockEntity beacon : finalBeacons) { - BeaconBlockEntity.playSound(beacon.getLevel(), beacon.getBlockPos(), SoundEvents.BEACON_DEACTIVATE); - new BeaconDeactivatedEvent(CraftBlock.at(beacon.getLevel(), beacon.getBlockPos())).callEvent(); - } - }; - } - - Set entityRemoves = set.getEntityRemoves(); - if (entityRemoves != null && !entityRemoves.isEmpty()) { - if (syncTasks == null) { - syncTasks = new Runnable[3]; - } - - syncTasks[2] = () -> { - Set entitiesRemoved = new HashSet<>(); - final List entities = PaperweightPlatformAdapter.getEntities(nmsChunk); - - for (Entity entity : entities) { - UUID uuid = entity.getUUID(); - if (entityRemoves.contains(uuid)) { - if (createCopy) { - copy.storeEntity(entity); - } - removeEntity(entity); - entitiesRemoved.add(uuid); - entityRemoves.remove(uuid); - } - } - if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { - for (UUID uuid : entityRemoves) { - Entity entity = nmsWorld.entityManager.getEntityGetter().get(uuid); - if (entity != null) { - removeEntity(entity); - } - } - } - // Only save entities that were actually removed to history - set.getEntityRemoves().clear(); - set.getEntityRemoves().addAll(entitiesRemoved); - }; - } - - Set entities = set.getEntities(); - if (entities != null && !entities.isEmpty()) { - if (syncTasks == null) { - syncTasks = new Runnable[2]; - } - - syncTasks[1] = () -> { - Iterator iterator = entities.iterator(); - while (iterator.hasNext()) { - final CompoundTag nativeTag = iterator.next(); - final Map entityTagMap = nativeTag.getValue(); - final StringTag idTag = (StringTag) entityTagMap.get("Id"); - final ListTag posTag = (ListTag) entityTagMap.get("Pos"); - final ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); - if (idTag == null || posTag == null || rotTag == null) { - LOGGER.error("Unknown entity tag: {}", nativeTag); - continue; - } - final double x = posTag.getDouble(0); - final double y = posTag.getDouble(1); - final double z = posTag.getDouble(2); - final float yaw = rotTag.getFloat(0); - final float pitch = rotTag.getFloat(1); - final String id = idTag.getValue(); - - EntityType type = EntityType.byString(id).orElse(null); - if (type != null) { - Entity entity = type.create(nmsWorld); - if (entity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); - for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - entity.load(tag); - entity.absMoveTo(x, y, z, yaw, pitch); - entity.setUUID(nativeTag.getUUID()); - if (!nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { - LOGGER.warn( - "Error creating entity of type `{}` in world `{}` at location `{},{},{}`", - id, - nmsWorld.getWorld().getName(), - x, - y, - z - ); - // Unsuccessful create should not be saved to history - iterator.remove(); - } - } - } - } - }; - } - - // set tiles - Map tiles = set.getTiles(); - if (tiles != null && !tiles.isEmpty()) { - if (syncTasks == null) { - syncTasks = new Runnable[1]; - } - - syncTasks[0] = () -> { - for (final Map.Entry entry : tiles.entrySet()) { - final CompoundTag nativeTag = entry.getValue(); - final BlockVector3 blockHash = entry.getKey(); - final int x = blockHash.getX() + bx; - final int y = blockHash.getY(); - final int z = blockHash.getZ() + bz; - final BlockPos pos = new BlockPos(x, y, z); - - synchronized (nmsWorld) { - BlockEntity tileEntity = nmsWorld.getBlockEntity(pos); - if (tileEntity == null || tileEntity.isRemoved()) { - nmsWorld.removeBlockEntity(pos); - tileEntity = nmsWorld.getBlockEntity(pos); - } - if (tileEntity != null) { - final net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative( - nativeTag); - tag.put("x", IntTag.valueOf(x)); - tag.put("y", IntTag.valueOf(y)); - tag.put("z", IntTag.valueOf(z)); - tileEntity.load(tag); - } - } - } - }; - } - - Runnable callback; - if (bitMask == 0 && biomes == null && !lightUpdate) { - callback = null; - } else { - int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0; - boolean finalLightUpdate = lightUpdate; - callback = () -> { - // Set Modified - nmsChunk.setLightCorrect(true); // Set Modified - nmsChunk.mustNotSave = false; - nmsChunk.markUnsaved(); - // send to player - if (Settings.settings().LIGHTING.MODE == 0 || !Settings.settings().LIGHTING.DELAY_PACKET_SENDING) { - this.send(finalMask, finalLightUpdate); - } - if (finalizer != null) { - finalizer.run(); - } - }; - } - if (syncTasks != null) { - QueueHandler queueHandler = Fawe.instance().getQueueHandler(); - Runnable[] finalSyncTasks = syncTasks; - - // Chain the sync tasks and the callback - Callable chain = () -> { - try { - // Run the sync tasks - for (Runnable task : finalSyncTasks) { - if (task != null) { - task.run(); - } - } - if (callback == null) { - if (finalizer != null) { - finalizer.run(); - } - return null; - } else { - return queueHandler.async(callback, null); - } - } catch (Throwable e) { - e.printStackTrace(); - throw e; - } - }; - //noinspection unchecked - required at compile time - return (T) (Future) queueHandler.sync(chain); - } else { - if (callback == null) { - if (finalizer != null) { - finalizer.run(); - } - } else { - callback.run(); - } - } - } - return null; - } catch (Throwable e) { - e.printStackTrace(); - return null; - } finally { - forceLoadSections = true; - } - } - - private void updateGet( - LevelChunk nmsChunk, - LevelChunkSection[] chunkSections, - LevelChunkSection section, - char[] arr, - int layer - ) { - try { - sectionLock.writeLock().lock(); - if (this.getChunk() != nmsChunk) { - this.levelChunk = nmsChunk; - this.sections = new LevelChunkSection[chunkSections.length]; - System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); - this.reset(); - } - if (this.sections == null) { - this.sections = new LevelChunkSection[chunkSections.length]; - System.arraycopy(chunkSections, 0, this.sections, 0, chunkSections.length); - } - if (this.sections[layer] != section) { - // Not sure why it's funky, but it's what I did in commit fda7d00747abe97d7891b80ed8bb88d97e1c70d1 and I don't want to touch it >dords - this.sections[layer] = new LevelChunkSection[]{section}.clone()[0]; - } - } finally { - sectionLock.writeLock().unlock(); - } - this.blocks[layer] = arr; - } - - private char[] loadPrivately(int layer) { - layer -= getMinSectionPosition(); - if (super.sections[layer] != null) { - synchronized (super.sectionLocks[layer]) { - if (super.sections[layer].isFull() && super.blocks[layer] != null) { - return super.blocks[layer]; - } - } - } - return PaperweightGetBlocks.this.update(layer, null, true); - } - - @Override - public void send(int mask, boolean lighting) { - synchronized (sendLock) { - PaperweightPlatformAdapter.sendChunk(serverLevel, chunkX, chunkZ, lighting); - } - } - - /** - * Update a given (nullable) data array to the current data stored in the server's chunk, associated with this - * {@link PaperweightPlatformAdapter} instance. Not synchronised to the {@link PaperweightPlatformAdapter} instance as synchronisation - * is handled where necessary in the method, and should otherwise be handled correctly by this method's caller. - * - * @param layer layer index (0 may denote a negative layer in the world, e.g. at y=-32) - * @param data array to be updated/filled with data or null - * @param aggressive if the cached section array should be re-acquired. - * @return the given array to be filled with data, or a new array if null is given. - */ - @Override - @SuppressWarnings("unchecked") - public char[] update(int layer, char[] data, boolean aggressive) { - LevelChunkSection section = getSections(aggressive)[layer]; - // Section is null, return empty array - if (section == null) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - return data; - } - if (data != null && data.length != 4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - } - if (data == null || data == FaweCache.INSTANCE.EMPTY_CHAR_4096) { - data = new char[4096]; - Arrays.fill(data, (char) BlockTypesCache.ReservedIDs.AIR); - } - Semaphore lock = PaperweightPlatformAdapter.applyLock(section); - synchronized (lock) { - // Efficiently convert ChunkSection to raw data - try { - lock.acquire(); - - final PalettedContainer blocks = section.getStates(); - final BitStorage bits = (BitStorage) PaperweightPlatformAdapter.fieldStorage.get(blocks); - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get(blocks); - - final int bitsPerEntry = (int) PaperweightPlatformAdapter.fieldBitsPerEntry.get(bits); - final long[] blockStates = bits.getRaw(); - - new BitArrayUnstretched(bitsPerEntry, 4096, blockStates).toRaw(data); - - int num_palette; - if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { - num_palette = palette.getSize(); - } else { - // The section's palette is the global block palette. - for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; - char ordinal = adapter.ibdIDToOrdinal(paletteVal); - data[i] = ordinal; - } - return data; - } - - char[] paletteToOrdinal = FaweCache.INSTANCE.PALETTE_TO_BLOCK_CHAR.get(); - try { - if (num_palette != 1) { - for (int i = 0; i < num_palette; i++) { - char ordinal = ordinal(palette.valueFor(i), adapter); - paletteToOrdinal[i] = ordinal; - } - for (int i = 0; i < 4096; i++) { - char paletteVal = data[i]; - char val = paletteToOrdinal[paletteVal]; - if (val == Character.MAX_VALUE) { - val = ordinal(palette.valueFor(i), adapter); - paletteToOrdinal[i] = val; - } - data[i] = val; - } - } else { - char ordinal = ordinal(palette.valueFor(0), adapter); - Arrays.fill(data, ordinal); - } - } finally { - for (int i = 0; i < num_palette; i++) { - paletteToOrdinal[i] = Character.MAX_VALUE; - } - } - return data; - } catch (IllegalAccessException | InterruptedException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } finally { - lock.release(); - } - } - } - - private char ordinal(net.minecraft.world.level.block.state.BlockState ibd, PaperweightFaweAdapter adapter) { - if (ibd == null) { - return BlockTypesCache.ReservedIDs.AIR; - } else { - return adapter.adaptToChar(ibd); - } - } - - public LevelChunkSection[] getSections(boolean force) { - force &= forceLoadSections; - LevelChunkSection[] tmp = sections; - if (tmp == null || force) { - try { - sectionLock.writeLock().lock(); - tmp = sections; - if (tmp == null || force) { - LevelChunkSection[] chunkSections = getChunk().getSections(); - tmp = new LevelChunkSection[chunkSections.length]; - System.arraycopy(chunkSections, 0, tmp, 0, chunkSections.length); - sections = tmp; - } - } finally { - sectionLock.writeLock().unlock(); - } - } - return tmp; - } - - public LevelChunk getChunk() { - LevelChunk levelChunk = this.levelChunk; - if (levelChunk == null) { - synchronized (this) { - levelChunk = this.levelChunk; - if (levelChunk == null) { - this.levelChunk = levelChunk = ensureLoaded(this.serverLevel, chunkX, chunkZ); - } - } - } - return levelChunk; - } - - private void fillLightNibble(char[][] light, LightLayer lightLayer, int minSectionPosition, int maxSectionPosition) { - for (int Y = 0; Y <= maxSectionPosition - minSectionPosition; Y++) { - if (light[Y] == null) { - continue; - } - SectionPos sectionPos = SectionPos.of(levelChunk.getPos(), Y + minSectionPosition); - DataLayer dataLayer = serverLevel.getChunkSource().getLightEngine().getLayerListener(lightLayer).getDataLayerData( - sectionPos); - if (dataLayer == null) { - byte[] LAYER_COUNT = new byte[2048]; - Arrays.fill(LAYER_COUNT, lightLayer == LightLayer.SKY ? (byte) 15 : (byte) 0); - dataLayer = new DataLayer(LAYER_COUNT); - ((LevelLightEngine) serverLevel.getChunkSource().getLightEngine()).queueSectionData( - lightLayer, - sectionPos, - dataLayer, - true - ); - } - synchronized (dataLayer) { - for (int x = 0; x < 16; x++) { - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - int i = y << 8 | z << 4 | x; - if (light[Y][i] < 16) { - dataLayer.set(x, y, z, light[Y][i]); - } - } - } - } - } - } - } - - @Override - public boolean hasSection(int layer) { - layer -= getMinSectionPosition(); - return getSections(false)[layer] != null; - } - - @Override - @SuppressWarnings("unchecked") - public synchronized boolean trim(boolean aggressive) { - skyLight = new DataLayer[getSectionCount()]; - blockLight = new DataLayer[getSectionCount()]; - if (aggressive) { - sectionLock.writeLock().lock(); - sections = null; - levelChunk = null; - sectionLock.writeLock().unlock(); - return super.trim(true); - } else if (sections == null) { - // don't bother trimming if there are no sections stored. - return true; - } else { - for (int i = getMinSectionPosition(); i <= getMaxSectionPosition(); i++) { - int layer = i - getMinSectionPosition(); - if (!hasSection(i) || !super.sections[layer].isFull()) { - continue; - } - LevelChunkSection existing = getSections(true)[layer]; - try { - final PalettedContainer blocksExisting = existing.getStates(); - - final Palette palette = (Palette) PaperweightPlatformAdapter.fieldPalette.get( - blocksExisting); - int paletteSize; - - if (palette instanceof LinearPalette || palette instanceof HashMapPalette) { - paletteSize = palette.getSize(); - } else { - super.trim(false, i); - continue; - } - if (paletteSize == 1) { - //If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks. - continue; - } - super.trim(false, i); - } catch (IllegalAccessException ignored) { - super.trim(false, i); - } - } - return true; - } - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java deleted file mode 100644 index cd9987d79..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightGetBlocks_Copy.java +++ /dev/null @@ -1,251 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType; -import com.fastasyncworldedit.core.queue.IBlocks; -import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.queue.IChunkSet; -import com.google.common.base.Suppliers; -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.worldedit.bukkit.WorldEditPlugin; -import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag; -import com.sk89q.worldedit.math.BlockVector3; -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.BlockTypesCache; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.chunk.ChunkBiomeContainer; -import net.minecraft.world.level.chunk.LevelChunk; - -import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Future; - -public class PaperweightGetBlocks_Copy implements IChunkGet { - - private final Map tiles = new HashMap<>(); - private final Set entities = new HashSet<>(); - private final char[][] blocks; - private final int minHeight; - private final int maxHeight; - final ServerLevel serverLevel; - final LevelChunk levelChunk; - private ChunkBiomeContainer chunkBiomeContainer; - - protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) { - this.levelChunk = levelChunk; - this.serverLevel = levelChunk.level; - this.minHeight = serverLevel.getMinBuildHeight(); - this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive. - this.blocks = new char[getSectionCount()][]; - } - - protected void storeTile(BlockEntity blockEntity) { - tiles.put( - BlockVector3.at( - blockEntity.getBlockPos().getX(), - blockEntity.getBlockPos().getY(), - blockEntity.getBlockPos().getZ() - ), - new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.save(new net.minecraft.nbt.CompoundTag()))) - ); - } - - @Override - public Map getTiles() { - return tiles; - } - - @Override - @Nullable - public CompoundTag getTile(int x, int y, int z) { - return tiles.get(BlockVector3.at(x, y, z)); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - protected void storeEntity(Entity entity) { - BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag(); - PaperweightPlatformAdapter.readEntityIntoTag(entity, compoundTag); - entities.add((CompoundTag) adapter.toNative(compoundTag)); - } - - @Override - public Set getEntities() { - return this.entities; - } - - @Override - public CompoundTag getEntity(UUID uuid) { - for (CompoundTag tag : entities) { - if (uuid.equals(tag.getUUID())) { - return tag; - } - } - return null; - } - - @Override - public boolean isCreateCopy() { - return false; - } - - @Override - public int setCreateCopy(boolean createCopy) { - return -1; - } - - @Override - public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) { - } - - @Override - public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) { - } - - @Override - public void setHeightmapToGet(HeightMapType type, int[] data) { - } - - @Override - public int getMaxY() { - return maxHeight; - } - - @Override - public int getMinY() { - return minHeight; - } - - @Override - public int getMaxSectionPosition() { - return maxHeight >> 4; - } - - @Override - public int getMinSectionPosition() { - return minHeight >> 4; - } - - protected void storeBiomes(ChunkBiomeContainer chunkBiomeContainer) { - // The to do one line below is pre-paperweight and needs to be revised - // TODO revisit last parameter, BiomeStorage[] *would* be more efficient - this.chunkBiomeContainer = new ChunkBiomeContainer(chunkBiomeContainer.biomeRegistry, serverLevel, - chunkBiomeContainer.writeBiomes() - ); - } - - @Override - public BiomeType getBiomeType(int x, int y, int z) { - Biome biome = null; - if (y == -1) { - for (y = serverLevel.getMinBuildHeight(); y <= serverLevel.getMaxBuildHeight(); y += 4) { - biome = this.chunkBiomeContainer.getNoiseBiome(x >> 2, y >> 2, z >> 2); - if (biome != null) { - break; - } - } - } else { - biome = this.chunkBiomeContainer.getNoiseBiome(x >> 2, y >> 2, z >> 2); - } - return biome != null ? PaperweightPlatformAdapter.adapt(biome, serverLevel) : null; - } - - @Override - public void removeSectionLighting(int layer, boolean sky) { - } - - @Override - public boolean trim(boolean aggressive, int layer) { - return false; - } - - @Override - public IBlocks reset() { - return null; - } - - @Override - public int getSectionCount() { - return serverLevel.getSectionsCount(); - } - - protected void storeSection(int layer, char[] data) { - blocks[layer] = data; - } - - @Override - public BaseBlock getFullBlock(int x, int y, int z) { - BlockState state = BlockTypesCache.states[get(x, y, z)]; - return state.toBaseBlock(this, x, y, z); - } - - @Override - public boolean hasSection(int layer) { - layer -= getMinSectionPosition(); - return blocks[layer] != null; - } - - @Override - public char[] load(int layer) { - layer -= getMinSectionPosition(); - if (blocks[layer] == null) { - blocks[layer] = new char[4096]; - Arrays.fill(blocks[layer], (char) BlockTypesCache.ReservedIDs.AIR); - } - return blocks[layer]; - } - - @Override - public char[] loadIfPresent(int layer) { - layer -= getMinSectionPosition(); - return blocks[layer]; - } - - @Override - public BlockState getBlock(int x, int y, int z) { - return BlockTypesCache.states[get(x, y, z)]; - } - - @Override - public int getSkyLight(int x, int y, int z) { - return 0; - } - - @Override - public int getEmittedLight(int x, int y, int z) { - return 0; - } - - @Override - public int[] getHeightMap(HeightMapType type) { - return new int[0]; - } - - @Override - public > T call(IChunkSet set, Runnable finalize) { - return null; - } - - public char get(int x, int y, int z) { - final int layer = (y >> 4) - getMinSectionPosition(); - final int index = (y & 15) << 8 | z << 4 | x; - return blocks[layer][index]; - } - - - @Override - public boolean trim(boolean aggressive) { - return false; - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightMapChunkUtil.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightMapChunkUtil.java deleted file mode 100644 index c9d5ed124..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightMapChunkUtil.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil; -import com.sk89q.worldedit.bukkit.adapter.Refraction; -import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket; - -public class PaperweightMapChunkUtil extends MapChunkUtil { - - public PaperweightMapChunkUtil() throws NoSuchFieldException { - fieldX = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a")); - fieldZ = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("x", "b")); - fieldBitMask = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("z", "c")); - fieldHeightMap = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("availableSections", "d")); - fieldChunkData = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("biomes", "f")); - fieldBlockEntities = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("buffer", "g")); - fieldFull = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("blockEntitiesTags", "h")); - fieldX.setAccessible(true); - fieldZ.setAccessible(true); - fieldBitMask.setAccessible(true); - fieldHeightMap.setAccessible(true); - fieldChunkData.setAccessible(true); - fieldBlockEntities.setAccessible(true); - fieldFull.setAccessible(true); - } - - @Override - public ClientboundLevelChunkPacket createPacket() { - // TODO ??? return new ClientboundLevelChunkPacket(); - throw new UnsupportedOperationException(); - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java deleted file mode 100644 index 16dfd7bd8..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPlatformAdapter.java +++ /dev/null @@ -1,526 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter; -import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; -import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; -import com.fastasyncworldedit.core.Fawe; -import com.fastasyncworldedit.core.FaweCache; -import com.fastasyncworldedit.core.configuration.Settings; -import com.fastasyncworldedit.core.math.BitArrayUnstretched; -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.adapter.Refraction; -import com.sk89q.worldedit.internal.util.LogManagerCompat; -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 it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket; -import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.TicketType; -import net.minecraft.util.BitStorage; -import net.minecraft.util.Unit; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.npc.AbstractVillager; -import net.minecraft.world.item.trading.MerchantOffers; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.chunk.ChunkBiomeContainer; -import net.minecraft.world.level.chunk.HashMapPalette; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.LinearPalette; -import net.minecraft.world.level.chunk.Palette; -import net.minecraft.world.level.chunk.PalettedContainer; -import net.minecraft.world.level.gameevent.GameEventDispatcher; -import net.minecraft.world.level.gameevent.GameEventListener; -import org.apache.logging.log4j.Logger; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.v1_17_R1.CraftChunk; -import sun.misc.Unsafe; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Function; -import java.util.stream.Stream; - -public final class PaperweightPlatformAdapter extends NMSAdapter { - - public static final Field fieldStorage; - public static final Field fieldPalette; - public static final Field fieldBits; - - public static final Field fieldBitsPerEntry; - - private static final Field fieldTickingFluidContent; - private static final Field fieldTickingBlockCount; - private static final Field fieldNonEmptyBlockCount; - - private static final Field fieldBiomes; - - private static final MethodHandle methodGetVisibleChunk; - - private static final Field fieldLock; - - private static final Field fieldGameEventDispatcherSections; - private static final MethodHandle methodremoveBlockEntityTicker; - - private static final Field fieldOffers; - private static final MerchantOffers OFFERS = new MerchantOffers(); - - private static final Field fieldRemove; - - private static final Logger LOGGER = LogManagerCompat.getLogger(); - - static { - try { - fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l")); - fieldBits.setAccessible(true); - fieldStorage = PalettedContainer.class.getDeclaredField(Refraction.pickName("storage", "c")); - fieldStorage.setAccessible(true); - fieldPalette = PalettedContainer.class.getDeclaredField(Refraction.pickName("palette", "k")); - fieldPalette.setAccessible(true); - - fieldBitsPerEntry = BitStorage.class.getDeclaredField(Refraction.pickName("bits", "c")); - fieldBitsPerEntry.setAccessible(true); - - fieldTickingFluidContent = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "h")); - fieldTickingFluidContent.setAccessible(true); - fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g")); - fieldTickingBlockCount.setAccessible(true); - fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f")); - fieldNonEmptyBlockCount.setAccessible(true); - - fieldBiomes = ChunkBiomeContainer.class.getDeclaredField(Refraction.pickName("biomes", "f")); - fieldBiomes.setAccessible(true); - - Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName( - "getVisibleChunkIfPresent", - "getVisibleChunk" - ), long.class); - getVisibleChunkIfPresent.setAccessible(true); - methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent); - - if (!PaperLib.isPaper()) { - fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m")); - fieldLock.setAccessible(true); - } else { - // in paper, the used methods are synchronized properly - fieldLock = null; - } - - fieldGameEventDispatcherSections = LevelChunk.class.getDeclaredField(Refraction.pickName( - "gameEventDispatcherSections", "x")); - fieldGameEventDispatcherSections.setAccessible(true); - Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( - Refraction.pickName( - "removeBlockEntityTicker", - "l" - ), BlockPos.class - ); - removeBlockEntityTicker.setAccessible(true); - methodremoveBlockEntityTicker = MethodHandles.lookup().unreflect(removeBlockEntityTicker); - - fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p")); - fieldRemove.setAccessible(true); - - fieldOffers = AbstractVillager.class.getDeclaredField(Refraction.pickName("offers", "bU")); - fieldOffers.setAccessible(true); - } catch (RuntimeException e) { - throw e; - } catch (Throwable rethrow) { - rethrow.printStackTrace(); - throw new RuntimeException(rethrow); - } - } - - static boolean setSectionAtomic( - LevelChunkSection[] sections, - LevelChunkSection expected, - LevelChunkSection value, - int layer - ) { - if (layer >= 0 && layer < sections.length) { - return ReflectionUtils.compareAndSet(sections, expected, value, layer); - } - return false; - } - - // There is no point in having a functional semaphore for paper servers. - private static final ThreadLocal SEMAPHORE_THREAD_LOCAL = - ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null)); - - static DelegateSemaphore applyLock(LevelChunkSection section) { - if (PaperLib.isPaper()) { - return SEMAPHORE_THREAD_LOCAL.get(); - } - try { - synchronized (section) { - PalettedContainer blocks = section.getStates(); - Semaphore currentLock = (Semaphore) fieldLock.get(blocks); - if (currentLock instanceof DelegateSemaphore delegateSemaphore) { - return delegateSemaphore; - } - DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); - fieldLock.set(blocks, newLock); - return newLock; - } - } catch (Throwable e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) { - if (!PaperLib.isPaper()) { - LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false); - if (nmsChunk != null) { - return nmsChunk; - } - if (Fawe.isMainThread()) { - return serverLevel.getChunk(chunkX, chunkZ); - } - } else { - LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); - if (nmsChunk != null) { - addTicket(serverLevel, chunkX, chunkZ); - return nmsChunk; - } - nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); - if (nmsChunk != null) { - addTicket(serverLevel, chunkX, chunkZ); - return nmsChunk; - } - // Avoid "async" methods from the main thread. - if (Fawe.isMainThread()) { - return serverLevel.getChunk(chunkX, chunkZ); - } - CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); - try { - CraftChunk chunk; - try { - chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS); - } catch (TimeoutException e) { - String world = serverLevel.getWorld().getName(); - // We've already taken 10 seconds we can afford to wait a little here. - boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null); - if (loaded) { - LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world); - // Retry chunk load - chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get(); - } else { - throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!"); - } - } - return chunk.getHandle(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); - } - - private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) { - // Ensure chunk is definitely loaded before applying a ticket - net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel - .getChunkSource() - .addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE)); - } - - public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) { - ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; - try { - return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ)); - } catch (Throwable thr) { - throw new RuntimeException(thr); - } - } - - @SuppressWarnings("unchecked") - public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) { - ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ); - if (chunkHolder == null) { - return; - } - ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ); - // UNLOADED_CHUNK - Optional optional = ((Either) chunkHolder - .getTickingChunkFuture() - .getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left(); - if (PaperLib.isPaper()) { - // getChunkAtIfLoadedImmediately is paper only - optional = optional.or(() -> Optional.ofNullable(nmsWorld - .getChunkSource() - .getChunkAtIfLoadedImmediately(chunkX, chunkZ))); - } - if (optional.isEmpty()) { - return; - } - LevelChunk levelChunk = optional.get(); - TaskManager.taskManager().task(() -> { - ClientboundLevelChunkPacket chunkPacket = new ClientboundLevelChunkPacket(levelChunk); - nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(chunkPacket)); - if (lighting) { - //This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad) - boolean trustEdges = true; - ClientboundLightUpdatePacket packet = - new ClientboundLightUpdatePacket(coordIntPair, nmsWorld.getChunkSource().getLightEngine(), null, null, - trustEdges - ); - nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet)); - } - }); - } - - private static Stream nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) { - return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false); - } - - /* - NMS conversion - */ - public static LevelChunkSection newChunkSection( - final int layer, - final char[] blocks, - boolean fastMode, - CachedBukkitAdapter adapter - ) { - return newChunkSection(layer, null, blocks, fastMode, adapter); - } - - public static LevelChunkSection newChunkSection( - final int layer, - final Function get, - char[] set, - boolean fastMode, - CachedBukkitAdapter adapter - ) { - if (set == null) { - return newChunkSection(layer); - } - final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get(); - final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get(); - final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get(); - final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get(); - try { - int num_palette; - final short[] nonEmptyBlockCount = fastMode ? new short[1] : null; - if (get == null) { - num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, nonEmptyBlockCount); - } else { - num_palette = createPalette( - layer, - blockToPalette, - paletteToBlock, - blocksCopy, - get, - set, - adapter, - nonEmptyBlockCount - ); - } - // BlockStates - int bitsPerEntry = MathMan.log2nlz(num_palette - 1); - if (Settings.settings().PROTOCOL_SUPPORT_FIX || num_palette != 1) { - bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry - } else { - bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries - } - if (bitsPerEntry > 8) { - bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1); - } - - final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry); - final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong); - - if (num_palette == 1) { - for (int i = 0; i < blockBitArrayEnd; i++) { - blockStates[i] = 0; - } - } else { - final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, 4096, blockStates); - bitArray.fromRaw(blocksCopy); - } - - LevelChunkSection levelChunkSection = newChunkSection(layer); - // set palette & data bits - final PalettedContainer dataPaletteBlocks = - levelChunkSection.getStates(); - // private DataPalette h; - // protected DataBits a; - final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd); - final BitStorage nmsBits = new BitStorage(bitsPerEntry, 4096, bits); - final Palette blockStatePalettedContainer; - if (bitsPerEntry <= 4) { - blockStatePalettedContainer = new LinearPalette<>(Block.BLOCK_STATE_REGISTRY, bitsPerEntry, dataPaletteBlocks, - NbtUtils::readBlockState - ); - } else if (bitsPerEntry < 9) { - blockStatePalettedContainer = new HashMapPalette<>( - Block.BLOCK_STATE_REGISTRY, - bitsPerEntry, - dataPaletteBlocks, - NbtUtils::readBlockState, - NbtUtils::writeBlockState - ); - } else { - blockStatePalettedContainer = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE; - } - - // set palette if required - if (bitsPerEntry < 9) { - for (int i = 0; i < num_palette; i++) { - final int ordinal = paletteToBlock[i]; - blockToPalette[ordinal] = Integer.MAX_VALUE; - final BlockState state = BlockTypesCache.states[ordinal]; - final net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState(); - blockStatePalettedContainer.idFor(blockState); - } - } - try { - fieldStorage.set(dataPaletteBlocks, nmsBits); - fieldPalette.set(dataPaletteBlocks, blockStatePalettedContainer); - fieldBits.set(dataPaletteBlocks, bitsPerEntry); - } catch (final IllegalAccessException e) { - throw new RuntimeException(e); - } - - if (!fastMode) { - levelChunkSection.recalcBlockCounts(); - } else { - try { - fieldNonEmptyBlockCount.set(levelChunkSection, nonEmptyBlockCount[0]); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - return levelChunkSection; - } catch (final Throwable e) { - throw e; - } finally { - Arrays.fill(blockToPalette, Integer.MAX_VALUE); - Arrays.fill(paletteToBlock, Integer.MAX_VALUE); - Arrays.fill(blockStates, 0); - Arrays.fill(blocksCopy, 0); - } - } - - private static LevelChunkSection newChunkSection(int layer) { - return new LevelChunkSection(layer); - } - - public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException { - fieldTickingFluidContent.setShort(section, (short) 0); - fieldTickingBlockCount.setShort(section, (short) 0); - } - - public static Biome[] getBiomeArray(ChunkBiomeContainer chunkBiomeContainer) { - try { - return (Biome[]) fieldBiomes.get(chunkBiomeContainer); - } catch (IllegalAccessException e) { - e.printStackTrace(); - return null; - } - } - - public static BiomeType adapt(Biome biome, LevelAccessor levelAccessor) { - ResourceLocation resourceLocation = levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).getKey( - biome); - if (resourceLocation == null) { - return levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).getId(biome) == -1 - ? BiomeTypes.OCEAN - : null; - } - return BiomeTypes.get(resourceLocation.toString().toLowerCase(Locale.ROOT)); - } - - @SuppressWarnings("unchecked") - static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) { - try { - // Do the method ourselves to avoid trying to reflect generic method parameters - if (levelChunk.loaded || levelChunk.level.isClientSide()) { - BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos()); - if (blockEntity != null) { - if (!levelChunk.level.isClientSide) { - Block block = beacon.getBlockState().getBlock(); - if (block instanceof EntityBlock) { - GameEventListener gameEventListener = ((EntityBlock) block).getListener(levelChunk.level, beacon); - if (gameEventListener != null) { - int i = SectionPos.blockToSectionCoord(beacon.getBlockPos().getY()); - GameEventDispatcher gameEventDispatcher = levelChunk.getEventDispatcher(i); - gameEventDispatcher.unregister(gameEventListener); - if (gameEventDispatcher.isEmpty()) { - try { - ((Int2ObjectMap) fieldGameEventDispatcherSections.get(levelChunk)) - .remove(i); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - } - } - } - fieldRemove.set(beacon, true); - } - } - methodremoveBlockEntityTicker.invoke(levelChunk, beacon.getBlockPos()); - } catch (Throwable throwable) { - throwable.printStackTrace(); - } - } - - static List getEntities(LevelChunk chunk) { - return chunk.level.entityManager.getEntities(chunk.getPos()); - } - - public static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) { - boolean isVillager = entity instanceof AbstractVillager && !Fawe.isMainThread(); - boolean unset = false; - if (isVillager) { - try { - if (fieldOffers.get(entity) != null) { - fieldOffers.set(entity, OFFERS); - unset = true; - } - } catch (IllegalAccessException e) { - throw new RuntimeException("Failed to set offers field to villager to avoid async catcher.", e); - } - } - entity.save(compoundTag); - if (unset) { - try { - fieldOffers.set(entity, null); - } catch (IllegalAccessException e) { - throw new RuntimeException("Failed to set offers field to null again on villager.", e); - } - } - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPostProcessor.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPostProcessor.java deleted file mode 100644 index 0833c6ecb..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightPostProcessor.java +++ /dev/null @@ -1,175 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.core.configuration.Settings; -import com.fastasyncworldedit.core.extent.processor.ProcessorScope; -import com.fastasyncworldedit.core.queue.IBatchProcessor; -import com.fastasyncworldedit.core.queue.IChunk; -import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.queue.IChunkSet; -import com.fastasyncworldedit.core.registry.state.PropertyKey; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.world.block.BlockState; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldedit.world.block.BlockTypesCache; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.Fluids; - -import javax.annotation.Nullable; - -public class PaperweightPostProcessor implements IBatchProcessor { - - @Override - public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) { - return set; - } - - @SuppressWarnings("deprecation") - @Override - public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) { - boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS; - // The PostProcessor shouldn't be added, but just in case - if (!tickFluid) { - return; - } - PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet; - layer: - for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) { - char[] set = iChunkSet.loadIfPresent(layer); - if (set == null) { - // No edit means no need to process - continue; - } - char[] get = null; - for (int i = 0; i < 4096; i++) { - char ordinal = set[i]; - char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__; - boolean fromGet = false; // Used for liquids - if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { - if (get == null) { - get = getBlocks.load(layer); - } - // If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't - // actually being set - if (get == null) { - continue layer; - } - fromGet = true; - ordinal = replacedOrdinal = get[i]; - } - if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) { - continue; - } else if (!fromGet) { // if fromGet, don't do the same again - if (get == null) { - get = getBlocks.load(layer); - } - replacedOrdinal = get[i]; - } - boolean ticking = BlockTypesCache.ticking[ordinal]; - boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal]; - boolean replacedWasLiquid = false; - BlockState replacedState = null; - if (!ticking) { - // If the block being replaced was not ticking, it cannot be a liquid - if (!replacedWasTicking) { - continue; - } - // If the block being replaced is not fluid, we do not need to worry - if (!(replacedWasLiquid = - (replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) { - continue; - } - } - BlockState state = BlockState.getFromOrdinal(ordinal); - boolean liquid = state.getMaterial().isLiquid(); - int x = i & 15; - int y = (i >> 8) & 15; - int z = (i >> 4) & 15; - BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z); - if (liquid || replacedWasLiquid) { - if (liquid) { - addFluid(getBlocks.serverLevel, state, position); - continue; - } - // If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this - // may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up - // being ticked anyway. We only need it to be "hit" once. - if (!wasAdjacentToWater(get, set, i, x, y, z)) { - continue; - } - addFluid(getBlocks.serverLevel, replacedState, position); - } - } - } - } - - @Nullable - @Override - public Extent construct(final Extent child) { - throw new UnsupportedOperationException("Processing only"); - } - - @Override - public ProcessorScope getScope() { - return ProcessorScope.READING_SET_BLOCKS; - } - - private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) { - if (set == null || get == null) { - return false; - } - char ordinal; - char reserved = BlockTypesCache.ReservedIDs.__RESERVED__; - if (x > 0 && set[i - 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) { - return true; - } - } - if (x < 15 && set[i + 1] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) { - return true; - } - } - if (z > 0 && set[i - 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) { - return true; - } - } - if (z < 15 && set[i + 16] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) { - return true; - } - } - if (y > 0 && set[i - 256] != reserved) { - if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) { - return true; - } - } - if (y < 15 && set[i + 256] != reserved) { - return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal); - } - return false; - } - - @SuppressWarnings("deprecation") - private boolean isFluid(char ordinal) { - return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid(); - } - - @SuppressWarnings("deprecation") - private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) { - Fluid type; - if (replacedState.getBlockType() == BlockTypes.LAVA) { - type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA; - } else { - type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER; - } - serverLevel.getLiquidTicks().scheduleTick( - position, - type, - type.getTickDelay(serverLevel) - ); - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightStarlightRelighter.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightStarlightRelighter.java deleted file mode 100644 index ea2afede3..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightStarlightRelighter.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.bukkit.adapter.StarlightRelighter; -import com.fastasyncworldedit.core.configuration.Settings; -import com.fastasyncworldedit.core.queue.IQueueExtent; -import net.minecraft.server.MCUtil; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ThreadedLevelLightEngine; -import net.minecraft.server.level.TicketType; -import net.minecraft.util.Unit; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkStatus; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; -import java.util.function.IntConsumer; - -public class PaperweightStarlightRelighter extends StarlightRelighter { - - private static final MethodHandle RELIGHT; - private static final TicketType FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0); - private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT); - - static { - MethodHandle tmp = null; - try { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - tmp = lookup.findVirtual( - ThreadedLevelLightEngine.class, - "relight", - MethodType.methodType( - int.class, // return type - // params - Set.class, - Consumer.class, - IntConsumer.class - ) - ); - tmp = MethodHandles.dropReturn(tmp); - } catch (NoSuchMethodException | IllegalAccessException e) { - LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e); - } - RELIGHT = tmp; - } - - public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent queue) { - super(serverLevel, queue); - } - - @Override - protected ChunkPos createChunkPos(final long chunkKey) { - return new ChunkPos(chunkKey); - } - - @Override - protected long asLong(final int chunkX, final int chunkZ) { - return ChunkPos.asLong(chunkX, chunkZ); - } - - @Override - protected CompletableFuture chunkLoadFuture(final ChunkPos chunkPos) { - return serverLevel.getWorld().getChunkAtAsync(chunkPos.x, chunkPos.z) - .thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel( - FAWE_TICKET, - chunkPos, - LIGHT_LEVEL, - Unit.INSTANCE - )); - } - - public static boolean isUsable() { - return RELIGHT != null; - } - - @Override - protected void invokeRelight( - Set coords, - Consumer chunkCallback, - IntConsumer processCallback - ) { - try { - RELIGHT.invokeExact( - serverLevel.getChunkSource().getLightEngine(), - coords, - chunkCallback, // callback per chunk - processCallback // callback for all chunks - ); - } catch (Throwable throwable) { - LOGGER.error("Error occurred on relighting", throwable); - } - } - - /* - * Allow the server to unload the chunks again. - * Also, if chunk packets are sent delayed, we need to do that here - */ - 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); - } - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightStarlightRelighterFactory.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightStarlightRelighterFactory.java deleted file mode 100644 index 9cd0a1ef8..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/PaperweightStarlightRelighterFactory.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2; - -import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter; -import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode; -import com.fastasyncworldedit.core.extent.processor.lighting.Relighter; -import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory; -import com.fastasyncworldedit.core.queue.IQueueExtent; -import com.sk89q.worldedit.world.World; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; - -import javax.annotation.Nonnull; - -public class PaperweightStarlightRelighterFactory implements RelighterFactory { - - @Override - public @Nonnull Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent queue) { - org.bukkit.World w = Bukkit.getWorld(world.getName()); - if (w == null) { - return NullRelighter.INSTANCE; - } - return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue); - } - -} - diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/nbt/PaperweightLazyCompoundTag.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/nbt/PaperweightLazyCompoundTag.java deleted file mode 100644 index 21bc0fe72..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/nbt/PaperweightLazyCompoundTag.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt; - -import com.sk89q.jnbt.CompoundTag; -import com.sk89q.jnbt.LazyCompoundTag; -import com.sk89q.jnbt.ListTag; -import com.sk89q.jnbt.StringTag; -import com.sk89q.jnbt.Tag; -import com.sk89q.worldedit.bukkit.WorldEditPlugin; -import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; -import net.minecraft.nbt.NumericTag; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -public class PaperweightLazyCompoundTag extends LazyCompoundTag { - - private final Supplier compoundTagSupplier; - private CompoundTag compoundTag; - - public PaperweightLazyCompoundTag(Supplier compoundTagSupplier) { - super(new HashMap<>()); - this.compoundTagSupplier = compoundTagSupplier; - } - - public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) { - this(() -> compoundTag); - } - - public net.minecraft.nbt.CompoundTag get() { - return compoundTagSupplier.get(); - } - - @Override - @SuppressWarnings("unchecked") - public Map getValue() { - if (compoundTag == null) { - compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get()); - } - return compoundTag.getValue(); - } - - @Override - public CompoundBinaryTag asBinaryTag() { - getValue(); - return compoundTag.asBinaryTag(); - } - - public boolean containsKey(String key) { - return compoundTagSupplier.get().contains(key); - } - - public byte[] getByteArray(String key) { - return compoundTagSupplier.get().getByteArray(key); - } - - public byte getByte(String key) { - return compoundTagSupplier.get().getByte(key); - } - - public double getDouble(String key) { - return compoundTagSupplier.get().getDouble(key); - } - - public double asDouble(String key) { - net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); - if (tag instanceof NumericTag numTag) { - return numTag.getAsDouble(); - } - return 0; - } - - public float getFloat(String key) { - return compoundTagSupplier.get().getFloat(key); - } - - public int[] getIntArray(String key) { - return compoundTagSupplier.get().getIntArray(key); - } - - public int getInt(String key) { - return compoundTagSupplier.get().getInt(key); - } - - public int asInt(String key) { - net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); - if (tag instanceof NumericTag numTag) { - return numTag.getAsInt(); - } - return 0; - } - - @SuppressWarnings("unchecked") - public List getList(String key) { - net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); - if (tag instanceof net.minecraft.nbt.ListTag nbtList) { - ArrayList list = new ArrayList<>(); - for (net.minecraft.nbt.Tag elem : nbtList) { - if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) { - list.add(new PaperweightLazyCompoundTag(compoundTag)); - } else { - list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem)); - } - } - return list; - } - return Collections.emptyList(); - } - - @SuppressWarnings("unchecked") - public ListTag getListTag(String key) { - net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); - if (tag instanceof net.minecraft.nbt.ListTag) { - return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag); - } - return new ListTag(StringTag.class, Collections.emptyList()); - } - - @SuppressWarnings("unchecked") - public List getList(String key, Class listType) { - ListTag listTag = getListTag(key); - if (listTag.getType().equals(listType)) { - return (List) listTag.getValue(); - } else { - return Collections.emptyList(); - } - } - - public long[] getLongArray(String key) { - return compoundTagSupplier.get().getLongArray(key); - } - - public long getLong(String key) { - return compoundTagSupplier.get().getLong(key); - } - - public long asLong(String key) { - net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key); - if (tag instanceof NumericTag numTag) { - return numTag.getAsLong(); - } - return 0; - } - - public short getShort(String key) { - return compoundTagSupplier.get().getShort(key); - } - - public String getString(String key) { - return compoundTagSupplier.get().getString(key); - } - - @Override - public String toString() { - return compoundTagSupplier.get().toString(); - } - -} diff --git a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java deleted file mode 100644 index 63d93156b..000000000 --- a/worldedit-bukkit/adapters/adapter-1_17_1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_17_R1_2/regen/PaperweightRegen.java +++ /dev/null @@ -1,694 +0,0 @@ -package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.regen; - -import com.fastasyncworldedit.bukkit.adapter.Regenerator; -import com.fastasyncworldedit.core.Fawe; -import com.fastasyncworldedit.core.queue.IChunkCache; -import com.fastasyncworldedit.core.queue.IChunkGet; -import com.fastasyncworldedit.core.util.ReflectionUtils; -import com.fastasyncworldedit.core.util.TaskManager; -import com.google.common.collect.ImmutableList; -import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.Lifecycle; -import com.sk89q.worldedit.bukkit.adapter.Refraction; -import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.PaperweightGetBlocks; -import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.internal.util.LogManagerCompat; -import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.util.io.file.SafeFiles; -import com.sk89q.worldedit.world.RegenOptions; -import io.papermc.lib.PaperLib; -import net.minecraft.core.MappedRegistry; -import net.minecraft.core.Registry; -import net.minecraft.data.BuiltinRegistries; -import net.minecraft.data.worldgen.biome.Biomes; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ThreadedLevelLightEngine; -import net.minecraft.server.level.progress.ChunkProgressListener; -import net.minecraft.util.LinearCongruentialGenerator; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.LevelSettings; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.FixedBiomeSource; -import net.minecraft.world.level.biome.OverworldBiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.ProtoChunk; -import net.minecraft.world.level.chunk.UpgradeData; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.levelgen.FlatLevelSource; -import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; -import net.minecraft.world.level.levelgen.NoiseGeneratorSettings; -import net.minecraft.world.level.levelgen.SimpleRandomSource; -import net.minecraft.world.level.levelgen.WorldGenSettings; -import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; -import net.minecraft.world.level.levelgen.synth.ImprovedNoise; -import net.minecraft.world.level.newbiome.area.Area; -import net.minecraft.world.level.newbiome.area.AreaFactory; -import net.minecraft.world.level.newbiome.context.BigContext; -import net.minecraft.world.level.newbiome.layer.Layer; -import net.minecraft.world.level.newbiome.layer.Layers; -import net.minecraft.world.level.newbiome.layer.traits.PixelTransformer; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.PrimaryLevelData; -import org.apache.logging.log4j.Logger; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.v1_17_R1.CraftServer; -import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_R1.generator.CustomChunkGenerator; -import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.BlockPopulator; - -import javax.annotation.Nullable; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.nio.file.Path; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.OptionalLong; -import java.util.Random; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BooleanSupplier; -import java.util.function.LongFunction; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -public class PaperweightRegen extends Regenerator { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); - - private static final Field worldsField; - private static final Field paperConfigField; - private static final Field generateFlatBedrockField; - private static final Field generatorSettingFlatField; - private static final Field generatorSettingBaseSupplierField; - private static final Field delegateField; - private static final Field chunkSourceField; - - //list of chunk stati in correct order without FULL - private static final Map chunkStati = new LinkedHashMap<>(); - - static { - chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing - chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps - chunkStati.put( - ChunkStatus.STRUCTURE_REFERENCES, - Concurrency.FULL - ); // structure refs: radius 8, but only writes to current chunk - chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0 - chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8 - chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE - chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results - chunkStati.put( - ChunkStatus.LIQUID_CARVERS, - Concurrency.NONE - ); // liquid carvers: radius 0, but RADIUS and FULL change results - chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps - chunkStati.put( - ChunkStatus.LIGHT, - Concurrency.FULL - ); // light: radius 1, but no writes to other chunks, only current chunk - chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0 - chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0 - - try { - worldsField = CraftServer.class.getDeclaredField("worlds"); - worldsField.setAccessible(true); - - Field tmpPaperConfigField; - Field tmpFlatBedrockField; - try { //only present on paper - tmpPaperConfigField = Level.class.getDeclaredField("paperConfig"); - tmpPaperConfigField.setAccessible(true); - - tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock"); - tmpFlatBedrockField.setAccessible(true); - } catch (Exception e) { - tmpPaperConfigField = null; - tmpFlatBedrockField = null; - } - paperConfigField = tmpPaperConfigField; - generateFlatBedrockField = tmpFlatBedrockField; - - generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName( - "settings", "g")); - generatorSettingBaseSupplierField.setAccessible(true); - - generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "e")); - generatorSettingFlatField.setAccessible(true); - - delegateField = CustomChunkGenerator.class.getDeclaredField("delegate"); - delegateField.setAccessible(true); - - chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "C")); - chunkSourceField.setAccessible(true); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - //runtime - private ServerLevel originalServerWorld; - private ServerChunkCache originalChunkProvider; - private ServerLevel freshWorld; - private ServerChunkCache freshChunkProvider; - private LevelStorageSource.LevelStorageAccess session; - private StructureManager structureManager; - private ThreadedLevelLightEngine threadedLevelLightEngine; - private ChunkGenerator chunkGenerator; - - private Path tempDir; - - private boolean generateFlatBedrock = false; - - public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) { - super(originalBukkitWorld, region, target, options); - } - - @Override - protected boolean prepare() { - this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); - originalChunkProvider = originalServerWorld.getChunkSource(); - - //flat bedrock? (only on paper) - if (paperConfigField != null) { - try { - generateFlatBedrock = generateFlatBedrockField.getBoolean(paperConfigField.get(originalServerWorld)); - } catch (Exception ignored) { - } - } - - seed = options.getSeed().orElse(originalServerWorld.getSeed()); - chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c)); - - return true; - } - - @Override - protected boolean initNewWorld() throws Exception { - //world folder - tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen"); - - //prepare for world init (see upstream implementation for reference) - org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment(); - org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator(); - LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir); - ResourceKey levelStemResourceKey = getWorldDimKey(environment); - session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey); - PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData; - - BiomeProvider biomeProvider = getBiomeProvider(); - - MinecraftServer server = originalServerWorld.getCraftServer().getServer(); - WorldGenSettings originalOpts = originalWorldData.worldGenSettings(); - WorldGenSettings newOpts = options.getSeed().isPresent() - ? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed)) - : originalOpts; - LevelSettings newWorldSettings = new LevelSettings( - "faweregentempworld", - originalWorldData.settings.gameType(), - originalWorldData.settings.hardcore(), - originalWorldData.settings.difficulty(), - originalWorldData.settings.allowCommands(), - originalWorldData.settings.gameRules(), - originalWorldData.settings.getDataPackConfig() - ); - PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable()); - - //init world - freshWorld = Fawe.instance().getQueueHandler().sync((Supplier) () -> new ServerLevel( - server, - server.executor, - session, - newWorldData, - originalServerWorld.dimension(), - originalServerWorld.dimensionType(), - new RegenNoOpWorldLoadListener(), - // placeholder. Required for new ChunkProviderServer, but we create and then set it later - newOpts.dimensions().get(levelStemResourceKey).generator(), - originalServerWorld.isDebug(), - seed, - ImmutableList.of(), - false, - environment, - generator, - biomeProvider - ) { - private final Biome singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.get(ResourceLocation.tryParse( - options - .getBiomeType() - .getId())) : null; - - @Override - public void tick(BooleanSupplier shouldKeepTicking) { //no ticking - } - - @Override - public Biome getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) { - if (options.hasBiomeType()) { - return singleBiome; - } - return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ); - } - }).get(); - freshWorld.noSave = true; - removeWorldFromWorldsMap(); - newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name - if (paperConfigField != null) { - paperConfigField.set(freshWorld, originalServerWorld.paperConfig); - } - - //generator - if (originalChunkProvider.getGenerator() instanceof FlatLevelSource) { - FlatLevelGeneratorSettings generatorSettingFlat = (FlatLevelGeneratorSettings) generatorSettingFlatField.get( - originalChunkProvider.getGenerator()); - chunkGenerator = new FlatLevelSource(generatorSettingFlat); - } else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator) { - Supplier generatorSettingBaseSupplier = (Supplier) generatorSettingBaseSupplierField - .get(originalChunkProvider.getGenerator()); - BiomeSource biomeSource; - if (options.hasBiomeType()) { - biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options - .getBiomeType() - .getId()))); - } else { - biomeSource = originalChunkProvider.getGenerator().getBiomeSource(); - if (biomeSource instanceof OverworldBiomeSource) { - biomeSource = fastOverworldBiomeSource(biomeSource); - } - } - chunkGenerator = new NoiseBasedChunkGenerator(biomeSource, seed, generatorSettingBaseSupplier); - } else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator) { - chunkGenerator = (ChunkGenerator) delegateField.get(originalChunkProvider.getGenerator()); - } else { - LOGGER.error("Unsupported generator type {}", originalChunkProvider.getGenerator().getClass().getName()); - return false; - } - if (generator != null) { - chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator); - generateConcurrent = generator.isParallelCapable(); - } - - freshChunkProvider = new ServerChunkCache( - freshWorld, - session, - server.getFixerUpper(), - server.getStructureManager(), - server.executor, - chunkGenerator, - freshWorld.spigotConfig.viewDistance, - server.forceSynchronousWrites(), - new RegenNoOpWorldLoadListener(), - (chunkCoordIntPair, state) -> { - }, - () -> server.overworld().getDataStorage() - ) { - // redirect to LevelChunks created in #createChunks - @Override - public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) { - ChunkAccess chunkAccess = getChunkAt(x, z); - if (chunkAccess == null && create) { - chunkAccess = createChunk(getProtoChunkAt(x, z)); - } - return chunkAccess; - } - }; - - chunkSourceField.set(freshWorld, freshChunkProvider); - //let's start then - structureManager = server.getStructureManager(); - threadedLevelLightEngine = freshChunkProvider.getLightEngine(); - - return true; - } - - @Override - protected void cleanup() { - try { - session.close(); - } catch (Exception ignored) { - } - - //shutdown chunk provider - try { - Fawe.instance().getQueueHandler().sync(() -> { - try { - freshChunkProvider.close(false); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } catch (Exception ignored) { - } - - //remove world from server - try { - Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap); - } catch (Exception ignored) { - } - - //delete directory - try { - SafeFiles.tryHardToDeleteDir(tempDir); - } catch (Exception ignored) { - } - } - - @Override - protected ProtoChunk createProtoChunk(int x, int z) { - return PaperLib.isPaper() - ? new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, freshWorld) // paper - : new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld); // spigot - } - - @Override - protected LevelChunk createChunk(ProtoChunk protoChunk) { - return new LevelChunk( - freshWorld, - protoChunk, - null // we don't want to add entities - ); - } - - @Override - protected ChunkStatusWrap getFullChunkStatus() { - return new ChunkStatusWrap(ChunkStatus.FULL); - } - - @Override - protected List getBlockPopulators() { - return originalServerWorld.getWorld().getPopulators(); - } - - @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())); - } - - @Override - protected IChunkCache initSourceQueueCache() { - return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) { - @Override - public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) { - return getChunkAt(x, z); - } - }; - } - - //util - @SuppressWarnings("unchecked") - private void removeWorldFromWorldsMap() { - Fawe.instance().getQueueHandler().sync(() -> { - try { - Map map = (Map) worldsField.get(Bukkit.getServer()); - map.remove("faweregentempworld"); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); - } - - private ResourceKey getWorldDimKey(org.bukkit.World.Environment env) { - return switch (env) { - case NETHER -> LevelStem.NETHER; - case THE_END -> LevelStem.END; - default -> LevelStem.OVERWORLD; - }; - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private BiomeSource fastOverworldBiomeSource(BiomeSource biomeSource) throws Exception { - Field legacyBiomeInitLayerField = OverworldBiomeSource.class.getDeclaredField( - Refraction.pickName("legacyBiomeInitLayer", "i")); - legacyBiomeInitLayerField.setAccessible(true); - Field largeBiomesField = OverworldBiomeSource.class.getDeclaredField(Refraction.pickName("largeBiomes", "j")); - largeBiomesField.setAccessible(true); - Field biomeRegistryField = OverworldBiomeSource.class.getDeclaredField(Refraction.pickName("biomes", "k")); - biomeRegistryField.setAccessible(true); - Field areaLazyField = Layer.class.getDeclaredField(Refraction.pickName("area", "b")); - areaLazyField.setAccessible(true); - Method initAreaFactoryMethod = Layers.class.getDeclaredMethod( - Refraction.pickName("getDefaultLayer", "a"), - boolean.class, - int.class, - int.class, - LongFunction.class - ); - initAreaFactoryMethod.setAccessible(true); - - //init new WorldChunkManagerOverworld - boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(biomeSource); - boolean largebiomes = largeBiomesField.getBoolean(biomeSource); - Registry biomeRegistryMojang = (Registry) biomeRegistryField.get(biomeSource); - Registry biomeRegistry; - if (options.hasBiomeType()) { - Biome biome = BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options.getBiomeType().getId())); - biomeRegistry = new MappedRegistry<>( - ResourceKey.createRegistryKey(new ResourceLocation("fawe_biomes")), - Lifecycle.experimental() - ); - ((MappedRegistry) biomeRegistry).registerMapping(0, BuiltinRegistries.BIOME.getResourceKey(biome).get(), biome, - Lifecycle.experimental() - ); - } else { - biomeRegistry = biomeRegistryMojang; - } - - //replace genLayer - AreaFactory factory = (AreaFactory) initAreaFactoryMethod.invoke( - null, - legacyBiomeInitLayer, - largebiomes ? 6 : 4, - 4, - (LongFunction) (salt -> new FastWorldGenContextArea(seed, salt)) - ); - biomeSource = new FastOverworldBiomeSource(biomeRegistry, new FastGenLayer(factory)); - - return biomeSource; - } - - private static class FastOverworldBiomeSource extends BiomeSource { - - private final Registry biomeRegistry; - private final boolean isSingleRegistry; - private final FastGenLayer fastGenLayer; - - public FastOverworldBiomeSource( - Registry biomeRegistry, - FastGenLayer genLayer - ) { - super(biomeRegistry.stream().collect(Collectors.toList())); - this.biomeRegistry = biomeRegistry; - this.isSingleRegistry = biomeRegistry.entrySet().size() == 1; - this.fastGenLayer = genLayer; - } - - @Override - protected Codec codec() { - return OverworldBiomeSource.CODEC; - } - - @Override - public BiomeSource withSeed(final long seed) { - return null; - } - - @Override - public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) { - if (this.isSingleRegistry) { - return this.biomeRegistry.byId(0); - } - return this.fastGenLayer.get(this.biomeRegistry, biomeX, biomeZ); - } - - } - - private static class FastWorldGenContextArea implements BigContext { - - private final ConcurrentHashMap sharedAreaMap = new ConcurrentHashMap<>(); - private final ImprovedNoise improvedNoise; - private final long magicrandom; - private final ConcurrentHashMap map = new ConcurrentHashMap<>(); //needed for multithreaded generation - - public FastWorldGenContextArea(long seed, long lconst) { - this.magicrandom = mix(seed, lconst); - this.improvedNoise = new ImprovedNoise(new SimpleRandomSource(seed)); - } - - private static long mix(long seed, long salt) { - long l = LinearCongruentialGenerator.next(salt, salt); - l = LinearCongruentialGenerator.next(l, salt); - l = LinearCongruentialGenerator.next(l, salt); - long m = LinearCongruentialGenerator.next(seed, l); - m = LinearCongruentialGenerator.next(m, l); - m = LinearCongruentialGenerator.next(m, l); - return m; - } - - @Override - public FastAreaLazy createResult(PixelTransformer pixelTransformer) { - return new FastAreaLazy(sharedAreaMap, pixelTransformer); - } - - @Override - public void initRandom(long x, long z) { - long l = this.magicrandom; - l = LinearCongruentialGenerator.next(l, x); - l = LinearCongruentialGenerator.next(l, z); - l = LinearCongruentialGenerator.next(l, x); - l = LinearCongruentialGenerator.next(l, z); - this.map.put(Thread.currentThread().getId(), l); - } - - @Override - public int nextRandom(int y) { - long tid = Thread.currentThread().getId(); - long e = this.map.computeIfAbsent(tid, i -> 0L); - int mod = (int) Math.floorMod(e >> 24L, (long) y); - this.map.put(tid, LinearCongruentialGenerator.next(e, this.magicrandom)); - return mod; - } - - @Override - public ImprovedNoise getBiomeNoise() { - return this.improvedNoise; - } - - } - - private static class FastGenLayer extends Layer { - - private final FastAreaLazy fastAreaLazy; - - public FastGenLayer(AreaFactory factory) { - super(() -> null); - this.fastAreaLazy = factory.make(); - } - - @Override - public Biome get(Registry registry, int x, int z) { - ResourceKey key = Biomes.byId(this.fastAreaLazy.get(x, z)); - if (key == null) { - return registry.get(Biomes.byId(0)); - } - Biome biome = registry.get(key); - if (biome == null) { - return registry.get(Biomes.byId(0)); - } - return biome; - } - - } - - private record FastAreaLazy(ConcurrentHashMap sharedMap, PixelTransformer transformer) implements Area { - - @Override - public int get(int x, int z) { - long zx = ChunkPos.asLong(x, z); - return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z)); - } - - } - - private static class RegenNoOpWorldLoadListener implements ChunkProgressListener { - - private RegenNoOpWorldLoadListener() { - } - - @Override - public void updateSpawnPos(ChunkPos spawnPos) { - } - - @Override - public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) { - } - - @Override - public void start() { - - } - - @Override - public void stop() { - } - - // TODO Paper only(?) @Override - public void setChunkRadius(int radius) { - } - - } - - private class FastProtoChunk extends ProtoChunk { - - // avoid warning on paper - public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world, ServerLevel serverLevel) { - super(pos, upgradeData, world, serverLevel); - } - - // compatibility with spigot - public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor) { - super(pos, upgradeData, levelHeightAccessor); - } - - public boolean generateFlatBedrock() { - return generateFlatBedrock; - } - - // no one will ever see the entities! - @Override - public List getEntities() { - return Collections.emptyList(); - } - - } - - protected class ChunkStatusWrap extends ChunkStatusWrapper { - - private final ChunkStatus chunkStatus; - - public ChunkStatusWrap(ChunkStatus chunkStatus) { - this.chunkStatus = chunkStatus; - } - - @Override - public int requiredNeighborChunkRadius() { - return chunkStatus.getRange(); - } - - @Override - public String name() { - return chunkStatus.getName(); - } - - @Override - public CompletableFuture processChunk(List accessibleChunks) { - return chunkStatus.generate( - Runnable::run, // TODO revisit, we might profit from this somehow? - freshWorld, - chunkGenerator, - structureManager, - threadedLevelLightEngine, - c -> CompletableFuture.completedFuture(Either.left(c)), - accessibleChunks - ); - } - - } - -} diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 1a922b7af..e02685954 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -210,7 +210,7 @@ tasks { versionNumber.set("${project.version}") versionType.set("release") uploadFile.set(file("build/libs/${rootProject.name}-Bukkit-${project.version}.jar")) - gameVersions.addAll(listOf("1.20.4", "1.20.3", "1.20.2", "1.20.1", "1.20", "1.19.4", "1.18.2", "1.17.1")) + gameVersions.addAll(listOf("1.20.4", "1.20.3", "1.20.2", "1.20.1", "1.20", "1.19.4", "1.18.2")) loaders.addAll(listOf("paper", "spigot")) changelog.set("The changelog is available on GitHub: https://github.com/IntellectualSites/" + "FastAsyncWorldEdit/releases/tag/${project.version}") diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java index d35ea9a28..3bf09481e 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitServerInterface.java @@ -307,9 +307,6 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser if (!tickFluid) { return null; } - if (Settings.settings().QUEUE.NO_TICK_FASTMODE && fastMode) { - return null; - } return this.plugin.getBukkitImplAdapter().getTickingPostProcessor(); } //FAWE end diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index 009958270..89f48b697 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -511,7 +511,7 @@ public class Settings extends Config { " - A larger value will use slightly less CPU time", " - A smaller value will reduce memory usage", " - A value too small may break some operations (deform?)", - " - Values smaller than the configurated parallel-threads are not accepted", + " - Values smaller than the configured parallel-threads are not accepted", " - It is recommended this option be at least 4x greater than parallel-threads" }) @@ -544,12 +544,6 @@ public class Settings extends Config { }) public boolean POOL = true; - @Comment({ - "When using fastmode do not bother to tick existing/placed blocks/fluids", - "Only works in versions up to 1.17.1" - }) - public boolean NO_TICK_FASTMODE = true; - public static class PROGRESS { @Comment({"Display constant titles about the progress of a user's edit", From 2d0a3e6081967bb677afffbc03ba39f67745280d Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 17 Mar 2024 13:15:28 +0100 Subject: [PATCH 092/114] fix: do not error when attempting to parse "|" as a pattern (#2625) --- .../extension/factory/parser/DefaultBlockParser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index 0e70a18a6..969216896 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -64,6 +64,7 @@ import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityTypes; import com.sk89q.worldedit.world.registry.LegacyMapper; +import javax.annotation.Nonnull; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; @@ -337,9 +338,10 @@ public class DefaultBlockParser extends InputParser { return SuggestionHelper.getBlockPropertySuggestions(blockType, props); } + @Nonnull private BaseBlock parseLogic(String input, ParserContext context) throws InputParseException { //FAWE start - String[] blockAndExtraData = input.trim().split("\\|"); + String[] blockAndExtraData = input.trim().split("(?, Object> blockStates = new HashMap<>(); //FAWE end From 2d0ea9adf8832684d2bb7c73e42cc87dc9c03a7f Mon Sep 17 00:00:00 2001 From: Alexander Brandes Date: Sun, 17 Mar 2024 13:16:10 +0100 Subject: [PATCH 093/114] Reword tree type message (#2628) Signed-off-by: Alexander Brandes --- .../main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 a9ba45afe..1598cb547 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 @@ -327,12 +327,12 @@ public class BukkitWorld extends AbstractWorld { treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_MUSHROOM, TreeType.BROWN_MUSHROOM); for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) { if (treeTypeMapping.get(type) == null) { - LOGGER.error("No TreeType mapping for TreeGenerator.TreeType." + type); //FAWE start + LOGGER.info("No TreeType mapping for TreeGenerator.TreeType." + type); LOGGER.info("The above message is displayed because your FAWE version is newer than {}" + " and contains features of future minecraft versions which do not exist in {} hence the tree type" + - "{} is not available. This is not an error. This version will work on your version of Minecraft." + - "This is an informative message only.", Bukkit.getVersion(), Bukkit.getVersion(), type); + " {} is not available. This is not an error. This version of FAWE will work on your version of " + + " Minecraft. This is an informative message only.", Bukkit.getVersion(), Bukkit.getVersion(), type); //FAWE end } } From dc61efe11ccf4c41a3bc0c791fea06780884019d Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sun, 17 Mar 2024 13:17:00 +0100 Subject: [PATCH 094/114] Implement linear patterns using RandomPattern + SimpleRandom (#2630) --- .../parser/pattern/Linear2DPatternParser.java | 8 +-- .../parser/pattern/Linear3DPatternParser.java | 8 +-- .../pattern/Linear2DBlockPattern.java | 5 ++ .../pattern/Linear3DBlockPattern.java | 5 ++ .../core/math/random/Linear2DRandom.java | 50 ++++++++++++++++ .../core/math/random/Linear3DRandom.java | 58 +++++++++++++++++++ .../core/math/random/SimpleRandom.java | 18 +++++- .../collection/SimpleRandomCollection.java | 2 +- 8 files changed, 141 insertions(+), 13 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/Linear2DRandom.java create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/Linear3DRandom.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear2DPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear2DPatternParser.java index 5a310f60a..7da0b2377 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear2DPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear2DPatternParser.java @@ -2,7 +2,7 @@ package com.fastasyncworldedit.core.extension.factory.parser.pattern; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.extension.factory.parser.RichParser; -import com.fastasyncworldedit.core.function.pattern.Linear2DBlockPattern; +import com.fastasyncworldedit.core.math.random.Linear2DRandom; import com.google.common.base.Preconditions; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.util.SuggestionHelper; @@ -14,7 +14,6 @@ import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.block.BlockStateHolder; import javax.annotation.Nonnull; -import java.util.Set; import java.util.stream.Stream; public class Linear2DPatternParser extends RichParser { @@ -59,9 +58,8 @@ public class Linear2DPatternParser extends RichParser { zScale = Integer.parseInt(arguments[2]); Preconditions.checkArgument(zScale != 0); } - if (inner instanceof RandomPattern) { - Set patterns = ((RandomPattern) inner).getPatterns(); - return new Linear2DBlockPattern(patterns.toArray(new Pattern[0]), xScale, zScale); + if (inner instanceof RandomPattern rp) { + return new RandomPattern(new Linear2DRandom(xScale, zScale), rp); } throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName() + " cannot be used with " + getPrefix())); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear3DPatternParser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear3DPatternParser.java index ef45e8faa..cd3a7d8db 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear3DPatternParser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extension/factory/parser/pattern/Linear3DPatternParser.java @@ -2,7 +2,7 @@ package com.fastasyncworldedit.core.extension.factory.parser.pattern; import com.fastasyncworldedit.core.configuration.Caption; import com.fastasyncworldedit.core.extension.factory.parser.RichParser; -import com.fastasyncworldedit.core.function.pattern.Linear3DBlockPattern; +import com.fastasyncworldedit.core.math.random.Linear3DRandom; import com.google.common.base.Preconditions; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.util.SuggestionHelper; @@ -14,7 +14,6 @@ import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.block.BlockStateHolder; import javax.annotation.Nonnull; -import java.util.Set; import java.util.stream.Stream; public class Linear3DPatternParser extends RichParser { @@ -64,9 +63,8 @@ public class Linear3DPatternParser extends RichParser { zScale = Integer.parseInt(arguments[3]); Preconditions.checkArgument(zScale != 0); } - if (inner instanceof RandomPattern) { - Set patterns = ((RandomPattern) inner).getPatterns(); - return new Linear3DBlockPattern(patterns.toArray(new Pattern[0]), xScale, yScale, zScale); + if (inner instanceof RandomPattern rp) { + return new RandomPattern(new Linear3DRandom(xScale, yScale, zScale), rp); } throw new InputParseException(TextComponent.of("Pattern " + inner.getClass().getSimpleName() + " cannot be used with " + getPrefix())); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear2DBlockPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear2DBlockPattern.java index 7c923c4d4..523f22541 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear2DBlockPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear2DBlockPattern.java @@ -9,6 +9,11 @@ import com.sk89q.worldedit.world.block.BaseBlock; import static java.lang.Math.floorDiv; +/** + * @deprecated replaced by {@link com.sk89q.worldedit.function.pattern.RandomPattern} + * combined with {@link com.fastasyncworldedit.core.math.random.Linear2DRandom}. + */ +@Deprecated(forRemoval = true, since = "TODO") public class Linear2DBlockPattern extends AbstractPattern { private final Pattern[] patternsArray; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear3DBlockPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear3DBlockPattern.java index 04028244d..ea2e37588 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear3DBlockPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear3DBlockPattern.java @@ -9,6 +9,11 @@ import com.sk89q.worldedit.world.block.BaseBlock; import static java.lang.Math.floorDiv; +/** + * @deprecated replaced by {@link com.sk89q.worldedit.function.pattern.RandomPattern} + * combined with {@link com.fastasyncworldedit.core.math.random.Linear3DRandom}. + */ +@Deprecated(forRemoval = true, since = "TODO") public class Linear3DBlockPattern extends AbstractPattern { private final Pattern[] patternsArray; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/Linear2DRandom.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/Linear2DRandom.java new file mode 100644 index 000000000..4f039031e --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/Linear2DRandom.java @@ -0,0 +1,50 @@ +package com.fastasyncworldedit.core.math.random; + +import static com.fastasyncworldedit.core.math.random.Linear3DRandom.doubleDiv; +import static java.lang.Math.floorDiv; + +/** + * A {@link SimpleRandom} that deterministically maps coordinates + * to values. + * @since TODO + */ +public class Linear2DRandom implements SimpleRandom { + private final int xScale; + private final int zScale; + + /** + * Creates a new {@link Linear2DRandom} instance + * + * @param xScale the scale applied to the x component of a coordinate + * @param zScale the scale applied to the z component of a coordinate + */ + public Linear2DRandom(final int xScale, final int zScale) { + this.xScale = xScale; + this.zScale = zScale; + } + + @Override + public double nextDouble(final int x, final int y, final int z) { + return nextDouble(x, y, z, 1d); + } + + @Override + public double nextDouble(final int x, final int y, final int z, double bound) { + double index = (doubleDiv(x, this.xScale) + doubleDiv(z, this.zScale)) % bound; + if (index < 0) { + index += bound; + } + return index; + + } + + @Override + public int nextInt(final int x, final int y, final int z, final int bound) { + int index = (floorDiv(x, this.xScale) + floorDiv(z, this.zScale)) % bound; + if (index < 0) { + index += bound; + } + return index; + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/Linear3DRandom.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/Linear3DRandom.java new file mode 100644 index 000000000..87f350fe4 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/Linear3DRandom.java @@ -0,0 +1,58 @@ +package com.fastasyncworldedit.core.math.random; + +import static java.lang.Math.floorDiv; + +/** + * A {@link SimpleRandom} that deterministically maps coordinates + * to values. + * @since TODO + */ +public class Linear3DRandom implements SimpleRandom { + + private final int xScale; + private final int yScale; + private final int zScale; + + /** + * Creates a new {@link Linear3DRandom} instance + * + * @param xScale the scale applied to the x component of a coordinate + * @param yScale the scale applied to the y component of a coordinate + * @param zScale the scale applied to the z component of a coordinate + */ + public Linear3DRandom(final int xScale, final int yScale, final int zScale) { + this.xScale = xScale; + this.yScale = yScale; + this.zScale = zScale; + } + + @Override + public double nextDouble(final int x, final int y, final int z) { + return nextDouble(x, y, z, 1d); + } + + @Override + public double nextDouble(final int x, final int y, final int z, double bound) { + double index = (doubleDiv(x, this.xScale) + doubleDiv(y, this.yScale) + doubleDiv(z, this.zScale)) % bound; + if (index < 0) { + index += bound; + } + return index; + } + + // used to avoid explicit conversion at call site + static double doubleDiv(double dividend, double divisor) { + // add a minimal value to avoid too many integral values hitting the exact weight of an entry in SimpleRandomCollection + return Math.nextUp(dividend) / divisor; + } + + @Override + public int nextInt(final int x, final int y, final int z, final int bound) { + int index = (floorDiv(x, this.xScale) + floorDiv(y, this.yScale) + floorDiv(z, this.zScale)) % bound; + if (index < 0) { + index += bound; + } + return index; + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/SimpleRandom.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/SimpleRandom.java index e38214936..8b22b66a2 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/SimpleRandom.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/math/random/SimpleRandom.java @@ -13,6 +13,20 @@ public interface SimpleRandom { */ double nextDouble(int x, int y, int z); + /** + * Generate a random double from three integer components. + * The generated value is between 0 (inclusive) and {@code bound} (exclusive). + * + * @param x the first component + * @param y the second component + * @param z the third component + * @param bound upper bound (exclusive) + * @return a double between 0 (inclusive) and {@code bound} (exclusive) + */ + default double nextDouble(int x, int y, int z, double bound) { + return nextDouble(x, y, z) * bound; + } + /** * Generate a random integer from three integer components. * The generated value is between 0 (inclusive) and 1 (exclusive) @@ -24,8 +38,8 @@ public interface SimpleRandom { * @return a random integer between 0 (inclusive) and {@code bound} (exclusive) */ default int nextInt(int x, int y, int z, int bound) { - double val = nextDouble(x, y, z); - return (int) (val * bound); + double val = nextDouble(x, y, z, bound); + return (int) val; } } 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 dc2107559..5b7dca98f 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 @@ -36,7 +36,7 @@ public class SimpleRandomCollection extends RandomCollection { @Override public E next(int x, int y, int z) { - return map.ceilingEntry(getRandom().nextDouble(x, y, z) * this.total).getValue(); + return map.ceilingEntry(getRandom().nextDouble(x, y, z, this.total)).getValue(); } } From d2ca3ed6fe8ca58ca31a441a3ebecdc943f4431f Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 17 Mar 2024 20:25:09 +0100 Subject: [PATCH 095/114] fix: better image reading errors (#2632) - closes #2593 --- .../core/command/tool/brush/HeightBrush.java | 2 +- .../core/util/MainUtil.java | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java index fea4884f1..2c6ea5ec0 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/command/tool/brush/HeightBrush.java @@ -53,7 +53,7 @@ public class HeightBrush implements Brush { try { heightMap = ScalableHeightMap.fromPNG(stream); } catch (IOException e) { - throw new FaweException(Caption.of("fawe.worldedit.brush.brush.height.invalid")); + throw new FaweException(Caption.of("fawe.worldedit.brush.brush.height.invalid", e.getMessage())); } } else if (clipboard != null) { heightMap = ScalableHeightMap.fromClipboard(clipboard, minY, maxY); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java index 8122a840c..cbe9c6897 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java @@ -38,6 +38,8 @@ import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; @@ -70,6 +72,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -519,8 +522,22 @@ public class MainUtil { return destFile; } - public static BufferedImage readImage(InputStream in) throws IOException { - return MainUtil.toRGB(ImageIO.read(in)); + public static BufferedImage readImage(InputStream stream) throws IOException { + Iterator iter = ImageIO.getImageReaders(stream); + if (!iter.hasNext()) { + throw new IOException("Could not get image reader from stream."); + } + ImageReader reader = iter.next(); + ImageReadParam param = reader.getDefaultReadParam(); + reader.setInput(stream, true, true); + BufferedImage bi; + try { + bi = reader.read(0, param); + } finally { + reader.dispose(); + stream.close(); + } + return MainUtil.toRGB(bi); } public static BufferedImage readImage(URL url) throws IOException { From b93f01c5b30871ddf0a8ca6e26a8d525280bad9a Mon Sep 17 00:00:00 2001 From: Pierre Maurice Schwang Date: Sun, 17 Mar 2024 20:25:29 +0100 Subject: [PATCH 096/114] Update paperweight 1.20.4 - isOpaque does not exist (anymore) (#2629) * chore/fix: update paperweight 1.20.4 - isOpaque does not exist (anymore) * chore: update paperweight yet again (i feel scammed) * chore: missing invert of canOcclude in isTranslucent --- .../adapters/adapter-1_20_4/build.gradle.kts | 2 +- .../v1_20_R3/PaperweightBlockMaterial.java | 22 +++++-------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index 02f5a3c53..f18ef582f 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT - the().paperDevBundle("1.20.4-R0.1-20240106.182028-62") + the().paperDevBundle("1.20.4-R0.1-20240316.193646-135") compileOnly(libs.paperlib) } diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java index edbe268fe..74b1c035c 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightBlockMaterial.java @@ -2,18 +2,15 @@ package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3; import com.google.common.base.Suppliers; import com.sk89q.jnbt.CompoundTag; -import com.sk89q.util.ReflectionUtil; -import com.sk89q.worldedit.bukkit.adapter.Refraction; import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R3.nbt.PaperweightLazyCompoundTag; import com.sk89q.worldedit.world.registry.BlockMaterial; import net.minecraft.core.BlockPos; import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.material.PushReaction; import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; @@ -21,7 +18,6 @@ public class PaperweightBlockMaterial implements BlockMaterial { private final Block block; private final BlockState blockState; - private final boolean isTranslucent; private final CraftBlockData craftBlockData; private final org.bukkit.Material craftMaterial; private final int opacity; @@ -36,11 +32,6 @@ public class PaperweightBlockMaterial implements BlockMaterial { this.blockState = blockState; this.craftBlockData = CraftBlockData.fromData(blockState); this.craftMaterial = craftBlockData.getMaterial(); - BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block, - Refraction.pickName("properties", "aP")); - this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo, - Refraction.pickName("canOcclude", "n") - ); opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity( BlockPos.ZERO, @@ -75,7 +66,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { @Override public boolean isOpaque() { - return blockState.isOpaque(); + return blockState.canOcclude(); } @Override @@ -85,14 +76,13 @@ public class PaperweightBlockMaterial implements BlockMaterial { @Override public boolean isLiquid() { - // TODO: Better check ? - return block instanceof LiquidBlock; + return !blockState.getFluidState().is(Fluids.EMPTY); } @Override public boolean isSolid() { - // TODO: Replace - return blockState.isSolid(); + // No access to world -> EmptyBlockGetter + return blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); } @Override @@ -158,7 +148,7 @@ public class PaperweightBlockMaterial implements BlockMaterial { @Override public boolean isTranslucent() { - return isTranslucent; + return !blockState.canOcclude(); } @Override From b512182e1f6e417501236abab56d6b39a6080229 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 17 Mar 2024 20:25:43 +0100 Subject: [PATCH 097/114] feat: save region selector to session store (#2621) * feat: save region selector to session store - closes #2397 * Move new gson adapters to FAWE packages --- .../core}/util/gson/ItemTypeAdapter.java | 2 +- .../core/util/gson/RegionSelectorAdapter.java | 33 +++++++++++++++++ .../com/sk89q/worldedit/LocalSession.java | 5 ++- .../worldedit/command/SelectionCommands.java | 8 +---- .../regions/selector/RegionSelectorType.java | 35 ++++++++++++++++++- .../sk89q/worldedit/util/gson/GsonUtil.java | 7 +++- 6 files changed, 79 insertions(+), 11 deletions(-) rename worldedit-core/src/main/java/com/{sk89q/worldedit => fastasyncworldedit/core}/util/gson/ItemTypeAdapter.java (94%) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/RegionSelectorAdapter.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/ItemTypeAdapter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/ItemTypeAdapter.java similarity index 94% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/ItemTypeAdapter.java rename to worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/ItemTypeAdapter.java index 5f8d6d9a2..fe684fb8c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/ItemTypeAdapter.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/ItemTypeAdapter.java @@ -1,4 +1,4 @@ -package com.sk89q.worldedit.util.gson; +package com.fastasyncworldedit.core.util.gson; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/RegionSelectorAdapter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/RegionSelectorAdapter.java new file mode 100644 index 000000000..d817185ee --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/RegionSelectorAdapter.java @@ -0,0 +1,33 @@ +package com.fastasyncworldedit.core.util.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.sk89q.worldedit.regions.RegionSelector; +import com.sk89q.worldedit.regions.selector.RegionSelectorType; + +import java.lang.reflect.Type; + +public class RegionSelectorAdapter implements JsonDeserializer, JsonSerializer { + + @Override + public RegionSelector deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + RegionSelectorType regionType = RegionSelectorType.valueOf(json.getAsString()); + return regionType.createSelector(); + } + + @Override + public JsonElement serialize(RegionSelector selector, Type type, JsonSerializationContext context) { + RegionSelectorType regionType = RegionSelectorType.getForSelector(selector); + // Cannot nicely deserialize Fuzzy region type + if (regionType == null || regionType == RegionSelectorType.FUZZY) { + return null; + } + return new JsonPrimitive(regionType.toString()); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index a00b26702..5a5fa654e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -125,7 +125,9 @@ public class LocalSession implements TextureHolder { private transient int cuiVersion = CUI_VERSION_UNINITIALIZED; // Session related - private transient RegionSelector selector = new CuboidRegionSelector(); + //FAWE start - allow saving to session store + private RegionSelector selector = new CuboidRegionSelector(); + //FAWE end private transient boolean placeAtPos1 = false; //FAWE start private final transient List history = Collections.synchronizedList(new LinkedList<>() { @@ -771,6 +773,7 @@ public class LocalSession implements TextureHolder { checkNotNull(selector); selector.setWorld(world); this.selector = selector; + setDirty(); if (hasWorldOverride() && !world.equals(getWorldOverride())) { setWorldOverride(null); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index 64e3c8a81..a643742d2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -755,13 +755,7 @@ public class SelectionCommands { } if (setDefaultSelector) { - RegionSelectorType found = null; - for (RegionSelectorType type : RegionSelectorType.values()) { - if (type.getSelectorClass() == newSelector.getClass()) { - found = type; - break; - } - } + RegionSelectorType found = RegionSelectorType.getForSelector(newSelector); if (found != null) { session.setDefaultRegionSelector(found); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/RegionSelectorType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/RegionSelectorType.java index 61e46aeab..5c8655893 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/RegionSelectorType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/selector/RegionSelectorType.java @@ -19,8 +19,14 @@ package com.sk89q.worldedit.regions.selector; +import com.fastasyncworldedit.core.regions.selector.FuzzyRegionSelector; +import com.fastasyncworldedit.core.regions.selector.PolyhedralRegionSelector; import com.sk89q.worldedit.regions.RegionSelector; +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; + /** * An enum of default region selector types. */ @@ -32,7 +38,21 @@ public enum RegionSelectorType { SPHERE(SphereRegionSelector.class), ELLIPSOID(EllipsoidRegionSelector.class), POLYGON(Polygonal2DRegionSelector.class), - CONVEX_POLYHEDRON(ConvexPolyhedralRegionSelector.class); + CONVEX_POLYHEDRON(ConvexPolyhedralRegionSelector.class), + //FAWE start + POLYHEDRAL(PolyhedralRegionSelector.class), + FUZZY(FuzzyRegionSelector.class); + //FAWE end + + //FAWE start + private static final Map, RegionSelectorType> VALUE_MAP = new HashMap<>(); + + static { + for (RegionSelectorType type : values()) { + VALUE_MAP.put(type.getSelectorClass(), type); + } + } + //FAWE end private final Class selectorClass; @@ -40,6 +60,19 @@ public enum RegionSelectorType { this.selectorClass = selectorClass; } + //FAWE start + /** + * Get a {@link RegionSelectorType} for the given {@link RegionSelector} + * + * @param selector Region selector to get type enum for + * @since TODO + */ + @Nullable + public static RegionSelectorType getForSelector(RegionSelector selector) { + return VALUE_MAP.get(selector.getClass()); + } + //FAWE end + /** * Get the selector class. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java index d31750e11..e5d05ee6d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java @@ -19,12 +19,14 @@ package com.sk89q.worldedit.util.gson; +import com.fastasyncworldedit.core.util.gson.ItemTypeAdapter; +import com.fastasyncworldedit.core.util.gson.RegionSelectorAdapter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.world.item.ItemType; -import com.sk89q.worldedit.world.item.ItemTypes; /** * Utility methods for Google's GSON library. @@ -43,7 +45,10 @@ public final class GsonUtil { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Vector3.class, new VectorAdapter()); gsonBuilder.registerTypeAdapter(BlockVector3.class, new BlockVectorAdapter()); + //FAWE start + gsonBuilder.registerTypeAdapter(RegionSelector.class, new RegionSelectorAdapter()); gsonBuilder.registerTypeAdapter(ItemType.class, new ItemTypeAdapter()); + //FAWE end return gsonBuilder; } From 38242068f043f190b491b84f10b2008cc2fd3a73 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:18:17 +0000 Subject: [PATCH 098/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.21 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a6acb6c2e..1b9bef60e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.1.20" +towny = "0.100.1.21" plotsquared = "7.3.6" # Third party From 36da507cf7972a83a45b0fb46de4ceed470e39a3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:22:41 +0000 Subject: [PATCH 099/114] Update dependency paperweight-userdev to v1.20.4-R0.1-20240319.191757-136 --- worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index f18ef582f..d1250a94f 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT - the().paperDevBundle("1.20.4-R0.1-20240316.193646-135") + the().paperDevBundle("1.20.4-R0.1-20240319.191757-136") compileOnly(libs.paperlib) } From 63d5852b6c73c2851d40a0135bcf897e1c6e999c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 20:46:11 +0000 Subject: [PATCH 100/114] Update dependency paperweight-userdev to v1.20.4-R0.1-20240320.194253-137 --- worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index d1250a94f..bc85e188f 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT - the().paperDevBundle("1.20.4-R0.1-20240319.191757-136") + the().paperDevBundle("1.20.4-R0.1-20240320.194253-137") compileOnly(libs.paperlib) } From 68eb24a2f92a084c8a368b8d9bef725248d5f017 Mon Sep 17 00:00:00 2001 From: Pierre Maurice Schwang Date: Wed, 20 Mar 2024 23:39:36 +0100 Subject: [PATCH 101/114] fix: imgur image resolution, image input stream reading (#2637) --- .../core/util/MainUtil.java | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java index cbe9c6897..41c4e5704 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/MainUtil.java @@ -40,6 +40,7 @@ import javax.annotation.Nullable; import javax.imageio.ImageIO; import javax.imageio.ImageReadParam; import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; @@ -58,6 +59,9 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; @@ -94,6 +98,10 @@ import static java.lang.System.arraycopy; public class MainUtil { private static final Logger LOGGER = LogManagerCompat.getLogger(); + private static final String CURL_USER_AGENT = "curl/8.1.1"; + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() + .followRedirects(HttpClient.Redirect.NORMAL) + .build(); public static List filter(String prefix, List suggestions) { if (prefix.isEmpty()) { @@ -523,25 +531,52 @@ public class MainUtil { } public static BufferedImage readImage(InputStream stream) throws IOException { - Iterator iter = ImageIO.getImageReaders(stream); + final ImageInputStream imageStream = ImageIO.createImageInputStream(stream); + if (imageStream == null) { + throw new IOException("Can't find suitable ImageInputStream"); + } + Iterator iter = ImageIO.getImageReaders(imageStream); if (!iter.hasNext()) { throw new IOException("Could not get image reader from stream."); } ImageReader reader = iter.next(); ImageReadParam param = reader.getDefaultReadParam(); - reader.setInput(stream, true, true); + reader.setInput(imageStream, true, true); BufferedImage bi; try { bi = reader.read(0, param); } finally { reader.dispose(); stream.close(); + imageStream.close(); } return MainUtil.toRGB(bi); } public static BufferedImage readImage(URL url) throws IOException { - return readImage(url.openStream()); + try { + final URI uri = url.toURI(); + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).GET(); + + if (uri.getHost().equalsIgnoreCase("i.imgur.com")) { + requestBuilder = requestBuilder.setHeader("User-Agent", CURL_USER_AGENT); + } + + final HttpResponse response = HTTP_CLIENT.send( + requestBuilder.build(), + HttpResponse.BodyHandlers.ofInputStream() + ); + try (final InputStream body = response.body()) { + if (response.statusCode() > 299) { + throw new IOException("Expected 2xx as response code, but received " + response.statusCode()); + } + return readImage(body); + } + } catch (InterruptedException e) { + throw new IOException("request was interrupted", e); + } catch (URISyntaxException e) { + throw new IOException("failed to parse url to uri reference", e); + } } public static BufferedImage readImage(File file) throws IOException { From 07011e242063317bad97ee197afeee66ef0cb380 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:40:52 +0000 Subject: [PATCH 102/114] Update dependency paperweight-userdev to v1.20.4-R0.1-20240320.215354-140 --- worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index bc85e188f..b225a6875 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT - the().paperDevBundle("1.20.4-R0.1-20240320.194253-137") + the().paperDevBundle("1.20.4-R0.1-20240320.215354-140") compileOnly(libs.paperlib) } From a957af38c09fb8756202bf8bf978bd66f8d71b7f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 22:40:43 +0000 Subject: [PATCH 103/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.22 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1b9bef60e..dd3e3d38e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.1.21" +towny = "0.100.1.22" plotsquared = "7.3.6" # Third party From 4b91f6410f519103dec01b6603404fbd6ffee4f8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:29:04 +0000 Subject: [PATCH 104/114] Update dependency gradle to v8.7 --- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch delta 34118 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 delta 34176 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 SGW6ptSs!X7{rG*o_g?|vpSEZK diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce5..b82aa23a4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 0182d52c02884284068fec30dadad75dc6e2fc7b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:14:32 +0000 Subject: [PATCH 105/114] Update dependency com.palmergames.bukkit.towny:towny to v0.100.1.23 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dd3e3d38e..85d7a18c8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ mapmanager = "1.8.0-SNAPSHOT" griefprevention = "17.0.0" griefdefender = "2.1.0-SNAPSHOT" residence = "4.5._13.1" -towny = "0.100.1.22" +towny = "0.100.1.23" plotsquared = "7.3.6" # Third party From 9d20df9e6b81896b3244115515df3a0f896f6658 Mon Sep 17 00:00:00 2001 From: Zeranny Date: Sat, 23 Mar 2024 19:26:13 +0000 Subject: [PATCH 106/114] Fix queryRel returning data value for block at absolute coordinates (#2645) * Replace toWorld with toWorldRel for relative data * re-add accidentally deleted annotation --- .../worldedit/regions/shape/WorldEditExpressionEnvironment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java index 4b87ebcc6..2284b9733 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/regions/shape/WorldEditExpressionEnvironment.java @@ -85,7 +85,7 @@ public class WorldEditExpressionEnvironment implements ExpressionEnvironment { @SuppressWarnings("deprecation") @Override public int getBlockDataRel(double x, double y, double z) { - return extent.getBlock(toWorld(x, y, z)).getBlockType().getLegacyCombinedId() & 0xF; + return extent.getBlock(toWorldRel(x, y, z).toBlockPoint()).getBlockType().getLegacyCombinedId() & 0xF; } //FAWE start From 4c98ac2bd877062572507886d5fea0fe8447c4db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Mar 2024 19:27:44 +0000 Subject: [PATCH 107/114] Update eps1lon/actions-label-merge-conflict action to v3 --- .github/workflows/label-merge-conflicts.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-merge-conflicts.yaml b/.github/workflows/label-merge-conflicts.yaml index d189f5520..fa6f3d72d 100644 --- a/.github/workflows/label-merge-conflicts.yaml +++ b/.github/workflows/label-merge-conflicts.yaml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Label conflicting PRs - uses: eps1lon/actions-label-merge-conflict@v2.1.0 + uses: eps1lon/actions-label-merge-conflict@v3.0.0 with: dirtyLabel: "unresolved-merge-conflict" repoToken: "${{ secrets.GITHUB_TOKEN }}" From 5e234a7003e00674460eed65140f1a619a128d27 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Mar 2024 21:56:42 +0000 Subject: [PATCH 108/114] Update dependency paperweight-userdev to v1.20.4-R0.1-20240323.213332-142 --- worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index b225a6875..088dd6f41 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT - the().paperDevBundle("1.20.4-R0.1-20240320.215354-140") + the().paperDevBundle("1.20.4-R0.1-20240323.213332-142") compileOnly(libs.paperlib) } From c10f58ac950ef1a5ac343e4bcdecd7ede067dcaf Mon Sep 17 00:00:00 2001 From: Zeranny Date: Sun, 24 Mar 2024 14:49:58 +0000 Subject: [PATCH 109/114] Fix Linear pattern index incrementing above array length (#2626) * Fix index incrementing above array length in async * Add fork method --- .../function/pattern/LinearBlockPattern.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java index 086cf3a31..0b6ef1be9 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java @@ -1,5 +1,6 @@ package com.fastasyncworldedit.core.function.pattern; +import com.fastasyncworldedit.core.queue.Filter; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.pattern.AbstractPattern; @@ -7,6 +8,8 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; +import java.util.Arrays; + public class LinearBlockPattern extends AbstractPattern implements ResettablePattern { private final Pattern[] patternsArray; @@ -15,7 +18,7 @@ public class LinearBlockPattern extends AbstractPattern implements ResettablePat /** * Create a new {@link Pattern} instance * - * @param patterns array of patterns to linearly choose from based on x/z coordinates + * @param patterns array of patterns to linearly choose from */ public LinearBlockPattern(Pattern[] patterns) { this.patternsArray = patterns; @@ -23,18 +26,20 @@ public class LinearBlockPattern extends AbstractPattern implements ResettablePat @Override public BaseBlock applyBlock(BlockVector3 position) { - if (index == patternsArray.length) { - index = 0; - } - return patternsArray[index++].applyBlock(position); + index = (index + 1) % patternsArray.length; + return patternsArray[index].applyBlock(position); } @Override public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { - if (index == patternsArray.length) { - index = 0; - } - return patternsArray[index++].apply(extent, get, set); + index = (index + 1) % patternsArray.length; + return patternsArray[index].apply(extent, get, set); + } + + @Override + public Filter fork() { + final Pattern[] forked = Arrays.stream(this.patternsArray).map(Pattern::fork).toArray(Pattern[]::new); + return new LinearBlockPattern(forked); } @Override From 2dee197f2b1fad53827a2a2dcb2f02d84fb2e7a4 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sun, 24 Mar 2024 18:02:16 +0100 Subject: [PATCH 110/114] Use correct find class in ExtentTraverser (#2649) use correct find class in ExtentTraverser --- .../com/fastasyncworldedit/core/util/ExtentTraverser.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java index e7038deaa..6ae147765 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/ExtentTraverser.java @@ -51,11 +51,10 @@ public class ExtentTraverser { return last; } - @SuppressWarnings("unchecked") @Nullable - public U findAndGet(Class clazz) { - ExtentTraverser traverser = find(clazz); - return (traverser != null) ? (U) traverser.get() : null; + public U findAndGet(Class clazz) { + ExtentTraverser traverser = find(clazz); + return (traverser != null) ? traverser.get() : null; } @SuppressWarnings("unchecked") From c68544d66d095102511f971bd70ab2533eb9e343 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:51:53 +0000 Subject: [PATCH 111/114] Update dependency paperweight-userdev to v1.20.4-R0.1-20240325.123556-143 --- worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts index 088dd6f41..d55d44a76 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_20_4/build.gradle.kts @@ -12,6 +12,6 @@ repositories { dependencies { // url=https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/1.20.4-R0.1-SNAPSHOT - the().paperDevBundle("1.20.4-R0.1-20240323.213332-142") + the().paperDevBundle("1.20.4-R0.1-20240325.123556-143") compileOnly(libs.paperlib) } From 8095111effa158f1d0e7991768e2ea1317321f6c Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Wed, 27 Mar 2024 15:15:42 +0100 Subject: [PATCH 112/114] Simplify processor ordering (#2651) --- .../extent/processor/MultiBatchProcessor.java | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java index e57ccb490..c266fd544 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/processor/MultiBatchProcessor.java @@ -19,12 +19,9 @@ import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.function.Supplier; @@ -80,28 +77,18 @@ public class MultiBatchProcessor implements IBatchProcessor { @Override public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) { - Map> ordered = new HashMap<>(); + Map> ordered = new HashMap<>(); IChunkSet chunkSet = set; for (IBatchProcessor processor : processors) { if (processor.getScope() != ProcessorScope.ADDING_BLOCKS) { - ordered.merge( - processor.getScope().intValue(), - new HashSet<>(Collections.singleton(processor)), - (existing, theNew) -> { - existing.add(processor); - return existing; - } - ); + ordered.computeIfAbsent(processor.getScope().intValue(), k -> new ArrayList<>()) + .add(processor); continue; } chunkSet = processSet(processor, chunk, get, chunkSet); } - if (ordered.size() > 0) { - for (int i = 1; i <= 4; i++) { - Set processors = ordered.get(i); - if (processors == null) { - continue; - } + if (!ordered.isEmpty()) { + for (List processors : ordered.values()) { for (IBatchProcessor processor : processors) { chunkSet = processSet(processor, chunk, get, chunkSet); if (chunkSet == null) { From 7b8c7894bae20f30acb1504028d591e205a025b7 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Wed, 27 Mar 2024 15:16:06 +0100 Subject: [PATCH 113/114] Make usage of stateful patterns thread-safe (#2633) * Make usage of No(Axis)Patterns thread-safe * make more pattern usage thread-safe * make more WE patterns thread safe * remove StatefulPattern --- .../core/function/pattern/ExpressionPattern.java | 6 ++++++ .../function/pattern/Linear2DBlockPattern.java | 8 ++++++++ .../function/pattern/Linear3DBlockPattern.java | 8 ++++++++ .../core/function/pattern/LinearBlockPattern.java | 6 ++++++ .../core/function/pattern/MaskedPattern.java | 5 +++++ .../core/function/pattern/NoXPattern.java | 5 +++++ .../core/function/pattern/NoYPattern.java | 5 +++++ .../core/function/pattern/NoZPattern.java | 6 ++++++ .../core/function/pattern/OffsetPattern.java | 5 +++++ .../function/pattern/RandomOffsetPattern.java | 5 +++++ .../core/function/pattern/RelativePattern.java | 7 +++++++ .../pattern/SolidRandomOffsetPattern.java | 5 +++++ .../pattern/SurfaceRandomOffsetPattern.java | 5 +++++ .../extent/buffer/ForgetfulExtentBuffer.java | 7 +++++++ .../pattern/ExtentBufferedCompositePattern.java | 10 ++++++++++ .../sk89q/worldedit/function/pattern/Pattern.java | 5 +++++ .../worldedit/function/pattern/RandomPattern.java | 15 +++++++++++++++ .../function/pattern/StateApplyingPattern.java | 5 ++++- 18 files changed, 117 insertions(+), 1 deletion(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/ExpressionPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/ExpressionPattern.java index a9c1ec962..638872ea8 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/ExpressionPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/ExpressionPattern.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.core.function.pattern; import com.sk89q.worldedit.function.pattern.AbstractPattern; +import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.expression.EvaluationException; import com.sk89q.worldedit.internal.expression.Expression; import com.sk89q.worldedit.internal.expression.ExpressionException; @@ -57,4 +58,9 @@ public class ExpressionPattern extends AbstractPattern { } } + @Override + public Pattern fork() { + return new ExpressionPattern(this.expression.clone()); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear2DBlockPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear2DBlockPattern.java index 523f22541..e99383d06 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear2DBlockPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear2DBlockPattern.java @@ -7,6 +7,8 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; +import java.util.Arrays; + import static java.lang.Math.floorDiv; /** @@ -52,4 +54,10 @@ public class Linear2DBlockPattern extends AbstractPattern { return patternsArray[index].apply(extent, get, set); } + @Override + public Pattern fork() { + final Pattern[] forked = Arrays.stream(this.patternsArray).map(Pattern::fork).toArray(Pattern[]::new); + return new Linear2DBlockPattern(forked, this.xScale, this.zScale); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear3DBlockPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear3DBlockPattern.java index ea2e37588..e4d3822cc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear3DBlockPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/Linear3DBlockPattern.java @@ -7,6 +7,8 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; +import java.util.Arrays; + import static java.lang.Math.floorDiv; /** @@ -56,4 +58,10 @@ public class Linear3DBlockPattern extends AbstractPattern { return patternsArray[index].apply(extent, get, set); } + @Override + public Pattern fork() { + final Pattern[] forked = Arrays.stream(this.patternsArray).map(Pattern::fork).toArray(Pattern[]::new); + return new Linear3DBlockPattern(forked, this.xScale, this.yScale, this.zScale); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java index 0b6ef1be9..c65918e89 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java @@ -47,4 +47,10 @@ public class LinearBlockPattern extends AbstractPattern implements ResettablePat index = 0; } + @Override + public Pattern fork() { + final Pattern[] forked = Arrays.stream(this.patternsArray).map(Pattern::fork).toArray(Pattern[]::new); + return new LinearBlockPattern(forked); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/MaskedPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/MaskedPattern.java index 5d840d771..a5210feba 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/MaskedPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/MaskedPattern.java @@ -43,4 +43,9 @@ public class MaskedPattern extends AbstractPattern { return secondary.apply(extent, get, set); } + @Override + public Pattern fork() { + return new MaskedPattern(this.mask.copy(), this.primary.fork(), this.secondary.fork()); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoXPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoXPattern.java index d9fefb1bc..b4e9aac37 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoXPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoXPattern.java @@ -36,4 +36,9 @@ public class NoXPattern extends AbstractPattern { return pattern.apply(extent, mutable, set); } + @Override + public Pattern fork() { + return new NoXPattern(this.pattern.fork()); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoYPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoYPattern.java index 05d26f496..2fefaedfc 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoYPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoYPattern.java @@ -36,4 +36,9 @@ public class NoYPattern extends AbstractPattern { return pattern.apply(extent, mutable, set); } + @Override + public Pattern fork() { + return new NoYPattern(this.pattern.fork()); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoZPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoZPattern.java index faebb59aa..8c100c5e3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoZPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/NoZPattern.java @@ -1,6 +1,7 @@ package com.fastasyncworldedit.core.function.pattern; import com.fastasyncworldedit.core.math.MutableBlockVector3; +import com.fastasyncworldedit.core.queue.Filter; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.pattern.AbstractPattern; @@ -36,4 +37,9 @@ public class NoZPattern extends AbstractPattern { return pattern.apply(extent, mutable, set); } + @Override + public Pattern fork() { + return new NoZPattern(this.pattern.fork()); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/OffsetPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/OffsetPattern.java index 894f9d06a..628b848e8 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/OffsetPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/OffsetPattern.java @@ -60,4 +60,9 @@ public class OffsetPattern extends AbstractPattern { return pattern.apply(extent, get, mutable); } + @Override + public Pattern fork() { + return new OffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RandomOffsetPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RandomOffsetPattern.java index 3eb5c3b77..c0afd02e3 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RandomOffsetPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RandomOffsetPattern.java @@ -72,4 +72,9 @@ public class RandomOffsetPattern extends AbstractPattern { return pattern.apply(extent, get, mutable); } + @Override + public Pattern fork() { + return new RandomOffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RelativePattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RelativePattern.java index cda875f04..541aaa494 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RelativePattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/RelativePattern.java @@ -63,4 +63,11 @@ public class RelativePattern extends AbstractPattern implements ResettablePatter origin = null; } + @Override + public Pattern fork() { + RelativePattern forked = new RelativePattern(this.pattern.fork(), this.minY, this.maxY); + forked.origin = this.origin; // maintain origin for forks + return forked; + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SolidRandomOffsetPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SolidRandomOffsetPattern.java index 12b64a497..54ecf6676 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SolidRandomOffsetPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SolidRandomOffsetPattern.java @@ -94,4 +94,9 @@ public class SolidRandomOffsetPattern extends AbstractPattern { return pattern.apply(extent, get, set); } + @Override + public Pattern fork() { + return new SolidRandomOffsetPattern(this.pattern.fork(), this.dx, this.dy, this.dz, this.minY, this.maxY); + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SurfaceRandomOffsetPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SurfaceRandomOffsetPattern.java index 73327f94a..34866fc54 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SurfaceRandomOffsetPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/SurfaceRandomOffsetPattern.java @@ -129,4 +129,9 @@ public class SurfaceRandomOffsetPattern extends AbstractPattern { return !block.getBlockType().getMaterial().isMovementBlocker(); } + @Override + public Pattern fork() { + return new SurfaceRandomOffsetPattern(this.pattern.fork(), this.moves, this.minY, this.maxY); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java index f569acd00..ca2bfd965 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/buffer/ForgetfulExtentBuffer.java @@ -235,4 +235,11 @@ public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pat }; } + //FAWE - stateful pattern + @Override + public Pattern fork() { + return new ForgetfulExtentBuffer(extent, mask.copy()); + } + //FAWE end + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/ExtentBufferedCompositePattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/ExtentBufferedCompositePattern.java index 49e7f5472..863cf1e25 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/ExtentBufferedCompositePattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/ExtentBufferedCompositePattern.java @@ -25,6 +25,8 @@ import com.sk89q.worldedit.extent.buffer.ExtentBuffer; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.world.block.BaseBlock; +import java.util.Arrays; + import static com.google.common.base.Preconditions.checkArgument; /** @@ -64,4 +66,12 @@ public class ExtentBufferedCompositePattern extends AbstractExtentPattern { return lastBlock; } + //FAWE - stateful pattern + @Override + public Pattern fork() { + final Pattern[] forkedPatterns = Arrays.stream(patterns).map(Pattern::fork).toArray(Pattern[]::new); + return new ExtentBufferedCompositePattern(getExtent(), forkedPatterns); + } + //FAWE end + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java index 801b07a65..ab284c0cf 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/Pattern.java @@ -59,6 +59,11 @@ public interface Pattern extends Filter { apply(block, block, block); } + @Override + default Pattern fork() { // covariant return type + return this; + } + //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 1d2df7223..c45629e5f 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 @@ -67,6 +67,13 @@ public class RandomPattern extends AbstractPattern { this.collection = RandomCollection.of(weights, random); this.patterns = parent.patterns; } + + private RandomPattern(SimpleRandom random, Map weights) { + this.random = random; + this.weights = weights; + this.collection = RandomCollection.of(weights, random); + this.patterns = new LinkedHashSet<>(weights.keySet()); + } //FAWE end /** @@ -107,6 +114,14 @@ public class RandomPattern extends AbstractPattern { public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException { return collection.next(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)); + return new RandomPattern(this.random, newWeights); + } + //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/StateApplyingPattern.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/StateApplyingPattern.java index 3d834f76b..b328e78ea 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/StateApplyingPattern.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/pattern/StateApplyingPattern.java @@ -29,13 +29,16 @@ import com.sk89q.worldedit.world.block.BlockType; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; import static com.sk89q.worldedit.blocks.Blocks.resolveProperties; public class StateApplyingPattern extends AbstractExtentPattern { private final Map states; - private final Map, Object>> cache = Maps.newHashMap(); + //FAWE - avoid race conditions + private final Map, Object>> cache = new ConcurrentHashMap<>(); + //FAWE end public StateApplyingPattern(Extent extent, Map statesToSet) { super(extent); From c1c3a5f7ed4d7223150e9fe23173ce8d75101796 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Wed, 27 Mar 2024 16:32:11 +0000 Subject: [PATCH 114/114] fix: git did not find conflict here, remove duplicate fork method --- .../core/function/pattern/LinearBlockPattern.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java index c65918e89..1490bf37d 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/function/pattern/LinearBlockPattern.java @@ -36,12 +36,6 @@ public class LinearBlockPattern extends AbstractPattern implements ResettablePat return patternsArray[index].apply(extent, get, set); } - @Override - public Filter fork() { - final Pattern[] forked = Arrays.stream(this.patternsArray).map(Pattern::fork).toArray(Pattern[]::new); - return new LinearBlockPattern(forked); - } - @Override public void reset() { index = 0;