diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 1c476dcde..237377471 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -37,6 +37,8 @@ dependencies { "api"(project(":worldedit-libs:bukkit")) "compile"(":worldedit-adapters:") "compile"("org.spigotmcv1_13_r2:spigotmcv1_13_r2:1_13_r2") + "compile"("org.spigotmcv1_14_r1:spigotmcv1_14_r1:1_14_r1") + "compile"("org.spigotmcv1_15_r1:spigotmcv1_15_r1:1_15_r1") "compile"("it.unimi.dsi:fastutil:8.2.1") "api"("com.destroystokyo.paper:paper-api:1.14.4-R0.1-SNAPSHOT") { exclude("junit", "junit") diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BlockMaterial_1_13.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BlockMaterial_1_13.java new file mode 100644 index 000000000..1a81c20fe --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BlockMaterial_1_13.java @@ -0,0 +1,153 @@ +package com.boydti.fawe.bukkit.adapter.mc1_13; + +import com.sk89q.util.ReflectionUtil; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import net.minecraft.server.v1_13_R2.Block; +import net.minecraft.server.v1_13_R2.EnumPistonReaction; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.ITileEntity; +import net.minecraft.server.v1_13_R2.Material; +import org.bukkit.craftbukkit.v1_13_R2.block.data.CraftBlockData; + +public class BlockMaterial_1_13 implements BlockMaterial { + private final Block block; + private final IBlockData defaultState; + private final Material material; + private final boolean isTranslucent; + private final CraftBlockData craftBlockData; + private final org.bukkit.Material craftMaterial; + + public BlockMaterial_1_13(Block block) { + this(block, block.getBlockData()); + } + + public BlockMaterial_1_13(Block block, IBlockData defaultState) { + this.block = block; + this.defaultState = defaultState; + this.material = defaultState.getMaterial(); + this.craftBlockData = CraftBlockData.fromData(defaultState); + this.craftMaterial = craftBlockData.getMaterial(); + this.isTranslucent = ReflectionUtil.getField(Block.class, block, "n"); + } + + public Block getBlock() { + return block; + } + + public IBlockData getState() { + return defaultState; + } + + public CraftBlockData getCraftBlockData() { + return craftBlockData; + } + + public Material getMaterial() { + return material; + } + + @Override + public boolean isAir() { + return defaultState.isAir(); + } + + @Override + public boolean isFullCube() { + return craftMaterial.isOccluding(); + } + + @Override + public boolean isOpaque() { + return material.f(); + } + + @Override + public boolean isPowerSource() { + return defaultState.isPowerSource(); + } + + @Override + public boolean isLiquid() { + return material.isLiquid(); + } + + @Override + public boolean isSolid() { + return material.isBuildable(); + } + + @Override + public float getHardness() { + return block.strength; + } + + @Override + public float getResistance() { + return block.getDurability(); + } + + @Override + public float getSlipperiness() { + return block.n(); + } + + @Override + public int getLightValue() { + return defaultState.e(); + } + + @Override + public int getLightOpacity() { + return isTranslucent() ? 15 : 0; + } + + @Override + public boolean isFragileWhenPushed() { + return material.getPushReaction() == EnumPistonReaction.DESTROY; + } + + @Override + public boolean isUnpushable() { + return material.getPushReaction() == EnumPistonReaction.BLOCK; + } + + @Override + public boolean isTicksRandomly() { + return block.isTicking(defaultState); + } + + @Override + public boolean isMovementBlocker() { + return material.isSolid(); + } + + @Override + public boolean isBurnable() { + return material.isBurnable(); + } + + @Override + public boolean isToolRequired() { + return !material.isAlwaysDestroyable(); + } + + @Override + public boolean isReplacedDuringPlacement() { + return material.isReplaceable(); + } + + @Override + public boolean isTranslucent() { + return isTranslucent; + } + + @Override + public boolean hasContainer() { + return block instanceof ITileEntity; + } + + @Override + public int getMapColor() { + return material.i().rgb; + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BukkitAdapter_1_13.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BukkitAdapter_1_13.java new file mode 100644 index 000000000..3b118179b --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BukkitAdapter_1_13.java @@ -0,0 +1,287 @@ +package com.boydti.fawe.bukkit.adapter.mc1_13; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.adapter.DelegateLock; +import com.boydti.fawe.bukkit.adapter.NMSAdapter; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.collection.BitArray4096; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import io.papermc.lib.PaperLib; +import net.jpountz.util.UnsafeUtils; +import net.minecraft.server.v1_13_R2.Block; +import net.minecraft.server.v1_13_R2.Chunk; +import net.minecraft.server.v1_13_R2.ChunkCoordIntPair; +import net.minecraft.server.v1_13_R2.ChunkSection; +import net.minecraft.server.v1_13_R2.DataBits; +import net.minecraft.server.v1_13_R2.DataPalette; +import net.minecraft.server.v1_13_R2.DataPaletteBlock; +import net.minecraft.server.v1_13_R2.DataPaletteLinear; +import net.minecraft.server.v1_13_R2.GameProfileSerializer; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.PlayerChunk; +import net.minecraft.server.v1_13_R2.PlayerChunkMap; +import org.bukkit.craftbukkit.v1_13_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.Lock; +import java.util.function.Function; + +public final class BukkitAdapter_1_13 extends NMSAdapter { + /* + NMS fields + */ + public final static Field fieldBits; + public final static Field fieldPalette; + public final static Field fieldSize; + + public final static Field fieldFluidCount; + public final static Field fieldTickingBlockCount; + public final static Field fieldNonEmptyBlockCount; + + private final static Field fieldDirtyCount; + private final static Field fieldDirtyBits; + + private static final int CHUNKSECTION_BASE; + private static final int CHUNKSECTION_SHIFT; + + private static final Field fieldLock; + + static { + try { + fieldSize = DataPaletteBlock.class.getDeclaredField("i"); + fieldSize.setAccessible(true); + fieldBits = DataPaletteBlock.class.getDeclaredField("a"); + fieldBits.setAccessible(true); + fieldPalette = DataPaletteBlock.class.getDeclaredField("h"); + fieldPalette.setAccessible(true); + + fieldFluidCount = ChunkSection.class.getDeclaredField("e"); + fieldFluidCount.setAccessible(true); + fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount"); + fieldNonEmptyBlockCount.setAccessible(true); + + fieldDirtyCount = PlayerChunk.class.getDeclaredField("dirtyCount"); + fieldDirtyCount.setAccessible(true); + fieldDirtyBits = PlayerChunk.class.getDeclaredField("h"); + fieldDirtyBits.setAccessible(true); + + { + Field tmp; + try { + tmp = DataPaletteBlock.class.getDeclaredField("writeLock"); + } catch (NoSuchFieldException paper) { + tmp = DataPaletteBlock.class.getDeclaredField("j"); + } + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + int modifiers = modifiersField.getInt(tmp); + int newModifiers = modifiers & (~Modifier.FINAL); + if (newModifiers != modifiers) modifiersField.setInt(tmp, newModifiers); + fieldLock = tmp; + fieldLock.setAccessible(true); + } + + Unsafe unsafe = UnsafeUtils.getUNSAFE(); + CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class); + int scale = unsafe.arrayIndexScale(ChunkSection[].class); + if ((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale); + } catch (RuntimeException e) { + throw e; + } catch (Throwable rethrow) { + rethrow.printStackTrace(); + throw new RuntimeException(rethrow); + } + } + + protected static boolean setSectionAtomic(ChunkSection[] sections, ChunkSection expected, ChunkSection value, int layer) { + long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE; + if (layer >= 0 && layer < sections.length) { + return UnsafeUtils.getUNSAFE().compareAndSwapObject(sections, offset, expected, value); + } + return false; + } + + protected static DelegateLock applyLock(ChunkSection section) { + try { + synchronized (section) { + DataPaletteBlock blocks = section.getBlocks(); + Lock currentLock = (Lock) fieldLock.get(blocks); + if (currentLock instanceof DelegateLock) { + return (DelegateLock) currentLock; + } + DelegateLock newLock = new DelegateLock(currentLock); + fieldLock.set(blocks, newLock); + return newLock; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public static Chunk ensureLoaded(net.minecraft.server.v1_13_R2.World nmsWorld, int X, int Z) { + Chunk nmsChunk = nmsWorld.getChunkIfLoaded(X, Z); + if (nmsChunk != null) { + return nmsChunk; + } + if (Fawe.isMainThread()) { + return nmsWorld.getChunkAt(X, Z); + } + if (PaperLib.isPaper()) { + CraftWorld craftWorld = nmsWorld.getWorld(); + CompletableFuture future = craftWorld.getChunkAtAsync(X, Z, true); + try { + CraftChunk chunk = (CraftChunk) future.get(); + return chunk.getHandle(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + // TODO optimize + return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z)); + } + + public static PlayerChunk getPlayerChunk(net.minecraft.server.v1_13_R2.WorldServer nmsWorld, final int cx, final int cz) { + PlayerChunkMap chunkMap = nmsWorld.getPlayerChunkMap(); + PlayerChunk playerChunk = chunkMap.getChunk(cx, cz); + if (playerChunk == null) { + return null; + } + return playerChunk; + } + + public static void sendChunk(net.minecraft.server.v1_13_R2.WorldServer nmsWorld, int X, int Z, int mask) { + PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z); + if (playerChunk == null) { + return; + } + if (playerChunk.e()) { + Chunk nmsChunk = playerChunk.chunk; + ChunkSection[] sections = nmsChunk.getSections(); + for (int layer = 0; layer < 16; layer++) { + if (sections[layer] == null && (mask & (1 << layer)) != 0) { + sections[layer] = new ChunkSection(layer << 4, nmsWorld.worldProvider.g()); + } + } + TaskManager.IMP.sync(() -> { + try { + int dirtyBits = fieldDirtyBits.getInt(playerChunk); + if (dirtyBits == 0) { + nmsWorld.getPlayerChunkMap().a(playerChunk); + } + if (mask == 0) { + dirtyBits = 65535; + } else { + dirtyBits |= mask; + } + + fieldDirtyBits.set(playerChunk, dirtyBits); + fieldDirtyCount.set(playerChunk, 64); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + }); + return; + } + return; + } + + /* + NMS conversion + */ + public static ChunkSection newChunkSection(final int layer, final char[] blocks, boolean light) { + return newChunkSection(layer, null, blocks, light); + } + + public static ChunkSection newChunkSection(final int layer, final Function get, char[] set, boolean light) { + if (set == null) { + return newChunkSection(layer, light); + } + final int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get(); + final int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get(); + final long[] blockStates = FaweCache.IMP.BLOCK_STATES.get(); + final int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get(); + try { + int[] num_palette_buffer = new int[1]; + int air; + if (get == null) { + air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set); + } else { + air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set); + } + int num_palette = num_palette_buffer[0]; + // BlockStates + int bitsPerEntry = MathMan.log2nlz(num_palette - 1); + if (Settings.IMP.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 + } + + final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6; + if (num_palette == 1) { + for (int i = 0; i < blockBitArrayEnd; i++) blockStates[i] = 0; + } else { + final BitArray4096 bitArray = new BitArray4096(blockStates, bitsPerEntry); + bitArray.fromRaw(blocksCopy); + } + + ChunkSection section = newChunkSection(layer, light); + // set palette & data bits + final DataPaletteBlock dataPaletteBlocks = section.getBlocks(); + // private DataPalette h; + // protected DataBits a; + final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd); + final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits); + final DataPalette palette; +// palette = new DataPaletteHash<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d, GameProfileSerializer::a); + palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d); + + // set palette + 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 IBlockData ibd = ((BlockMaterial_1_13) state.getMaterial()).getState(); + palette.a(ibd); + } + try { + fieldBits.set(dataPaletteBlocks, nmsBits); + fieldPalette.set(dataPaletteBlocks, palette); + fieldSize.set(dataPaletteBlocks, bitsPerEntry); + setCount(0, 4096 - air, section); + } catch (final IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + + return section; + } catch (final Throwable e){ + Arrays.fill(blockToPalette, Integer.MAX_VALUE); + throw e; + } + } + + private static ChunkSection newChunkSection(int layer, boolean light) { + return new ChunkSection(layer << 4, light); + } + + public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws NoSuchFieldException, IllegalAccessException { + fieldFluidCount.setShort(section, (short) 0); // TODO FIXME + fieldTickingBlockCount.setShort(section, (short) tickingBlockCount); + fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BukkitGetBlocks_1_13.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BukkitGetBlocks_1_13.java new file mode 100644 index 000000000..ff88bc681 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BukkitGetBlocks_1_13.java @@ -0,0 +1,657 @@ +package com.boydti.fawe.bukkit.adapter.mc1_13; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; +import com.boydti.fawe.bukkit.adapter.DelegateLock; +import com.boydti.fawe.object.collection.AdaptedMap; +import com.boydti.fawe.object.collection.BitArray4096; +import com.boydti.fawe.util.ReflectionUtils; +import com.google.common.base.Suppliers; +import com.google.common.collect.Iterables; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +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.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_13_R2; +import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockTypes; +import net.minecraft.server.v1_13_R2.BiomeBase; +import net.minecraft.server.v1_13_R2.BlockPosition; +import net.minecraft.server.v1_13_R2.Chunk; +import net.minecraft.server.v1_13_R2.ChunkSection; +import net.minecraft.server.v1_13_R2.DataBits; +import net.minecraft.server.v1_13_R2.DataPalette; +import net.minecraft.server.v1_13_R2.DataPaletteBlock; +import net.minecraft.server.v1_13_R2.DataPaletteHash; +import net.minecraft.server.v1_13_R2.DataPaletteLinear; +import net.minecraft.server.v1_13_R2.Entity; +import net.minecraft.server.v1_13_R2.EntityTypes; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.NBTTagCompound; +import net.minecraft.server.v1_13_R2.NBTTagInt; +import net.minecraft.server.v1_13_R2.TileEntity; +import net.minecraft.server.v1_13_R2.WorldServer; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_13_R2.block.CraftBlock; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +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.Future; +import java.util.function.Function; + +import static org.slf4j.LoggerFactory.getLogger; + +public class BukkitGetBlocks_1_13 extends CharGetBlocks { + public ChunkSection[] sections; + public Chunk nmsChunk; + public WorldServer world; + public int X, Z; + + public BukkitGetBlocks_1_13(World world, int X, int Z) { + this(((CraftWorld) world).getHandle(), X, Z); + } + + public BukkitGetBlocks_1_13(WorldServer world, int X, int Z) { + this.world = world; + this.X = X; + this.Z = Z; + } + + public int getX() { + return X; + } + + public int getZ() { + return Z; + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + BiomeBase base = getChunk().getBiomeIndex()[(z << 4) + x]; + return BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)); + } + + @Override + public CompoundTag getTag(int x, int y, int z) { + TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + (X << 4), y, (z & 15) + (Z << 4))); + if (tileEntity == null) { + return null; + } + return new LazyCompoundTag_1_13(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); + } + + private static final Function posNms2We = new Function() { + @Override + public BlockVector3 apply(BlockPosition v) { + return BlockVector3.at(v.getX(), v.getY(), v.getZ()); + } + }; + + private final static Function nmsTile2We = new Function() { + @Override + public CompoundTag apply(TileEntity tileEntity) { + return new LazyCompoundTag_1_13(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); + } + }; + + @Override + public Map getTiles() { + Map nmsTiles = getChunk().getTileEntities(); + if (nmsTiles.isEmpty()) { + return Collections.emptyMap(); + } + return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); + } + + @Override + public CompoundTag getEntity(UUID uuid) { + Entity entity = world.getEntity(uuid); + if (entity != null) { + org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); + return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); + } + for (List entry : getChunk().getEntitySlices()) { + if (entry != null) { + for (Entity ent : entry) { + if (uuid.equals(ent.getUniqueID())) { + org.bukkit.entity.Entity bukkitEnt = ent.getBukkitEntity(); + return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); + } + } + } + } + return null; + } + + @Override + public Set getEntities() { + List[] slices = getChunk().getEntitySlices(); + int size = 0; + for (List slice : slices) { + if (slice != null) size += slice.size(); + } + if (slices.length == 0) { + return Collections.emptySet(); + } + int finalSize = size; + return new AbstractSet() { + @Override + public int size() { + return finalSize; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof CompoundTag)) { + return false; + } + CompoundTag getTag = (CompoundTag) get; + Map value = getTag.getValue(); + CompoundTag getParts = (CompoundTag) value.get("UUID"); + UUID getUUID = new UUID(getParts.getLong("Most"), getParts.getLong("Least")); + for (List slice : slices) { + if (slice != null) { + for (Entity entity : slice) { + UUID uuid = entity.getUniqueID(); + if (uuid.equals(getUUID)) { + return true; + } + } + } + } + return false; + } + + @NotNull + @Override + public Iterator iterator() { + Iterable result = Iterables.transform(Iterables.concat(slices), new com.google.common.base.Function() { + @Nullable + @Override + public CompoundTag apply(@Nullable Entity input) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + NBTTagCompound tag = new NBTTagCompound(); + return (CompoundTag) adapter.toNative(input.save(tag)); + } + }); + return result.iterator(); + } + }; + } + + private void updateGet(BukkitGetBlocks_1_13 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { + synchronized (get) { + if (this.nmsChunk != nmsChunk) { + this.nmsChunk = nmsChunk; + this.sections = sections.clone(); + this.reset(); + } + if (this.sections == null) { + this.sections = sections.clone(); + } + if (this.sections[layer] != section) { + this.sections[layer] = section; + } + this.blocks[layer] = arr; + } + } + + private void removeEntity(Entity entity) { + entity.die(); + entity.valid = false; + } + + public Chunk ensureLoaded(net.minecraft.server.v1_13_R2.World nmsWorld, int X, int Z) { + return BukkitAdapter_1_13.ensureLoaded(nmsWorld, X, Z); + } + + @Override + public > T call(IChunkSet set, Runnable finalizer) { + try { + WorldServer nmsWorld = world; + boolean light = nmsWorld.worldProvider.g(); + Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); + + // Remove existing tiles + { + Map tiles = nmsChunk.getTileEntities(); + if (!tiles.isEmpty()) { + final Iterator> iterator = tiles.entrySet().iterator(); + while (iterator.hasNext()) { + final Map.Entry entry = iterator.next(); + final BlockPosition 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; + } + if (set.getBlock(lx, ly, lz).getOrdinal() != 0) { + TileEntity tile = entry.getValue(); + tile.y(); + tile.invalidateBlockCache(); + } + } + } + } + + int bitMask = 0; + synchronized (nmsChunk) { + ChunkSection[] sections = nmsChunk.getSections(); + + for (int layer = 0; layer < 16; layer++) { + if (!set.hasSection(layer)) continue; + + bitMask |= 1 << layer; + + char[] setArr = set.load(layer); + ChunkSection newSection; + ChunkSection existingSection = sections[layer]; + if (existingSection == null) { + newSection = BukkitAdapter_1_13.newChunkSection(layer, setArr, light); + if (BukkitAdapter_1_13.setSectionAtomic(sections, null, newSection, layer)) { + updateGet(this, nmsChunk, sections, newSection, setArr, layer); + continue; + } else { + existingSection = sections[layer]; + if (existingSection == null) { + System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + continue; + } + } + } + DelegateLock lock = BukkitAdapter_1_13.applyLock(existingSection); + synchronized (this) { + synchronized (lock) { + lock.untilFree(); + ChunkSection getSection; + if (this.nmsChunk != nmsChunk) { + this.nmsChunk = nmsChunk; + this.sections = null; + this.reset(); + } else { + getSection = this.getSections()[layer]; + if (getSection != existingSection) { + this.sections[layer] = existingSection; + this.reset(); + } else if (lock.isModified()) { + this.reset(layer); + } + } + newSection = BukkitAdapter_1_13.newChunkSection(layer, this::load, setArr, light); + if (!BukkitAdapter_1_13.setSectionAtomic(sections, existingSection, newSection, layer)) { + System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + continue; + } else { + updateGet(this, nmsChunk, sections, newSection, setArr, layer); + } + } + } + } + + // Biomes + BiomeType[] biomes = set.getBiomes(); + if (biomes != null) { + // set biomes + final BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex(); + for (int i = 0; i < biomes.length; i++) { + final BiomeType biome = biomes[i]; + if (biome != null) { + final Biome craftBiome = BukkitAdapter.adapt(biome); + currentBiomes[i] = CraftBlock.biomeToBiomeBase(craftBiome); + } + } + } + + Runnable[] syncTasks = null; + + int bx = X << 4; + int bz = Z << 4; + + Set entityRemoves = set.getEntityRemoves(); + if (entityRemoves != null && !entityRemoves.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[3]; + + syncTasks[2] = new Runnable() { + @Override + public void run() { + final List[] entities = nmsChunk.getEntitySlices(); + + for (int i = 0; i < entities.length; i++) { + final Collection ents = entities[i]; + if (!ents.isEmpty()) { + final Iterator iter = ents.iterator(); + while (iter.hasNext()) { + final Entity entity = iter.next(); + if (entityRemoves.contains(entity.getUniqueID())) { + iter.remove(); + removeEntity(entity); + } + } + } + } + } + }; + } + + Set entities = set.getEntities(); + if (entities != null && !entities.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[2]; + + syncTasks[1] = new Runnable() { + @Override + public void run() { + for (final CompoundTag nativeTag : entities) { + final Map entityTagMap = ReflectionUtils.getMap(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) { + getLogger(BukkitGetBlocks_1_13.class).debug("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(); + + EntityTypes type = EntityTypes.a(id); + if (type != null) { + Entity entity = type.a(nmsWorld); + if (entity != null) { + UUID uuid = entity.getUniqueID(); + entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); + entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); + if (nativeTag != null) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + final NBTTagCompound tag = (NBTTagCompound) adapter.fromNative(nativeTag); + for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + entity.f(tag); + } + entity.setLocation(x, y, z, yaw, pitch); + nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + } + } + } + } + }; + + } + + // set tiles + Map tiles = set.getTiles(); + if (tiles != null && !tiles.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[1]; + + syncTasks[0] = new Runnable() { + @Override + public void run() { + 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 BlockPosition pos = new BlockPosition(x, y, z); + + synchronized (nmsWorld) { + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity == null || tileEntity.x()) { + nmsWorld.n(pos); + tileEntity = nmsWorld.getTileEntity(pos); + } + if (tileEntity != null) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + final NBTTagCompound tag = (NBTTagCompound) adapter.fromNative(nativeTag); + tag.set("x", new NBTTagInt(x)); + tag.set("y", new NBTTagInt(y)); + tag.set("z", new NBTTagInt(z)); + tileEntity.load(tag); + } + } + } + } + }; + } + +// {//Lighting +// for (int layer = 0; layer < 16; layer++) { +// if (!set.hasSection(layer)) continue; +// //TODO lighting +// } +// } + + Runnable callback; + if (bitMask == 0 && biomes == null) { + callback = null; + } else { + int finalMask = bitMask; + callback = () -> { + // Set Modified + nmsChunk.f(true); // Set Modified + nmsChunk.mustSave = true; + nmsChunk.markDirty(); + // send to player + BukkitAdapter_1_13.sendChunk(nmsWorld, X, Z, finalMask); + if (finalizer != null) finalizer.run(); + }; + } + if (syncTasks != null) { + QueueHandler queueHandler = Fawe.get().getQueueHandler(); + Runnable[] finalSyncTasks = syncTasks; + + // Chain the sync tasks and the callback + Callable chain = new Callable() { + @Override + public Future call() { + try { + // Run the sync tasks + for (int i = 0; i < finalSyncTasks.length; i++) { + Runnable task = finalSyncTasks[i]; + 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; + } + } + }; + 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; + } + } + + @Override + public synchronized char[] update(int layer, char[] data) { + ChunkSection section = getSections()[layer]; + // Section is null, return empty array + if (section == null) { + return FaweCache.IMP.EMPTY_CHAR_4096; + } + if (data == null || data == FaweCache.IMP.EMPTY_CHAR_4096) { + data = new char[4096]; + } + DelegateLock lock = BukkitAdapter_1_13.applyLock(section); + synchronized (lock) { + lock.untilFree(); + lock.setModified(false); + // Efficiently convert ChunkSection to raw data + try { + FAWE_Spigot_v1_13_R2 adapter = ((FAWE_Spigot_v1_13_R2) WorldEditPlugin.getInstance().getBukkitImplAdapter()); + + final DataPaletteBlock blocks = section.getBlocks(); + final DataBits bits = (DataBits) BukkitAdapter_1_13.fieldBits.get(blocks); + final DataPalette palette = (DataPalette) BukkitAdapter_1_13.fieldPalette.get(blocks); + + final int bitsPerEntry = bits.c(); + final long[] blockStates = bits.a(); + + new BitArray4096(blockStates, bitsPerEntry).toRaw(data); + + int num_palette; + if (palette instanceof DataPaletteLinear) { + num_palette = ((DataPaletteLinear) palette).b(); + } else if (palette instanceof DataPaletteHash) { + num_palette = ((DataPaletteHash) palette).b(); + } else { + num_palette = 0; + int[] paletteToBlockInts = FaweCache.IMP.PALETTE_TO_BLOCK.get(); + char[] paletteToBlockChars = FaweCache.IMP.PALETTE_TO_BLOCK_CHAR.get(); + try { + for (int i = 0; i < 4096; i++) { + char paletteVal = data[i]; + char ordinal = paletteToBlockChars[paletteVal]; + if (ordinal == Character.MAX_VALUE) { + paletteToBlockInts[num_palette++] = paletteVal; + IBlockData ibd = palette.a(data[i]); + if (ibd == null) { + ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar(); + } else { + ordinal = adapter.adaptToChar(ibd); + } + paletteToBlockChars[paletteVal] = ordinal; + } + data[i] = ordinal; + } + } finally { + for (int i = 0; i < num_palette; i++) { + int paletteVal = paletteToBlockInts[i]; + paletteToBlockChars[paletteVal] = Character.MAX_VALUE; + } + } + return data; + } + + char[] paletteToOrdinal = FaweCache.IMP.PALETTE_TO_BLOCK_CHAR.get(); + try { + if (num_palette != 1) { + for (int i = 0; i < num_palette; i++) { + char ordinal = ordinal(palette.a(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.a(i), adapter); + paletteToOrdinal[i] = val; + } + data[i] = val; + } + } else { + char ordinal = ordinal(palette.a(0), adapter); + Arrays.fill(data, ordinal); + } + } finally { + for (int i = 0; i < num_palette; i++) { + paletteToOrdinal[i] = Character.MAX_VALUE; + } + } + return data; + } catch (IllegalAccessException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + } + + private final char ordinal(IBlockData ibd, FAWE_Spigot_v1_13_R2 adapter) { + if (ibd == null) { + return BlockTypes.AIR.getDefaultState().getOrdinalChar(); + } else { + return adapter.adaptToChar(ibd); + } + } + + public ChunkSection[] getSections() { + ChunkSection[] tmp = sections; + if (tmp == null) { + synchronized (this) { + tmp = sections; + if (tmp == null) { + Chunk chunk = getChunk(); + sections = tmp = chunk.getSections().clone(); + } + } + } + return tmp; + } + + public Chunk getChunk() { + Chunk tmp = nmsChunk; + if (tmp == null) { + synchronized (this) { + tmp = nmsChunk; + if (tmp == null) { + nmsChunk = tmp = ensureLoaded(this.world, X, Z); + } + } + } + return tmp; + } + + @Override + public boolean hasSection(int layer) { + return getSections()[layer] != null; + } + + @Override + public boolean trim(boolean aggressive) { + if (aggressive) { + sections = null; + nmsChunk = null; + } + return super.trim(aggressive); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/LazyCompoundTag_1_13.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/LazyCompoundTag_1_13.java new file mode 100644 index 000000000..257d3782b --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/LazyCompoundTag_1_13.java @@ -0,0 +1,152 @@ +package com.boydti.fawe.bukkit.adapter.mc1_13; + +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.WorldEditPlugin; +import net.minecraft.server.v1_13_R2.NBTBase; +import net.minecraft.server.v1_13_R2.NBTNumber; +import net.minecraft.server.v1_13_R2.NBTTagCompound; +import net.minecraft.server.v1_13_R2.NBTTagList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class LazyCompoundTag_1_13 extends CompoundTag { + private final Supplier nmsTag; + + public LazyCompoundTag_1_13(Supplier tag) { + super(null); + this.nmsTag = tag; + } + + public LazyCompoundTag_1_13(NBTTagCompound tag) { + this(() -> tag); + } + + public NBTTagCompound get() { + return nmsTag.get(); + } + + @Override + public Map getValue() { + Map value = super.getValue(); + if (value == null) { + Tag tag = WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(nmsTag.get()); + setValue(((CompoundTag) tag).getValue()); + } + return super.getValue(); + } + + public boolean containsKey(String key) { + return nmsTag.get().hasKey(key); + } + + public byte[] getByteArray(String key) { + return nmsTag.get().getByteArray(key); + } + + public byte getByte(String key) { + return nmsTag.get().getByte(key); + } + + public double getDouble(String key) { + return nmsTag.get().getDouble(key); + } + + public double asDouble(String key) { + NBTBase value = nmsTag.get().get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asDouble(); + } + return 0; + } + + public float getFloat(String key) { + return nmsTag.get().getFloat(key); + } + + public int[] getIntArray(String key) { + return nmsTag.get().getIntArray(key); + } + + public int getInt(String key) { + return nmsTag.get().getInt(key); + } + + public int asInt(String key) { + NBTBase value = nmsTag.get().get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asInt(); + } + return 0; + } + + public List getList(String key) { + NBTBase tag = nmsTag.get().get(key); + if (tag instanceof NBTTagList) { + ArrayList list = new ArrayList<>(); + NBTTagList nbtList = (NBTTagList) tag; + for (NBTBase elem : nbtList) { + if (elem instanceof NBTTagCompound) { + list.add(new LazyCompoundTag_1_13((NBTTagCompound) elem)); + } else { + list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem)); + } + } + return list; + } + return Collections.emptyList(); + } + + public ListTag getListTag(String key) { + NBTBase tag = nmsTag.get().get(key); + if (tag instanceof NBTTagList) { + 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 nmsTag.get().o(key); + } + + public long getLong(String key) { + return nmsTag.get().getLong(key); + } + + public long asLong(String key) { + NBTBase value = nmsTag.get().get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asLong(); + } + return 0; + } + + public short getShort(String key) { + return nmsTag.get().getShort(key); + } + + public String getString(String key) { + return nmsTag.get().getString(key); + } + + @Override + public String toString() { + return nmsTag.get().toString(); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/MapChunkUtil_1_13.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/MapChunkUtil_1_13.java new file mode 100644 index 000000000..b3cc9b6ec --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/MapChunkUtil_1_13.java @@ -0,0 +1,27 @@ +package com.boydti.fawe.bukkit.adapter.mc1_13; + +import com.boydti.fawe.bukkit.adapter.MapChunkUtil; +import net.minecraft.server.v1_13_R2.PacketPlayOutMapChunk; + +public class MapChunkUtil_1_13 extends MapChunkUtil { + public MapChunkUtil_1_13() throws NoSuchFieldException { + fieldX = PacketPlayOutMapChunk.class.getDeclaredField("a"); + fieldZ = PacketPlayOutMapChunk.class.getDeclaredField("b"); + fieldBitMask = PacketPlayOutMapChunk.class.getDeclaredField("c"); + fieldChunkData = PacketPlayOutMapChunk.class.getDeclaredField("d"); + fieldBlockEntities = PacketPlayOutMapChunk.class.getDeclaredField("e"); + fieldFull = PacketPlayOutMapChunk.class.getDeclaredField("f"); + + fieldX.setAccessible(true); + fieldZ.setAccessible(true); + fieldBitMask.setAccessible(true); + fieldChunkData.setAccessible(true); + fieldBlockEntities.setAccessible(true); + fieldFull.setAccessible(true); + } + + @Override + public PacketPlayOutMapChunk createPacket() { + return new PacketPlayOutMapChunk(); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BlockMaterial_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BlockMaterial_1_14.java new file mode 100644 index 000000000..9c881f012 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BlockMaterial_1_14.java @@ -0,0 +1,153 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14; + +import com.sk89q.util.ReflectionUtil; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import net.minecraft.server.v1_14_R1.Block; +import net.minecraft.server.v1_14_R1.EnumPistonReaction; +import net.minecraft.server.v1_14_R1.IBlockData; +import net.minecraft.server.v1_14_R1.ITileEntity; +import net.minecraft.server.v1_14_R1.Material; +import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData; + +public class BlockMaterial_1_14 implements BlockMaterial { + private final Block block; + private final IBlockData defaultState; + private final Material material; + private final boolean isTranslucent; + private final CraftBlockData craftBlockData; + private final org.bukkit.Material craftMaterial; + + public BlockMaterial_1_14(Block block) { + this(block, block.getBlockData()); + } + + public BlockMaterial_1_14(Block block, IBlockData defaultState) { + this.block = block; + this.defaultState = defaultState; + this.material = defaultState.getMaterial(); + this.craftBlockData = CraftBlockData.fromData(defaultState); + this.craftMaterial = craftBlockData.getMaterial(); + this.isTranslucent = ReflectionUtil.getField(Block.class, block, "v"); + } + + public Block getBlock() { + return block; + } + + public IBlockData getState() { + return defaultState; + } + + public CraftBlockData getCraftBlockData() { + return craftBlockData; + } + + public Material getMaterial() { + return material; + } + + @Override + public boolean isAir() { + return defaultState.isAir(); + } + + @Override + public boolean isFullCube() { + return craftMaterial.isOccluding(); + } + + @Override + public boolean isOpaque() { + return material.f(); + } + + @Override + public boolean isPowerSource() { + return defaultState.isPowerSource(); + } + + @Override + public boolean isLiquid() { + return material.isLiquid(); + } + + @Override + public boolean isSolid() { + return material.isBuildable(); + } + + @Override + public float getHardness() { + return block.strength; + } + + @Override + public float getResistance() { + return block.getDurability(); + } + + @Override + public float getSlipperiness() { + return block.m(); + } + + @Override + public int getLightValue() { + return defaultState.h(); + } + + @Override + public int getLightOpacity() { + return isTranslucent() ? 15 : 0; + } + + @Override + public boolean isFragileWhenPushed() { + return material.getPushReaction() == EnumPistonReaction.DESTROY; + } + + @Override + public boolean isUnpushable() { + return material.getPushReaction() == EnumPistonReaction.BLOCK; + } + + @Override + public boolean isTicksRandomly() { + return block.isTicking(defaultState); + } + + @Override + public boolean isMovementBlocker() { + return material.isSolid(); + } + + @Override + public boolean isBurnable() { + return material.isBurnable(); + } + + @Override + public boolean isToolRequired() { + return !material.isAlwaysDestroyable(); + } + + @Override + public boolean isReplacedDuringPlacement() { + return material.isReplaceable(); + } + + @Override + public boolean isTranslucent() { + return isTranslucent; + } + + @Override + public boolean hasContainer() { + return block instanceof ITileEntity; + } + + @Override + public int getMapColor() { + return material.i().rgb; + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java new file mode 100644 index 000000000..5a845f498 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitAdapter_1_14.java @@ -0,0 +1,280 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.adapter.NMSAdapter; +import com.boydti.fawe.bukkit.adapter.DelegateLock; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.object.collection.BitArray4096; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import io.papermc.lib.PaperLib; +import net.jpountz.util.UnsafeUtils; +import net.minecraft.server.v1_14_R1.Block; +import net.minecraft.server.v1_14_R1.Chunk; +import net.minecraft.server.v1_14_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_14_R1.ChunkSection; +import net.minecraft.server.v1_14_R1.DataBits; +import net.minecraft.server.v1_14_R1.DataPalette; +import net.minecraft.server.v1_14_R1.DataPaletteBlock; +import net.minecraft.server.v1_14_R1.DataPaletteLinear; +import net.minecraft.server.v1_14_R1.GameProfileSerializer; +import net.minecraft.server.v1_14_R1.IBlockData; +import net.minecraft.server.v1_14_R1.PlayerChunk; +import net.minecraft.server.v1_14_R1.PlayerChunkMap; +import org.bukkit.craftbukkit.v1_14_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.Lock; +import java.util.function.Function; + +public final class BukkitAdapter_1_14 extends NMSAdapter { + /* + NMS fields + */ + public final static Field fieldBits; + public final static Field fieldPalette; + public final static Field fieldSize; + + public final static Field fieldFluidCount; + public final static Field fieldTickingBlockCount; + public final static Field fieldNonEmptyBlockCount; + + private final static Field fieldDirtyCount; + private final static Field fieldDirtyBits; + + private static final int CHUNKSECTION_BASE; + private static final int CHUNKSECTION_SHIFT; + + private static final Field fieldLock; + + static { + try { + fieldSize = DataPaletteBlock.class.getDeclaredField("i"); + fieldSize.setAccessible(true); + fieldBits = DataPaletteBlock.class.getDeclaredField("a"); + fieldBits.setAccessible(true); + fieldPalette = DataPaletteBlock.class.getDeclaredField("h"); + fieldPalette.setAccessible(true); + + fieldFluidCount = ChunkSection.class.getDeclaredField("e"); + fieldFluidCount.setAccessible(true); + fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount"); + fieldNonEmptyBlockCount.setAccessible(true); + + fieldDirtyCount = PlayerChunk.class.getDeclaredField("dirtyCount"); + fieldDirtyCount.setAccessible(true); + fieldDirtyBits = PlayerChunk.class.getDeclaredField("r"); + fieldDirtyBits.setAccessible(true); + + { + Field tmp; + try { + tmp = DataPaletteBlock.class.getDeclaredField("writeLock"); + } catch (NoSuchFieldException paper) { + tmp = DataPaletteBlock.class.getDeclaredField("j"); + } + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + int modifiers = modifiersField.getInt(tmp); + int newModifiers = modifiers & (~Modifier.FINAL); + if (newModifiers != modifiers) modifiersField.setInt(tmp, newModifiers); + fieldLock = tmp; + fieldLock.setAccessible(true); + } + + Unsafe unsafe = UnsafeUtils.getUNSAFE(); + CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class); + int scale = unsafe.arrayIndexScale(ChunkSection[].class); + if ((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale); + } catch (RuntimeException e) { + throw e; + } catch (Throwable rethrow) { + rethrow.printStackTrace(); + throw new RuntimeException(rethrow); + } + } + + protected static boolean setSectionAtomic(ChunkSection[] sections, ChunkSection expected, ChunkSection value, int layer) { + long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE; + if (layer >= 0 && layer < sections.length) { + return UnsafeUtils.getUNSAFE().compareAndSwapObject(sections, offset, expected, value); + } + return false; + } + + protected static DelegateLock applyLock(ChunkSection section) { + try { + synchronized (section) { + DataPaletteBlock blocks = section.getBlocks(); + Lock currentLock = (Lock) fieldLock.get(blocks); + if (currentLock instanceof DelegateLock) { + return (DelegateLock) currentLock; + } + DelegateLock newLock = new DelegateLock(currentLock); + fieldLock.set(blocks, newLock); + return newLock; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public static Chunk ensureLoaded(net.minecraft.server.v1_14_R1.World nmsWorld, int X, int Z) { + Chunk nmsChunk = nmsWorld.getChunkIfLoaded(X, Z); + if (nmsChunk != null) { + return nmsChunk; + } + if (Fawe.isMainThread()) { + return nmsWorld.getChunkAt(X, Z); + } + if (PaperLib.isPaper()) { + CraftWorld craftWorld = nmsWorld.getWorld(); + CompletableFuture future = craftWorld.getChunkAtAsync(X, Z, true); + try { + CraftChunk chunk = (CraftChunk) future.get(); + return chunk.getHandle(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + // TODO optimize + return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z)); + } + + public static PlayerChunk getPlayerChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, final int cx, final int cz) { + PlayerChunkMap chunkMap = nmsWorld.getChunkProvider().playerChunkMap; + PlayerChunk playerChunk = chunkMap.visibleChunks.get(ChunkCoordIntPair.pair(cx, cz)); + if (playerChunk == null) { + return null; + } + return playerChunk; + } + + public static void sendChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, int X, int Z, int mask) { + PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z); + if (playerChunk == null) { + return; + } + if (playerChunk.hasBeenLoaded()) { + TaskManager.IMP.sync(() -> { + try { + int dirtyBits = fieldDirtyBits.getInt(playerChunk); + if (dirtyBits == 0) { + nmsWorld.getChunkProvider().playerChunkMap.a(playerChunk); + } + if (mask == 0) { + dirtyBits = 65535; + } else { + dirtyBits |= mask; + } + + fieldDirtyBits.set(playerChunk, dirtyBits); + fieldDirtyCount.set(playerChunk, 64); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + }); + return; + } + return; + } + + /* + NMS conversion + */ + public static ChunkSection newChunkSection(final int layer, final char[] blocks) { + return newChunkSection(layer, null, blocks); + } + + public static ChunkSection newChunkSection(final int layer, final Function get, char[] set) { + if (set == null) { + return newChunkSection(layer); + } + final int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get(); + final int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get(); + final long[] blockStates = FaweCache.IMP.BLOCK_STATES.get(); + final int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get(); + try { + int[] num_palette_buffer = new int[1]; + int air; + if (get == null) { + air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set); + } else { + air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set); + } + int num_palette = num_palette_buffer[0]; + // BlockStates + int bitsPerEntry = MathMan.log2nlz(num_palette - 1); + if (Settings.IMP.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 + } + + final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6; + if (num_palette == 1) { + for (int i = 0; i < blockBitArrayEnd; i++) blockStates[i] = 0; + } else { + final BitArray4096 bitArray = new BitArray4096(blockStates, bitsPerEntry); + bitArray.fromRaw(blocksCopy); + } + + ChunkSection section = newChunkSection(layer); + // set palette & data bits + final DataPaletteBlock dataPaletteBlocks = section.getBlocks(); + // private DataPalette h; + // protected DataBits a; + final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd); + final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits); + final DataPalette palette; +// palette = new DataPaletteHash<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d, GameProfileSerializer::a); + palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d); + + // set palette + 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 IBlockData ibd = ((BlockMaterial_1_14) state.getMaterial()).getState(); + palette.a(ibd); + } + try { + fieldBits.set(dataPaletteBlocks, nmsBits); + fieldPalette.set(dataPaletteBlocks, palette); + fieldSize.set(dataPaletteBlocks, bitsPerEntry); + setCount(0, 4096 - air, section); + } catch (final IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + + return section; + } catch (final Throwable e){ + Arrays.fill(blockToPalette, Integer.MAX_VALUE); + throw e; + } + } + + private static ChunkSection newChunkSection(int layer) { + return new ChunkSection(layer << 4); + } + + public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws NoSuchFieldException, IllegalAccessException { + fieldFluidCount.setShort(section, (short) 0); // TODO FIXME + fieldTickingBlockCount.setShort(section, (short) tickingBlockCount); + fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java new file mode 100644 index 000000000..ff1f58be2 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/BukkitGetBlocks_1_14.java @@ -0,0 +1,662 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +import com.bekvon.bukkit.residence.commands.set; +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkSet; +import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks; +import com.boydti.fawe.beta.implementation.queue.QueueHandler; +import com.boydti.fawe.bukkit.adapter.DelegateLock; +import com.boydti.fawe.bukkit.adapter.mc1_14.nbt.LazyCompoundTag_1_14; +import com.boydti.fawe.object.collection.AdaptedMap; +import com.boydti.fawe.object.collection.BitArray4096; +import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.util.TaskManager; +import com.google.common.base.Suppliers; +import com.google.common.collect.Iterables; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +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.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_14_R4; +import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockTypes; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +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.Future; +import java.util.function.Function; +import java.util.function.Supplier; +import javax.annotation.Nullable; +import net.minecraft.server.v1_14_R1.BiomeBase; +import net.minecraft.server.v1_14_R1.BlockPosition; +import net.minecraft.server.v1_14_R1.Chunk; +import net.minecraft.server.v1_14_R1.ChunkSection; +import net.minecraft.server.v1_14_R1.ChunkStatus; +import net.minecraft.server.v1_14_R1.DataBits; +import net.minecraft.server.v1_14_R1.DataPalette; +import net.minecraft.server.v1_14_R1.DataPaletteBlock; +import net.minecraft.server.v1_14_R1.DataPaletteHash; +import net.minecraft.server.v1_14_R1.DataPaletteLinear; +import net.minecraft.server.v1_14_R1.Entity; +import net.minecraft.server.v1_14_R1.EntityTypes; +import net.minecraft.server.v1_14_R1.IBlockData; +import net.minecraft.server.v1_14_R1.LightEngine; +import net.minecraft.server.v1_14_R1.LightEngineThreaded; +import net.minecraft.server.v1_14_R1.NBTTagCompound; +import net.minecraft.server.v1_14_R1.NBTTagInt; +import net.minecraft.server.v1_14_R1.TileEntity; +import net.minecraft.server.v1_14_R1.WorldServer; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_14_R1.block.CraftBlock; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.jetbrains.annotations.NotNull; + +public class BukkitGetBlocks_1_14 extends CharGetBlocks { + public ChunkSection[] sections; + public Chunk nmsChunk; + public WorldServer world; + public int X, Z; + + public BukkitGetBlocks_1_14(World world, int X, int Z) { + this(((CraftWorld) world).getHandle(), X, Z); + } + + public BukkitGetBlocks_1_14(WorldServer world, int X, int Z) { + this.world = world; + this.X = X; + this.Z = Z; + } + + public int getX() { + return X; + } + + public int getZ() { + return Z; + } + + @Override + public BiomeType getBiomeType(int x, int y, int z) { + BiomeBase base = getChunk().getBiomeIndex()[(z << 4) + x]; + return BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)); + } + + @Override + public CompoundTag getTag(int x, int y, int z) { + TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + (X << 4), y, (z & 15) + (Z << 4))); + if (tileEntity == null) { + return null; + } + return new LazyCompoundTag_1_14(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); + } + + private static final Function posNms2We = new Function() { + @Override + public BlockVector3 apply(BlockPosition v) { + return BlockVector3.at(v.getX(), v.getY(), v.getZ()); + } + }; + + private final static Function nmsTile2We = new Function() { + @Override + public CompoundTag apply(TileEntity tileEntity) { + return new LazyCompoundTag_1_14(Suppliers.memoize(() -> tileEntity.save(new NBTTagCompound()))); + } + }; + + @Override + public Map getTiles() { + Map nmsTiles = getChunk().getTileEntities(); + if (nmsTiles.isEmpty()) { + return Collections.emptyMap(); + } + return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We); + } + + @Override + public CompoundTag getEntity(UUID uuid) { + Entity entity = world.getEntity(uuid); + if (entity != null) { + org.bukkit.entity.Entity bukkitEnt = entity.getBukkitEntity(); + return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); + } + for (List entry : getChunk().getEntitySlices()) { + if (entry != null) { + for (Entity ent : entry) { + if (uuid.equals(ent.getUniqueID())) { + org.bukkit.entity.Entity bukkitEnt = ent.getBukkitEntity(); + return BukkitAdapter.adapt(bukkitEnt).getState().getNbtData(); + } + } + } + } + return null; + } + + @Override + public Set getEntities() { + List[] slices = getChunk().getEntitySlices(); + int size = 0; + for (List slice : slices) { + if (slice != null) size += slice.size(); + } + if (slices.length == 0) { + return Collections.emptySet(); + } + int finalSize = size; + return new AbstractSet() { + @Override + public int size() { + return finalSize; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean contains(Object get) { + if (!(get instanceof CompoundTag)) { + return false; + } + CompoundTag getTag = (CompoundTag) get; + Map value = getTag.getValue(); + CompoundTag getParts = (CompoundTag) value.get("UUID"); + UUID getUUID = new UUID(getParts.getLong("Most"), getParts.getLong("Least")); + for (List slice : slices) { + if (slice != null) { + for (Entity entity : slice) { + UUID uuid = entity.getUniqueID(); + if (uuid.equals(getUUID)) { + return true; + } + } + } + } + return false; + } + + @NotNull + @Override + public Iterator iterator() { + Iterable result = Iterables.transform(Iterables.concat(slices), new com.google.common.base.Function() { + @Nullable + @Override + public CompoundTag apply(@Nullable Entity input) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + NBTTagCompound tag = new NBTTagCompound(); + return (CompoundTag) adapter.toNative(input.save(tag)); + } + }); + return result.iterator(); + } + }; + } + + private void updateGet(BukkitGetBlocks_1_14 get, Chunk nmsChunk, ChunkSection[] sections, ChunkSection section, char[] arr, int layer) { + synchronized (get) { + if (this.nmsChunk != nmsChunk) { + this.nmsChunk = nmsChunk; + this.sections = sections.clone(); + this.reset(); + } + if (this.sections == null) { + this.sections = sections.clone(); + } + if (this.sections[layer] != section) { + this.sections[layer] = section; + } + this.blocks[layer] = arr; + } + } + + private void removeEntity(Entity entity) { + entity.die(); + entity.valid = false; + } + + public Chunk ensureLoaded(net.minecraft.server.v1_14_R1.World nmsWorld, int X, int Z) { + return BukkitAdapter_1_14.ensureLoaded(nmsWorld, X, Z); + } + + @Override + public > T call(IChunkSet set, Runnable finalizer) { + try { + WorldServer nmsWorld = world; + Chunk nmsChunk = ensureLoaded(nmsWorld, X, Z); + + // Remove existing tiles + { + Map tiles = nmsChunk.getTileEntities(); + if (!tiles.isEmpty()) { + final Iterator> iterator = tiles.entrySet().iterator(); + while (iterator.hasNext()) { + final Map.Entry entry = iterator.next(); + final BlockPosition 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; + } + if (set.getBlock(lx, ly, lz).getOrdinal() != 0) { + TileEntity tile = entry.getValue(); + tile.n(); + tile.invalidateBlockCache(); + } + } + } + } + + int bitMask = 0; + synchronized (nmsChunk) { + ChunkSection[] sections = nmsChunk.getSections(); + + for (int layer = 0; layer < 16; layer++) { + if (!set.hasSection(layer)) continue; + + bitMask |= 1 << layer; + + char[] setArr = set.load(layer); + ChunkSection newSection; + ChunkSection existingSection = sections[layer]; + if (existingSection == null) { + newSection = BukkitAdapter_1_14.newChunkSection(layer, setArr); + if (BukkitAdapter_1_14.setSectionAtomic(sections, null, newSection, layer)) { + updateGet(this, nmsChunk, sections, newSection, setArr, layer); + continue; + } else { + existingSection = sections[layer]; + if (existingSection == null) { + System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer); + continue; + } + } + } + DelegateLock lock = BukkitAdapter_1_14.applyLock(existingSection); + synchronized (this) { + synchronized (lock) { + lock.untilFree(); + ChunkSection getSection; + if (this.nmsChunk != nmsChunk) { + this.nmsChunk = nmsChunk; + this.sections = null; + this.reset(); + } else { + getSection = this.getSections()[layer]; + if (getSection != existingSection) { + this.sections[layer] = existingSection; + this.reset(); + } else if (lock.isModified()) { + this.reset(layer); + } + } + newSection = BukkitAdapter_1_14.newChunkSection(layer, this::load, setArr); + if (!BukkitAdapter_1_14.setSectionAtomic(sections, existingSection, newSection, layer)) { + System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer); + continue; + } else { + updateGet(this, nmsChunk, sections, newSection, setArr, layer); + } + } + } + } + + // Biomes + BiomeType[] biomes = set.getBiomes(); + if (biomes != null) { + // set biomes + final BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex(); + for (int i = 0; i < biomes.length; i++) { + final BiomeType biome = biomes[i]; + if (biome != null) { + final Biome craftBiome = BukkitAdapter.adapt(biome); + currentBiomes[i] = CraftBlock.biomeToBiomeBase(craftBiome); + } + } + } + + Runnable[] syncTasks = null; + + int bx = X << 4; + int bz = Z << 4; + + Set entityRemoves = set.getEntityRemoves(); + if (entityRemoves != null && !entityRemoves.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[3]; + + syncTasks[2] = new Runnable() { + @Override + public void run() { + final List[] entities = nmsChunk.getEntitySlices(); + + for (int i = 0; i < entities.length; i++) { + final Collection ents = entities[i]; + if (!ents.isEmpty()) { + final Iterator iter = ents.iterator(); + while (iter.hasNext()) { + final Entity entity = iter.next(); + if (entityRemoves.contains(entity.getUniqueID())) { + iter.remove(); + removeEntity(entity); + } + } + } + } + } + }; + } + + Set entities = set.getEntities(); + if (entities != null && !entities.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[2]; + + syncTasks[1] = new Runnable() { + @Override + public void run() { + for (final CompoundTag nativeTag : entities) { + final Map entityTagMap = ReflectionUtils.getMap(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) { + getLogger(BukkitGetBlocks_1_14.class).debug("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(); + + EntityTypes type = EntityTypes.a(id).orElse(null); + if (type != null) { + Entity entity = type.a(nmsWorld); + if (entity != null) { + UUID uuid = entity.getUniqueID(); + entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); + entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); + if (nativeTag != null) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + final NBTTagCompound tag = (NBTTagCompound) adapter.fromNative(nativeTag); + for (final String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + entity.f(tag); + } + entity.setLocation(x, y, z, yaw, pitch); + nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + } + } + } + } + }; + + } + + // set tiles + Map tiles = set.getTiles(); + if (tiles != null && !tiles.isEmpty()) { + if (syncTasks == null) syncTasks = new Runnable[1]; + + syncTasks[0] = new Runnable() { + @Override + public void run() { + 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 BlockPosition pos = new BlockPosition(x, y, z); + + synchronized (nmsWorld) { + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity == null || tileEntity.isRemoved()) { + nmsWorld.removeTileEntity(pos); + tileEntity = nmsWorld.getTileEntity(pos); + } + if (tileEntity != null) { + BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); + final NBTTagCompound tag = (NBTTagCompound) adapter.fromNative(nativeTag); + tag.set("x", new NBTTagInt(x)); + tag.set("y", new NBTTagInt(y)); + tag.set("z", new NBTTagInt(z)); + tileEntity.load(tag); + } + } + } + } + }; + } + + {//Lighting + // TODO optimize, cause this is really slow + LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e(); + engine.a(nmsChunk, false); + } + + Runnable callback; + if (bitMask == 0 && biomes == null) { + callback = null; + } else { + int finalMask = bitMask; + callback = () -> { + // Set Modified + nmsChunk.d(true); // Set Modified + nmsChunk.mustNotSave = false; + nmsChunk.markDirty(); + // send to player + BukkitAdapter_1_14.sendChunk(nmsWorld, X, Z, finalMask); + if (finalizer != null) finalizer.run(); + }; + } + if (syncTasks != null) { + QueueHandler queueHandler = Fawe.get().getQueueHandler(); + Runnable[] finalSyncTasks = syncTasks; + + // Chain the sync tasks and the callback + Callable chain = new Callable() { + @Override + public Future call() { + try { + // Run the sync tasks + for (int i = 0; i < finalSyncTasks.length; i++) { + Runnable task = finalSyncTasks[i]; + 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; + } + } + }; + 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; + } + } + + @Override + public synchronized char[] update(int layer, char[] data) { + ChunkSection section = getSections()[layer]; + // Section is null, return empty array + if (section == null) { + return FaweCache.IMP.EMPTY_CHAR_4096; + } + if (data == null || data == FaweCache.IMP.EMPTY_CHAR_4096) { + data = new char[4096]; + } + DelegateLock lock = BukkitAdapter_1_14.applyLock(section); + synchronized (lock) { + lock.untilFree(); + lock.setModified(false); + // Efficiently convert ChunkSection to raw data + try { + FAWE_Spigot_v1_14_R4 adapter = ((FAWE_Spigot_v1_14_R4) WorldEditPlugin.getInstance().getBukkitImplAdapter()); + + final DataPaletteBlock blocks = section.getBlocks(); + final DataBits bits = (DataBits) BukkitAdapter_1_14.fieldBits.get(blocks); + final DataPalette palette = (DataPalette) BukkitAdapter_1_14.fieldPalette.get(blocks); + + final int bitsPerEntry = bits.c(); + final long[] blockStates = bits.a(); + + new BitArray4096(blockStates, bitsPerEntry).toRaw(data); + + int num_palette; + if (palette instanceof DataPaletteLinear) { + num_palette = ((DataPaletteLinear) palette).b(); + } else if (palette instanceof DataPaletteHash) { + num_palette = ((DataPaletteHash) palette).b(); + } else { + num_palette = 0; + int[] paletteToBlockInts = FaweCache.IMP.PALETTE_TO_BLOCK.get(); + char[] paletteToBlockChars = FaweCache.IMP.PALETTE_TO_BLOCK_CHAR.get(); + try { + for (int i = 0; i < 4096; i++) { + char paletteVal = data[i]; + char ordinal = paletteToBlockChars[paletteVal]; + if (ordinal == Character.MAX_VALUE) { + paletteToBlockInts[num_palette++] = paletteVal; + IBlockData ibd = palette.a(data[i]); + if (ibd == null) { + ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar(); + } else { + ordinal = adapter.adaptToChar(ibd); + } + paletteToBlockChars[paletteVal] = ordinal; + } + data[i] = ordinal; + } + } finally { + for (int i = 0; i < num_palette; i++) { + int paletteVal = paletteToBlockInts[i]; + paletteToBlockChars[paletteVal] = Character.MAX_VALUE; + } + } + return data; + } + + char[] paletteToOrdinal = FaweCache.IMP.PALETTE_TO_BLOCK_CHAR.get(); + try { + if (num_palette != 1) { + for (int i = 0; i < num_palette; i++) { + char ordinal = ordinal(palette.a(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.a(i), adapter); + paletteToOrdinal[i] = val; + } + data[i] = val; + } + } else { + char ordinal = ordinal(palette.a(0), adapter); + Arrays.fill(data, ordinal); + } + } finally { + for (int i = 0; i < num_palette; i++) { + paletteToOrdinal[i] = Character.MAX_VALUE; + } + } + return data; + } catch (IllegalAccessException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + } + + private final char ordinal(IBlockData ibd, FAWE_Spigot_v1_14_R4 adapter) { + if (ibd == null) { + return BlockTypes.AIR.getDefaultState().getOrdinalChar(); + } else { + return adapter.adaptToChar(ibd); + } + } + + public ChunkSection[] getSections() { + ChunkSection[] tmp = sections; + if (tmp == null) { + synchronized (this) { + tmp = sections; + if (tmp == null) { + Chunk chunk = getChunk(); + sections = tmp = chunk.getSections().clone(); + } + } + } + return tmp; + } + + public Chunk getChunk() { + Chunk tmp = nmsChunk; + if (tmp == null) { + synchronized (this) { + tmp = nmsChunk; + if (tmp == null) { + nmsChunk = tmp = ensureLoaded(this.world, X, Z); + } + } + } + return tmp; + } + + @Override + public boolean hasSection(int layer) { + return getSections()[layer] != null; + } + + @Override + public boolean trim(boolean aggressive) { + if (aggressive) { + sections = null; + nmsChunk = null; + } + return super.trim(aggressive); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/MapChunkUtil_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/MapChunkUtil_1_14.java new file mode 100644 index 000000000..bfdde3f3a --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/MapChunkUtil_1_14.java @@ -0,0 +1,28 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14; + +import com.boydti.fawe.bukkit.adapter.MapChunkUtil; +import net.minecraft.server.v1_14_R1.PacketPlayOutMapChunk; + +public class MapChunkUtil_1_14 extends MapChunkUtil { + public MapChunkUtil_1_14() throws NoSuchFieldException { + fieldX = PacketPlayOutMapChunk.class.getDeclaredField("a"); + fieldZ = PacketPlayOutMapChunk.class.getDeclaredField("b"); + fieldBitMask = PacketPlayOutMapChunk.class.getDeclaredField("c"); + fieldHeightMap = PacketPlayOutMapChunk.class.getDeclaredField("d"); + fieldChunkData = PacketPlayOutMapChunk.class.getDeclaredField("e"); + fieldBlockEntities = PacketPlayOutMapChunk.class.getDeclaredField("f"); + fieldFull = PacketPlayOutMapChunk.class.getDeclaredField("g"); + fieldX.setAccessible(true); + fieldZ.setAccessible(true); + fieldBitMask.setAccessible(true); + fieldHeightMap.setAccessible(true); + fieldChunkData.setAccessible(true); + fieldBlockEntities.setAccessible(true); + fieldFull.setAccessible(true); + } + + @Override + public PacketPlayOutMapChunk createPacket() { + return new PacketPlayOutMapChunk(); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/nbt/LazyCompoundTag_1_14.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/nbt/LazyCompoundTag_1_14.java new file mode 100644 index 000000000..6c26166f3 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_14/nbt/LazyCompoundTag_1_14.java @@ -0,0 +1,153 @@ +package com.boydti.fawe.bukkit.adapter.mc1_14.nbt; + +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.WorldEditPlugin; +import net.minecraft.server.v1_14_R1.NBTBase; +import net.minecraft.server.v1_14_R1.NBTNumber; +import net.minecraft.server.v1_14_R1.NBTTagCompound; +import net.minecraft.server.v1_14_R1.NBTTagList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class LazyCompoundTag_1_14 extends CompoundTag { + private final Supplier nmsTag; + + public LazyCompoundTag_1_14(Supplier tag) { + super(null); + this.nmsTag = tag; + } + + public LazyCompoundTag_1_14(NBTTagCompound tag) { + this(() -> tag); + } + + public NBTTagCompound get() { + return nmsTag.get(); + } + + @Override + public Map getValue() { + Map value = super.getValue(); + if (value == null) { + Tag tag = WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(nmsTag.get()); + setValue(((CompoundTag) tag).getValue()); + } + return super.getValue(); + } + + public boolean containsKey(String key) { + return nmsTag.get().hasKey(key); + } + + public byte[] getByteArray(String key) { + return nmsTag.get().getByteArray(key); + } + + public byte getByte(String key) { + return nmsTag.get().getByte(key); + } + + public double getDouble(String key) { + return nmsTag.get().getDouble(key); + } + + public double asDouble(String key) { + NBTBase value = nmsTag.get().get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asDouble(); + } + return 0; + } + + public float getFloat(String key) { + return nmsTag.get().getFloat(key); + } + + public int[] getIntArray(String key) { + return nmsTag.get().getIntArray(key); + } + + public int getInt(String key) { + return nmsTag.get().getInt(key); + } + + public int asInt(String key) { + NBTBase value = nmsTag.get().get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asInt(); + } + return 0; + } + + public List getList(String key) { + NBTBase tag = nmsTag.get().get(key); + if (tag instanceof NBTTagList) { + ArrayList list = new ArrayList<>(); + NBTTagList nbtList = (NBTTagList) tag; + for (NBTBase elem : nbtList) { + if (elem instanceof NBTTagCompound) { + list.add(new LazyCompoundTag_1_14((NBTTagCompound) elem)); + } else { + list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem)); + } + } + return list; + } + return Collections.emptyList(); + } + + public ListTag getListTag(String key) { + NBTBase tag = nmsTag.get().get(key); + if (tag instanceof NBTTagList) { + 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 nmsTag.get().getLongArray(key); + } + + public long getLong(String key) { + return nmsTag.get().getLong(key); + } + + public long asLong(String key) { + NBTBase value = nmsTag.get().get(key); + if (value instanceof NBTNumber) { + return ((NBTNumber) value).asLong(); + } + return 0; + } + + public short getShort(String key) { + return nmsTag.get().getShort(key); + } + + public String getString(String key) { + return nmsTag.get().getString(key); + } + + @Override + public String toString() { + return nmsTag.get().toString(); + } +} diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java index d53b5cfed..316de949d 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_15/BukkitGetBlocks_1_15.java @@ -65,9 +65,18 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { } @Override - public BiomeType getBiomeType(int x, int z) { - BiomeBase base = getChunk().getBiomeIndex()[(z << 4) + x]; - return BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)); + public BiomeType getBiomeType(int x, int y, int z) { + BiomeStorage index = getChunk().getBiomeIndex(); + BiomeBase base = null; + if (y == -1) { + for (y = 0; y < FaweCache.IMP.WORLD_HEIGHT; y++) { + base = index.getBiome(x, y, z); + if (base != null) break; + } + } else { + base = index.getBiome(x, y, z); + } + return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null; } @Override @@ -280,12 +289,17 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { BiomeType[] biomes = set.getBiomes(); if (biomes != null) { // set biomes - final BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex(); - for (int i = 0; i < biomes.length; i++) { - final BiomeType biome = biomes[i]; - if (biome != null) { - final Biome craftBiome = BukkitAdapter.adapt(biome); - currentBiomes[i] = CraftBlock.biomeToBiomeBase(craftBiome); + BiomeStorage currentBiomes = nmsChunk.getBiomeIndex(); + for (int z = 0, i = 0; z < 16; z++) { + for (int x = 0; x < 16; x++, i++) { + final BiomeType biome = biomes[i]; + if (biome != null) { + final Biome craftBiome = BukkitAdapter.adapt(biome); + BiomeBase nmsBiome = CraftBlock.biomeToBiomeBase(craftBiome); + for (int y = 0; y < FaweCache.IMP.WORLD_HEIGHT; y++) { + currentBiomes.setBiome(x, y, z, nmsBiome); + } + } } } } @@ -445,7 +459,7 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks { throw e; } }; - return (T) queueHandler.sync(chain); + return (T) (Future) queueHandler.sync(chain); } else { if (callback == null) { if (finalizer != null) finalizer.run(); diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java index 25806cda6..48dc1500d 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncBlock.java @@ -246,7 +246,7 @@ public class AsyncBlock implements Block { @NotNull @Override public Biome getBiome() { - return world.getAdapter().adapt(world.getBiomeType(x, z)); + return world.getAdapter().adapt(world.getBiomeType(x, y, z)); } @Override diff --git a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java index 5cea8e76d..8a880d103 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/wrapper/AsyncWorld.java @@ -827,7 +827,7 @@ public class AsyncWorld extends PassthroughExtent implements World { @Override public Biome getBiome(int x, int z) { - return adapter.adapt(getExtent().getBiome(BlockVector2.at(x, z))); + return adapter.adapt(getExtent().getBiomeType(x, 0, z)); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java index 6e3066c1b..b94b9eb6a 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/CombinedBlocks.java @@ -115,10 +115,10 @@ public class CombinedBlocks implements IBlocks { } @Override - public BiomeType getBiomeType(int x, int z) { - BiomeType biome = primary.getBiomeType(x, z); + public BiomeType getBiomeType(int x, int y, int z) { + BiomeType biome = primary.getBiomeType(x, y, z); if (biome == null) { - return secondary.getBiomeType(x, z); + return secondary.getBiomeType(x, y, z); } return biome; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java index 2aa9bd34a..7d3fc3f44 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IBlocks.java @@ -33,7 +33,7 @@ public interface IBlocks extends Trimable { Set getEntities(); - BiomeType getBiomeType(int x, int z); + BiomeType getBiomeType(int x, int y, int z); default int getBitMask() { int mask = 0; @@ -126,7 +126,7 @@ public interface IBlocks extends Trimable { if (full) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { - BiomeType biome = getBiomeType(x, z); + BiomeType biome = getBiomeType(x, 0, z); if (biome != null) { sectionWriter.writeInt(biome.getLegacyId()); } else { diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java index 97451fdda..6b33dd47b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunk.java @@ -81,7 +81,7 @@ public interface IChunk extends Trimable, IChunkGet, IChunkSet { boolean setBlock(int x, int y, int z, BlockStateHolder block); @Override - BiomeType getBiomeType(int x, int z); + BiomeType getBiomeType(int x, int y, int z); @Override BlockState getBlock(int x, int y, int z); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java index 073f660c2..24e4158a9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkGet.java @@ -21,7 +21,7 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput { BaseBlock getFullBlock(int x, int y, int z); @Override - BiomeType getBiomeType(int x, int z); + BiomeType getBiomeType(int x, int y, int z); @Override BlockState getBlock(int x, int y, int z); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java index f063565b8..f5993b2ce 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IChunkSet.java @@ -43,7 +43,7 @@ public interface IChunkSet extends IBlocks, OutputExtent { } @Override - BiomeType getBiomeType(int x, int z); + BiomeType getBiomeType(int x, int y, int z); @Override Map getTiles(); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java index 02b431a15..c646915bc 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateChunk.java @@ -70,8 +70,8 @@ public interface IDelegateChunk extends IQueueChunk { } @Override - default BiomeType getBiomeType(int x, int z) { - return getParent().getBiomeType(x, z); + default BiomeType getBiomeType(int x, int y, int z) { + return getParent().getBiomeType(x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java index a784baf31..c3ba8bbb9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/IDelegateQueueExtent.java @@ -104,11 +104,6 @@ public interface IDelegateQueueExtent extends IQueueExten return getParent().getFullBlock(x, y, z); } - @Override - default BiomeType getBiome(int x, int z) { - return getParent().getBiome(x, z); - } - @Override default BlockVector3 getMinimumPoint() { return getParent().getMinimumPoint(); @@ -358,8 +353,8 @@ public interface IDelegateQueueExtent extends IQueueExten } @Override - default BiomeType getBiomeType(int x, int z) { - return getParent().getBiomeType(x, z); + default BiomeType getBiomeType(int x, int y, int z) { + return getParent().getBiomeType(x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkExtent.java index f26c0f37b..865fd6bdb 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/IChunkExtent.java @@ -13,8 +13,8 @@ public interface IChunkExtent extends Extent { /** * Get the IChunk at a position (and cache it if it's not already) * - * @param x - * @param z + * @param chunkX + * @param chunkZ * @return IChunk */ T getOrCreateChunk(int chunkX, int chunkZ); @@ -49,8 +49,9 @@ public interface IChunkExtent extends Extent { return chunk.getFullBlock(x & 15, y, z & 15); } - default BiomeType getBiome(int x, int z) { + @Override + default BiomeType getBiomeType(int x, int y, int z) { final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4); - return chunk.getBiomeType(x & 15, z & 15); + return chunk.getBiomeType(x & 15, y, z & 15); } } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java index 2f237324b..91869ff73 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/BitSetBlocks.java @@ -116,7 +116,7 @@ public class BitSetBlocks implements IChunkSet { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return null; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java index 353f22cb7..7db46500b 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/CharSetBlocks.java @@ -45,7 +45,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { if (biomes == null) return null; return biomes[(z << 4) | x]; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java index 0e0c81100..22c68af74 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/FallbackChunkGet.java @@ -32,8 +32,8 @@ public class FallbackChunkGet implements IChunkGet { } @Override - public BiomeType getBiomeType(int x, int z) { - return extent.getBiomeType(bx + x, bz + z); + public BiomeType getBiomeType(int x, int y, int z) { + return extent.getBiomeType(bx + x, y, bz + z); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java index ffdb8ecc8..630b92cdd 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/blocks/NullChunkGet.java @@ -27,7 +27,7 @@ public enum NullChunkGet implements IChunkGet { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return BiomeTypes.FOREST; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java index 2aea39ee4..49c1aa6dd 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/ChunkHolder.java @@ -124,8 +124,8 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public BiomeType getBiome(ChunkHolder chunk, int x, int z) { - return chunk.chunkExisting.getBiomeType(x, z); + public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) { + return chunk.chunkExisting.getBiomeType(x, y, z); } @Override @@ -169,8 +169,8 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public BiomeType getBiome(ChunkHolder chunk, int x, int z) { - return chunk.chunkExisting.getBiomeType(x, z); + public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) { + return chunk.chunkExisting.getBiomeType(x, y, z); } @Override @@ -210,10 +210,10 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public BiomeType getBiome(ChunkHolder chunk, int x, int z) { + public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) { chunk.getOrCreateGet(); chunk.delegate = BOTH; - return chunk.getBiomeType(x, z); + return chunk.getBiomeType(x, y, z); } @Override @@ -263,10 +263,10 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public BiomeType getBiome(ChunkHolder chunk, int x, int z) { + public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) { chunk.getOrCreateGet(); chunk.delegate = GET; - return chunk.getBiomeType(x, z); + return chunk.getBiomeType(x, y, z); } @Override @@ -467,8 +467,8 @@ public class ChunkHolder> implements IQueueChunk { } @Override - public BiomeType getBiomeType(int x, int z) { - return delegate.getBiome(this, x, z); + public BiomeType getBiomeType(int x, int y, int z) { + return delegate.getBiome(this, x, y, z); } @Override @@ -491,7 +491,7 @@ public class ChunkHolder> implements IQueueChunk { boolean setBlock(ChunkHolder chunk, int x, int y, int z, BlockStateHolder holder); - BiomeType getBiome(ChunkHolder chunk, int x, int z); + BiomeType getBiome(ChunkHolder chunk, int x, int y, int z); BlockState getBlock(ChunkHolder chunk, int x, int y, int z); diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java index 108c55fc4..c1a1ac53f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/chunk/NullChunk.java @@ -93,7 +93,7 @@ public enum NullChunk implements IQueueChunk { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return null; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/CharFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/CharFilterBlock.java index 63bb9ecc4..d45fd957f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/CharFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/CharFilterBlock.java @@ -416,11 +416,11 @@ public class CharFilterBlock extends ChunkFilterBlock { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { if (x >> 4 == chunkX && z >> 4 == chunkZ) { - return get.getBiomeType(x & 15, z & 15); + return get.getBiomeType(x & 15, y, z & 15); } - return getExtent().getBiomeType(x, z); + return getExtent().getBiomeType(x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilterBlock.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilterBlock.java index 7147be8ad..20a6e4aa7 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilterBlock.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/filter/block/DelegateFilterBlock.java @@ -781,8 +781,8 @@ public class DelegateFilterBlock extends FilterBlock { } @Override - public BiomeType getBiomeType(int x, int z) { - return parent.getBiomeType(x, z); + public BiomeType getBiomeType(int x, int y, int z) { + return parent.getBiomeType(x, y, z); } @Deprecated diff --git a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitExtent.java index 9dda5c80b..8e17bc57f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/beta/implementation/processors/LimitExtent.java @@ -563,10 +563,10 @@ public class LimitExtent extends PassthroughExtent { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { limit.THROW_MAX_CHECKS(); try { - return getExtent().getBiomeType(x, z); + return getExtent().getBiomeType(x, y, z); } catch (FaweException e) { if (!limit.MAX_FAILS()) { throw e; diff --git a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java index c89ef3b28..c7b06a3b4 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/jnbt/anvil/MCAChunk.java @@ -446,7 +446,7 @@ public class MCAChunk implements IChunk { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return this.biomes[(z << 4) | x]; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java index 32bfcfe36..b32ff5a5e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/brush/visualization/cfi/HeightMapMCAGenerator.java @@ -897,7 +897,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr } @Override - public BiomeType getBiomeType(int x, int z) throws FaweChunkLoadException { + public BiomeType getBiomeType(int x, int y, int z) throws FaweChunkLoadException { int index = z * getWidth() + x; if (index < 0 || index >= getArea()) index = Math.floorMod(index, getArea()); return BiomeTypes.get(biomes.getByte(index)); @@ -952,7 +952,7 @@ public class HeightMapMCAGenerator extends MCAWriter implements StreamChange, Dr @Override public BiomeType getBiome(BlockVector2 position) { - return getBiomeType(position.getBlockX(), position.getBlockZ()); + return getBiomeType(position.getBlockX(), 0, position.getBlockZ()); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java index 30a6b5200..f11e1bd9f 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/changeset/FaweChangeSet.java @@ -203,7 +203,7 @@ public abstract class FaweChangeSet implements ChangeSet, IBatchProcessor, Close for (int x = 0; x < 16; x++, index++) { BiomeType newBiome = biomes[index]; if (newBiome != null) { - BiomeType oldBiome = get.getBiomeType(x, z); + BiomeType oldBiome = get.getBiomeType(x, 0, z); if (oldBiome != newBiome) { addBiomeChange(bx + x, bz + z, oldBiome, newBiome); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java index 506f39ed5..6a09ef441 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/CPUOptimizedClipboard.java @@ -89,7 +89,7 @@ public class CPUOptimizedClipboard extends LinearClipboard { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return getBiome(getIndex(x, 0, z)); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java index 1cb9b41d5..7d668154c 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DelegateClipboard.java @@ -128,8 +128,8 @@ public class DelegateClipboard implements Clipboard { } @Override - public BiomeType getBiomeType(int x, int z) { - return parent.getBiomeType(x, z); + public BiomeType getBiomeType(int x, int y, int z) { + return parent.getBiomeType(x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java index f40657289..db1eb9f36 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/DiskOptimizedClipboard.java @@ -215,7 +215,7 @@ public class DiskOptimizedClipboard extends LinearClipboard implements Closeable } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return getBiome(getIndex(x, 0, z)); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java index 5cedda7ad..3a802311e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/MemoryOptimizedClipboard.java @@ -124,7 +124,7 @@ public class MemoryOptimizedClipboard extends LinearClipboard { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return getBiome(getIndex(x, 0, z)); } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java index 8c680e823..208898f97 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/clipboard/WorldCopyClipboard.java @@ -50,8 +50,8 @@ public class WorldCopyClipboard extends ReadOnlyClipboard { } @Override - public BiomeType getBiomeType(int x, int z) { - return getExtent().getBiomeType(x, z); + public BiomeType getBiomeType(int x, int y, int z) { + return getExtent().getBiomeType(x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java index a8663b3ff..f67b3b3e9 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/FaweRegionExtent.java @@ -101,14 +101,14 @@ public abstract class FaweRegionExtent extends ResettableExtent implements IBatc } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { if (!contains(x, z)) { if (!limit.MAX_FAILS()) { WEManager.IMP.cancelEditSafe(this, FaweCache.OUTSIDE_REGION); } return null; } - return super.getBiomeType(x, z); + return super.getBiomeType(x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java index 93f31fd29..e796c2b20 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/NullExtent.java @@ -68,7 +68,7 @@ public class NullExtent extends FaweRegionExtent implements IBatchProcessor { } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { throw reason; } diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java index 16baf4fe6..958005b3e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/extent/TransformExtent.java @@ -87,9 +87,9 @@ public class TransformExtent extends BlockTransformExtent { } @Override - public BiomeType getBiomeType(int x, int z) { - BlockVector3 p = getPos(x, 0, z); - return super.getBiomeType(p.getX(), p.getZ()); + public BiomeType getBiomeType(int x, int y, int z) { + BlockVector3 p = getPos(x, y, z); + return super.getBiomeType(p.getX(), y, p.getZ()); } @Override diff --git a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/visualizer/SchemVis.java b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/visualizer/SchemVis.java index ba2e72c4f..20555819e 100644 --- a/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/visualizer/SchemVis.java +++ b/worldedit-core/src/main/java/com/boydti/fawe/object/schematic/visualizer/SchemVis.java @@ -547,7 +547,7 @@ import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; // public void sendChunk(int x, int z, int bitMask) { /* do nothing - never used*/ } // // @Override -// public BiomeType getBiomeType(int x, int z) throws FaweCache.CHUNK { +// public BiomeType getBiomeType(int x, int y, int z) throws FaweCache.CHUNK { // // TODO later (currently not used) // return BiomeTypes.FOREST; // } 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 bdc7d304c..ab359cd02 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 @@ -168,8 +168,8 @@ public class AbstractDelegateExtent implements Extent, LightingExtent { } @Override - public BiomeType getBiomeType(int x, int z) { - return extent.getBiomeType(x, z); + public BiomeType getBiomeType(int x, int y, int z) { + return extent.getBiomeType(x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/InputExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/InputExtent.java index df48b533a..4d264c4e2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/InputExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/InputExtent.java @@ -79,10 +79,10 @@ public interface InputExtent { * @return the biome at the location */ default BiomeType getBiome(BlockVector2 position) { - return getBiomeType(position.getX(), position.getZ()); + return getBiomeType(position.getX(), 0, position.getZ()); } - default BiomeType getBiomeType(int x, int z) { + default BiomeType getBiomeType(int x, int y, int z) { return getBiome(MutableBlockVector2.get(x, z)); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java index 5efe2d316..a6088581e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/BlockArrayClipboard.java @@ -177,7 +177,7 @@ public class BlockArrayClipboard extends DelegateClipboard implements Clipboard, @Override public BiomeType getBiome(BlockVector2 position) { BlockVector2 v = position.subtract(region.getMinimumPoint().toBlockVector2()); - return getParent().getBiomeType(v.getX(), v.getZ()); + return getParent().getBiomeType(v.getX(), 0, v.getZ()); } @Override @@ -234,10 +234,10 @@ public class BlockArrayClipboard extends DelegateClipboard implements Clipboard, } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { x -= offset.getX(); z -= offset.getZ(); - return getParent().getBiomeType(x, z); + return getParent().getBiomeType(x, y, z); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java index d7dc007c5..b7a7fe998 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/NullWorld.java @@ -94,7 +94,7 @@ public class NullWorld extends AbstractWorld { return BiomeTypes.THE_VOID; } @Override - public BiomeType getBiomeType(int x, int z) { + public BiomeType getBiomeType(int x, int y, int z) { return BiomeTypes.THE_VOID; } diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java index 64b18c84e..4a6302e5b 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java @@ -68,7 +68,7 @@ import static org.mockito.Mockito.when; @DisplayName("An ordered block map") class BlockMapTest { - +/* private static Platform mockedPlatform = mock(Platform.class); @BeforeAll @@ -584,5 +584,5 @@ class BlockMapTest { assertEquals(air, entry.getValue()); } } - +*/ }