diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 6c44e67ab..88b4bdb3e 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -21,7 +21,7 @@ repositories { maven { url = uri("http://repo.dmulloy2.net/content/groups/public/") } maven { url = uri("http://ci.ender.zone/plugin/repository/everything/") } maven { url = uri("https://repo.inventivetalent.org/content/groups/public/")} - flatDir {dir(File("lib"))} + flatDir {dir(File("src/main/resources"))} } configurations.all { @@ -36,6 +36,7 @@ dependencies { "api"(project(":worldedit-libs:core")) "api"(project(":worldedit-libs:bukkit")) "compile"(":worldedit-adapters:") + "compile"("org.spigotmcv1_13_r2:spigotmcv1_13_r2:1_13_r2") "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/MapChunkUtil.java b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/MapChunkUtil.java index bd76c3012..a367b774e 100644 --- a/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/MapChunkUtil.java +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/MapChunkUtil.java @@ -31,8 +31,11 @@ public abstract class MapChunkUtil { fieldX.setInt(nmsPacket, packet.getChunkX()); fieldZ.setInt(nmsPacket, packet.getChunkZ()); fieldBitMask.set(nmsPacket, packet.getChunk().getBitMask()); - Object heightMap = adapter.fromNative(packet.getHeightMap()); - fieldHeightMap.set(nmsPacket, heightMap); + + if (fieldHeightMap != null) { + Object heightMap = adapter.fromNative(packet.getHeightMap()); + fieldHeightMap.set(nmsPacket, heightMap); + } fieldChunkData.set(nmsPacket, packet.getSectionBytes()); 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..8d6286c6b --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/boydti/fawe/bukkit/adapter/mc1_13/BukkitGetBlocks_1_13.java @@ -0,0 +1,654 @@ +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 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))); + 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/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 63291192d..af88d3fb4 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -564,7 +564,7 @@ public class BukkitWorld extends AbstractWorld { @Override public IChunkGet get(int chunkX, int chunkZ) { - return new BukkitGetBlocks_1_14(getWorldChecked(), chunkX, chunkZ); + return WorldEditPlugin.getInstance().getBukkitImplAdapter().get(getWorldChecked(), chunkX, chunkZ); } @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index bb7001ee1..8fabcefcb 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -24,6 +24,7 @@ import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAM import com.boydti.fawe.Fawe; import com.boydti.fawe.bukkit.FaweBukkit; +import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_13_R2; import com.sk89q.worldedit.bukkit.adapter.impl.FAWE_Spigot_v1_14_R4; import com.boydti.fawe.util.MainUtil; import com.google.common.base.Joiner; @@ -246,7 +247,8 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter // Biome for (Biome biome : Biome.values()) { String lowerCaseBiomeName = biome.name().toLowerCase(Locale.ROOT); - BiomeType.REGISTRY.register("minecraft:" + lowerCaseBiomeName, new BiomeType("minecraft:" + lowerCaseBiomeName)); + BiomeType biomeType = BiomeType.REGISTRY.register("minecraft:" + lowerCaseBiomeName, new BiomeType("minecraft:" + lowerCaseBiomeName)); + if (bukkitAdapter != null) biomeType.setLegacyId(bukkitAdapter.getInternalBiomeId(biomeType)); } /*// Block & Item for (Material material : Material.values()) { @@ -367,6 +369,7 @@ public class WorldEditPlugin extends JavaPlugin { //implements TabCompleter // Attempt to load a Bukkit adapter BukkitImplLoader adapterLoader = new BukkitImplLoader(); try { + adapterLoader.addClass(FAWE_Spigot_v1_13_R2.class); adapterLoader.addClass(FAWE_Spigot_v1_14_R4.class); } catch (Throwable throwable) { throwable.printStackTrace(); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index 6cd25cf8b..c5ef1e510 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.bukkit.adapter; import com.boydti.fawe.Fawe; +import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.bukkit.FaweBukkit; import com.sk89q.jnbt.CompoundTag; @@ -34,6 +35,7 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.world.DataFixer; +import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -42,6 +44,7 @@ import com.sk89q.worldedit.world.registry.BlockMaterial; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.WorldCreator; +import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; @@ -238,4 +241,12 @@ public interface BukkitImplAdapter extends IBukkitAdapter { default boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) { return editSession.regenerate(region); } + + default IChunkGet get(World world, int chunkX, int chunkZ) { + throw new UnsupportedOperationException(); + } + + default int getInternalBiomeId(BiomeType biome) { + return Biome.BADLANDS.ordinal(); + } } \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_13_R2.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_13_R2.java new file mode 100644 index 000000000..0296cde34 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_13_R2.java @@ -0,0 +1,453 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl; + +import com.bekvon.bukkit.residence.commands.material; +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkGet; +import com.boydti.fawe.beta.implementation.packet.ChunkPacket; +import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; +import com.boydti.fawe.bukkit.adapter.mc1_13.BlockMaterial_1_13; +import com.boydti.fawe.bukkit.adapter.mc1_13.BukkitAdapter_1_13; +import com.boydti.fawe.bukkit.adapter.mc1_13.BukkitGetBlocks_1_13; +import com.boydti.fawe.bukkit.adapter.mc1_13.LazyCompoundTag_1_13; +import com.boydti.fawe.bukkit.adapter.mc1_13.MapChunkUtil_1_13; +import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitGetBlocks_1_14; +import com.google.common.io.Files; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.blocks.TileEntityBlock; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.CachedBukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.IDelegateBukkitImplAdapter; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.LazyBaseEntity; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.block.BlockTypesCache; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import net.minecraft.server.v1_13_R2.BiomeBase; +import net.minecraft.server.v1_13_R2.Block; +import net.minecraft.server.v1_13_R2.BlockPosition; +import net.minecraft.server.v1_13_R2.Chunk; +import net.minecraft.server.v1_13_R2.ChunkProviderServer; +import net.minecraft.server.v1_13_R2.ChunkSection; +import net.minecraft.server.v1_13_R2.Entity; +import net.minecraft.server.v1_13_R2.EntityPlayer; +import net.minecraft.server.v1_13_R2.EntityTypes; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.IDataManager; +import net.minecraft.server.v1_13_R2.IRegistry; +import net.minecraft.server.v1_13_R2.ItemStack; +import net.minecraft.server.v1_13_R2.MinecraftKey; +import net.minecraft.server.v1_13_R2.NBTBase; +import net.minecraft.server.v1_13_R2.NBTTagCompound; +import net.minecraft.server.v1_13_R2.NBTTagInt; +import net.minecraft.server.v1_13_R2.PacketPlayOutMapChunk; +import net.minecraft.server.v1_13_R2.PlayerChunk; +import net.minecraft.server.v1_13_R2.TileEntity; +import net.minecraft.server.v1_13_R2.World; +import net.minecraft.server.v1_13_R2.WorldNBTStorage; +import net.minecraft.server.v1_13_R2.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_13_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_13_R2.CraftServer; +import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_13_R2.block.CraftBlock; +import org.bukkit.craftbukkit.v1_13_R2.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; +import org.bukkit.generator.ChunkGenerator; + +import javax.annotation.Nullable; +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.OptionalInt; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Supplier; + +import static com.google.common.base.Preconditions.checkNotNull; + +public final class FAWE_Spigot_v1_13_R2 extends CachedBukkitAdapter implements IDelegateBukkitImplAdapter { + private final Spigot_v1_13_R2_2 parent; + + // ------------------------------------------------------------------------ + // Code that may break between versions of Minecraft + // ------------------------------------------------------------------------ + + public FAWE_Spigot_v1_13_R2() throws NoSuchFieldException, NoSuchMethodException { + this.parent = new Spigot_v1_13_R2_2(); + } + + @Override + public BukkitImplAdapter getParent() { + return parent; + } + + public char[] idbToStateOrdinal; + + private synchronized boolean init() { + if (idbToStateOrdinal != null) return false; + idbToStateOrdinal = new char[Block.REGISTRY_ID.a()]; // size + for (int i = 0; i < idbToStateOrdinal.length; i++) { + BlockState state = BlockTypesCache.states[i]; + BlockMaterial mat = state.getMaterial(); + if (mat instanceof BlockMaterial_1_13) { + BlockMaterial_1_13 nmsMat = (BlockMaterial_1_13) mat; + int id = Block.REGISTRY_ID.getId(nmsMat.getState()); + idbToStateOrdinal[id] = state.getOrdinalChar(); + } + } + return true; + } + + @Override + public BlockMaterial getMaterial(BlockType blockType) { + Block block = getBlock(blockType); + return block != null ? new BlockMaterial_1_13(block) : null; + } + + @Override + public BlockMaterial getMaterial(BlockState state) { + IBlockData bs = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState(); + return bs != null ? new BlockMaterial_1_13(bs.getBlock(), bs) : null; + } + + public Block getBlock(BlockType blockType) { + return IRegistry.BLOCK.get(new MinecraftKey(blockType.getNamespace(), blockType.getResource())); + } + + @SuppressWarnings("deprecation") + @Override + public BaseBlock getBlock(Location location) { + checkNotNull(location); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + org.bukkit.block.Block bukkitBlock = location.getBlock(); + BlockState state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); + if (state.getBlockType().getMaterial().hasContainer()) { + //Read the NBT data + TileEntity te = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z)); + if (te != null) { + NBTTagCompound tag = new NBTTagCompound(); + te.save(tag); // readTileEntityIntoTag + return state.toBaseBlock((CompoundTag) toNative(tag)); + } + } + + return state.toBaseBlock(); + } + + @Override + public boolean setBlock(Location location, BlockStateHolder state, boolean notifyAndLight) { + return this.setBlock(location.getChunk(), location.getBlockX(), location.getBlockY(), location.getBlockZ(), state, notifyAndLight); + } + + public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) { + CraftChunk craftChunk = (CraftChunk) chunk; + Chunk nmsChunk = craftChunk.getHandle(); + World nmsWorld = nmsChunk.getWorld(); + + IBlockData blockData = ((BlockMaterial_1_13) state.getMaterial()).getState(); + ChunkSection[] sections = nmsChunk.getSections(); + int y4 = y >> 4; + ChunkSection section = sections[y4]; + + IBlockData existing; + if (section == null) { + existing = ((BlockMaterial_1_13) BlockTypes.AIR.getDefaultState().getMaterial()).getState(); + } else { + existing = section.getType(x & 15, y & 15, z & 15); + } + + BlockPosition pos = new BlockPosition(x, y, z); + + nmsChunk.d(pos); // removeTileEntity } Force delete the old tile entity + + CompoundTag nativeTag = state instanceof BaseBlock ? ((BaseBlock)state).getNbtData() : null; + if (nativeTag != null || existing instanceof TileEntityBlock) { + nmsWorld.setTypeAndData(pos, blockData, 0); + // remove tile + if (nativeTag != null) { + // We will assume that the tile entity was created for us, + // though we do not do this on the Forge version + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity != null) { + NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag); + tag.set("x", new NBTTagInt(x)); + tag.set("y", new NBTTagInt(y)); + tag.set("z", new NBTTagInt(z)); + tileEntity.load(tag); // readTagIntoTileEntity + } + } + } else { + if (existing == blockData) return true; + if (section == null) { + if (blockData.isAir()) return true; + sections[y4] = section = new ChunkSection(y4 << 4, nmsWorld.worldProvider.g()); + } + nmsChunk.setType(pos = new BlockPosition(x, y, z), blockData, false); + } + if (update) { + nmsWorld.getMinecraftWorld().notify(pos, existing, blockData, 0); + } + return true; + } + + @Nullable + private static String getEntityId(Entity entity) { + MinecraftKey minecraftkey = EntityTypes.getName(entity.P()); + return minecraftkey == null ? null : minecraftkey.toString(); + } + + private static void readEntityIntoTag(Entity entity, NBTTagCompound tag) { + entity.save(tag); + } + + @Override + public BaseEntity getEntity(org.bukkit.entity.Entity entity) { + checkNotNull(entity); + + CraftEntity craftEntity = ((CraftEntity) entity); + Entity mcEntity = craftEntity.getHandle(); + + String id = getEntityId(mcEntity); + + if (id != null) { + EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id); + Supplier saveTag = () -> { + NBTTagCompound tag = new NBTTagCompound(); + readEntityIntoTag(mcEntity, tag); + return (CompoundTag) toNative(tag); + }; + return new LazyBaseEntity(type, saveTag); + } else { + return null; + } + } + + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + BlockMaterial_1_13 material = (BlockMaterial_1_13) state.getMaterial(); + IBlockData mcState = material.getCraftBlockData().getState(); + return OptionalInt.of(Block.REGISTRY_ID.getId(mcState)); + } + + @Override + public BlockState adapt(BlockData blockData) { + CraftBlockData cbd = ((CraftBlockData) blockData); + IBlockData ibd = cbd.getState(); + return adapt(ibd); + } + + public BlockState adapt(IBlockData ibd) { + return BlockTypesCache.states[adaptToInt(ibd)]; + } + + public int adaptToInt(IBlockData ibd) { + try { + int id = Block.REGISTRY_ID.getId(ibd); + return idbToStateOrdinal[id]; + } catch (NullPointerException e) { + init(); + return adaptToInt(ibd); + } + } + + public char adaptToChar(IBlockData ibd) { + try { + int id = Block.REGISTRY_ID.getId(ibd); + return idbToStateOrdinal[id]; + } catch (NullPointerException e) { + init(); + return adaptToChar(ibd); + } + } + + @Override + public BlockData adapt(BlockStateHolder state) { + BlockMaterial_1_13 material = (BlockMaterial_1_13) state.getMaterial(); + return material.getCraftBlockData(); + } + + @Override + public void notifyAndLightBlock(Location position, BlockState previousType) { + this.setBlock(position.getChunk(), position.getBlockX(), position.getBlockY(), position.getBlockZ(), previousType, true); + } + + private MapChunkUtil_1_13 mapUtil = new MapChunkUtil_1_13(); + + @Override + public void sendFakeChunk(org.bukkit.World world, Player target, ChunkPacket packet) { + WorldServer nmsWorld = ((CraftWorld) world).getHandle(); + PlayerChunk map = BukkitAdapter_1_13.getPlayerChunk(nmsWorld, packet.getChunkX(), packet.getChunkZ()); + if (map != null && map.e()) { + boolean flag = false; + List inChunk = map.players; + if (inChunk != null && !inChunk.isEmpty()) { + EntityPlayer nmsTarget = target != null ? ((CraftPlayer) target).getHandle() : null; + for (EntityPlayer current : inChunk) { + if (nmsTarget == null || current == nmsTarget) { + synchronized (packet) { + PacketPlayOutMapChunk nmsPacket = (PacketPlayOutMapChunk) packet.getNativePacket(); + if (nmsPacket == null) { + nmsPacket = mapUtil.create(FAWE_Spigot_v1_13_R2.this, packet); + packet.setNativePacket(nmsPacket); + } + try { + FaweCache.IMP.CHUNK_FLAG.get().set(true); + current.playerConnection.sendPacket(nmsPacket); + } finally { + FaweCache.IMP.CHUNK_FLAG.get().set(false); + } + } + } + } + } + } + } + + @Override + public Map> getProperties(BlockType blockType) { + return getParent().getProperties(blockType); + } + + @Override + public org.bukkit.inventory.ItemStack adapt(BaseItemStack item) { + ItemStack stack = new ItemStack(IRegistry.ITEM.get(MinecraftKey.a(item.getType().getId())), item.getAmount()); + stack.setTag(((NBTTagCompound) fromNative(item.getNbtData()))); + return CraftItemStack.asCraftMirror(stack); + } + + @Override + public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { + final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); + final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount()); + weStack.setNbtData(((CompoundTag) toNative(nmsStack.getTag()))); + return weStack; + } + + @Override + public Tag toNative(NBTBase foreign) { + return parent.toNative(foreign); + } + + @Override + public NBTBase fromNative(Tag foreign) { + if (foreign instanceof LazyCompoundTag_1_13) { + return ((LazyCompoundTag_1_13) foreign).get(); + } + return parent.fromNative(foreign); + } + + @Override + public boolean regenerate(org.bukkit.World world, Region region, EditSession editSession) { + WorldServer originalWorld = ((CraftWorld) world).getHandle(); + ChunkProviderServer provider = originalWorld.getChunkProvider(); + if (!(provider instanceof ChunkProviderServer)) { + return false; + } + + File saveFolder = Files.createTempDir(); + // register this just in case something goes wrong + // normally it should be deleted at the end of this method + saveFolder.deleteOnExit(); + try { + CraftServer server = originalWorld.getServer(); + IDataManager originalDataManager = originalWorld.getDataManager(); + WorldNBTStorage saveHandler = new WorldNBTStorage(saveFolder, originalDataManager.getDirectory().getName(), server.getServer(), originalDataManager.i()); + ChunkGenerator originalGen = world.getGenerator(); + + try (WorldServer freshWorld = new WorldServer(server.getServer(), + saveHandler, + originalWorld.worldMaps, + originalWorld.worldData, + originalWorld.worldProvider.getDimensionManager(), + originalWorld.methodProfiler, + world.getEnvironment(), + originalGen + ) + ) { + SingleThreadQueueExtent extent = new SingleThreadQueueExtent(); + extent.init(null, (x, z) -> new BukkitGetBlocks_1_13(freshWorld, x, z) { + @Override + public Chunk ensureLoaded(World nmsWorld, int X, int Z) { + Chunk cached = nmsWorld.getChunkIfLoaded(X, Z); + if (cached != null) return cached; + Future future = Fawe.get().getQueueHandler().sync((Supplier) () -> freshWorld.getChunkAt(X, Z)); +// while (!future.isDone()) { +// // this feels so dirty +// freshWorld.getChunkProvider().runTasks(); +// } + try { + return future.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + }, null); + for (BlockVector3 vec : region) { + editSession.setBlock(vec, extent.getFullBlock(vec)); + } + } + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } finally { + saveFolder.delete(); + } + return true; + } + + @Override + public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) { + return new BukkitGetBlocks_1_13(world, chunkX, chunkZ); + } + + @Override + public int getInternalBiomeId(BiomeType biome) { + BiomeBase base = CraftBlock.biomeToBiomeBase(BukkitAdapter.adapt(biome)); + return IRegistry.BIOME.a(base); + } +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java index a7a726fd4..46ae43e18 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/FAWE_Spigot_v1_14_R4.java @@ -23,6 +23,7 @@ import com.bekvon.bukkit.residence.commands.material; import com.bekvon.bukkit.residence.commands.server; import com.boydti.fawe.Fawe; import com.boydti.fawe.FaweCache; +import com.boydti.fawe.beta.IChunkGet; import com.boydti.fawe.beta.implementation.packet.ChunkPacket; import com.boydti.fawe.beta.implementation.queue.SingleThreadQueueExtent; import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler; @@ -52,6 +53,7 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -60,6 +62,7 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.registry.BlockMaterial; +import net.minecraft.server.v1_14_R1.BiomeBase; import net.minecraft.server.v1_14_R1.Block; import net.minecraft.server.v1_14_R1.BlockPosition; import net.minecraft.server.v1_14_R1.Chunk; @@ -89,6 +92,7 @@ import org.bukkit.block.data.BlockData; import org.bukkit.craftbukkit.v1_14_R1.CraftChunk; import org.bukkit.craftbukkit.v1_14_R1.CraftServer; import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_14_R1.block.CraftBlock; import org.bukkit.craftbukkit.v1_14_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_14_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; @@ -447,4 +451,15 @@ public final class FAWE_Spigot_v1_14_R4 extends CachedBukkitAdapter implements I } return true; } + + @Override + public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) { + return new BukkitGetBlocks_1_14(world, chunkX, chunkZ); + } + + @Override + public int getInternalBiomeId(BiomeType biome) { + BiomeBase base = CraftBlock.biomeToBiomeBase(BukkitAdapter.adapt(biome)); + return IRegistry.BIOME.a(base); + } } diff --git a/worldedit-bukkit/src/main/resources/worldedit-adapters.jar b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar index 43a6d5009..843f2d3b6 100644 Binary files a/worldedit-bukkit/src/main/resources/worldedit-adapters.jar and b/worldedit-bukkit/src/main/resources/worldedit-adapters.jar differ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java index ce2fb9a84..5fc3fbcc2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/EditSession.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit; import com.boydti.fawe.FaweCache; +import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.boydti.fawe.config.Settings; import com.boydti.fawe.object.FaweLimit; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java index 822e20528..3ad7516fa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/HistorySubCommands.java @@ -21,6 +21,10 @@ import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.annotation.Confirm; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; import com.sk89q.worldedit.internal.annotation.AllowedRegion; import com.sk89q.worldedit.internal.annotation.Time; import com.sk89q.worldedit.math.BlockVector2; @@ -93,8 +97,7 @@ public class HistorySubCommands { @Command( name = "rollback", desc = "Undo a specific edit. " + - " - The time uses s, m, h, d, y.\n" + - " - Import from disk: /history import" + " - The time uses s, m, h, d, y." ) @CommandPermissions("worldedit.history.undo") @Confirm diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java index a49632fc3..b75e597f5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeTypes.java @@ -144,82 +144,4 @@ public final class BiomeTypes { } return maxBiomeId; } - - static { - OCEAN.setLegacyId(0); - PLAINS.setLegacyId(1); - DESERT.setLegacyId(2); - MOUNTAINS.setLegacyId(3); - FOREST.setLegacyId(4); - TAIGA.setLegacyId(5); - SWAMP.setLegacyId(6); - RIVER.setLegacyId(7); - NETHER.setLegacyId(8); - THE_END.setLegacyId(9); - FROZEN_OCEAN.setLegacyId(10); - FROZEN_RIVER.setLegacyId(11); - SNOWY_TUNDRA.setLegacyId(12); - SNOWY_MOUNTAINS.setLegacyId(13); - MUSHROOM_FIELDS.setLegacyId(14); - MUSHROOM_FIELD_SHORE.setLegacyId(15); - BEACH.setLegacyId(16); - DESERT_HILLS.setLegacyId(17); - WOODED_HILLS.setLegacyId(18); - TAIGA_HILLS.setLegacyId(19); - MOUNTAIN_EDGE.setLegacyId(20); - JUNGLE.setLegacyId(21); - JUNGLE_HILLS.setLegacyId(22); - JUNGLE_EDGE.setLegacyId(23); - DEEP_OCEAN.setLegacyId(24); - STONE_SHORE.setLegacyId(25); - SNOWY_BEACH.setLegacyId(26); - BIRCH_FOREST.setLegacyId(27); - BIRCH_FOREST_HILLS.setLegacyId(28); - DARK_FOREST.setLegacyId(29); - SNOWY_TAIGA.setLegacyId(30); - SNOWY_TAIGA_HILLS.setLegacyId(31); - GIANT_TREE_TAIGA.setLegacyId(32); - GIANT_TREE_TAIGA_HILLS.setLegacyId(33); - WOODED_MOUNTAINS.setLegacyId(34); - SAVANNA.setLegacyId(35); - SAVANNA_PLATEAU.setLegacyId(36); - BADLANDS.setLegacyId(37); - WOODED_BADLANDS_PLATEAU.setLegacyId(38); - BADLANDS_PLATEAU.setLegacyId(39); - SMALL_END_ISLANDS.setLegacyId(40); - END_MIDLANDS.setLegacyId(41); - END_HIGHLANDS.setLegacyId(42); - END_BARRENS.setLegacyId(43); - WARM_OCEAN.setLegacyId(44); - LUKEWARM_OCEAN.setLegacyId(45); - COLD_OCEAN.setLegacyId(46); - DEEP_WARM_OCEAN.setLegacyId(47); - DEEP_LUKEWARM_OCEAN.setLegacyId(48); - DEEP_COLD_OCEAN.setLegacyId(49); - DEEP_FROZEN_OCEAN.setLegacyId(50); - THE_VOID.setLegacyId(127); - SUNFLOWER_PLAINS.setLegacyId(129); - DESERT_LAKES.setLegacyId(130); - GRAVELLY_MOUNTAINS.setLegacyId(131); - FLOWER_FOREST.setLegacyId(132); - TAIGA_MOUNTAINS.setLegacyId(133); - SWAMP_HILLS.setLegacyId(134); - ICE_SPIKES.setLegacyId(140); - MODIFIED_JUNGLE.setLegacyId(149); - MODIFIED_JUNGLE_EDGE.setLegacyId(151); - TALL_BIRCH_FOREST.setLegacyId(155); - TALL_BIRCH_HILLS.setLegacyId(156); - DARK_FOREST_HILLS.setLegacyId(157); - SNOWY_TAIGA_MOUNTAINS.setLegacyId(158); - GIANT_SPRUCE_TAIGA.setLegacyId(160); - GIANT_SPRUCE_TAIGA_HILLS.setLegacyId(161); - MODIFIED_GRAVELLY_MOUNTAINS.setLegacyId(162); - SHATTERED_SAVANNA.setLegacyId(163); - SHATTERED_SAVANNA_PLATEAU.setLegacyId(164); - ERODED_BADLANDS.setLegacyId(165); - MODIFIED_WOODED_BADLANDS_PLATEAU.setLegacyId(166); - MODIFIED_BADLANDS_PLATEAU.setLegacyId(167); - BAMBOO_JUNGLE.setLegacyId(168); - BAMBOO_JUNGLE_HILLS.setLegacyId(169); - } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java index 9117d4d18..05fa97cb2 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/registry/LegacyMapper.java @@ -56,6 +56,13 @@ public final class LegacyMapper { private static final Logger log = LoggerFactory.getLogger(LegacyMapper.class); private static LegacyMapper INSTANCE = new LegacyMapper(); + static { + try { + INSTANCE.loadFromResource(); + } catch (Throwable e) { + log.warn("Failed to load the built-in legacy id registry", e); + } + } private Map blockEntries = new HashMap<>(); @@ -68,11 +75,6 @@ public final class LegacyMapper { * Create a new instance. */ private LegacyMapper() { - try { - loadFromResource(); - } catch (Throwable e) { - log.warn("Failed to load the built-in legacy id registry", e); - } } /** @@ -118,6 +120,7 @@ public final class LegacyMapper { } if (blockState == null) { log.warn("Unknown block: " + value); + continue; } } blockArr[combinedId] = blockState.getInternalId();