From 099fd9a63b4ac80ddd97ea2dd093eeb9ac2af01e Mon Sep 17 00:00:00 2001 From: sk89q Date: Sun, 6 Apr 2014 01:41:28 -0700 Subject: [PATCH] [Forge] Made TileEntity NBT handling fit WE spec. Fixed schematic loading/saving as a result. --- .../com/sk89q/worldedit/forge/ForgeWorld.java | 63 ++--- .../sk89q/worldedit/forge/NBTConverter.java | 244 ++++++++++++++++++ .../worldedit/forge/TileEntityBaseBlock.java | 88 +------ .../worldedit/forge/TileEntityUtils.java | 142 ++++++++++ 4 files changed, 413 insertions(+), 124 deletions(-) create mode 100644 src/forge/java/com/sk89q/worldedit/forge/NBTConverter.java create mode 100644 src/forge/java/com/sk89q/worldedit/forge/TileEntityUtils.java diff --git a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java index 65a3b3cc0..8395a6a2b 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/src/forge/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -19,36 +19,23 @@ package com.sk89q.worldedit.forge; -import java.lang.ref.WeakReference; -import java.lang.reflect.Field; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.*; +import com.sk89q.worldedit.blocks.*; +import com.sk89q.worldedit.foundation.Block; +import com.sk89q.worldedit.regions.Region; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityHanging; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.IProjectile; -import net.minecraft.entity.item.EntityBoat; -import net.minecraft.entity.item.EntityEnderEye; -import net.minecraft.entity.item.EntityFallingSand; -import net.minecraft.entity.item.EntityItem; -import net.minecraft.entity.item.EntityItemFrame; -import net.minecraft.entity.item.EntityMinecart; -import net.minecraft.entity.item.EntityPainting; -import net.minecraft.entity.item.EntityTNTPrimed; -import net.minecraft.entity.item.EntityXPOrb; +import net.minecraft.entity.item.*; import net.minecraft.entity.monster.EntityGolem; import net.minecraft.entity.passive.EntityAmbientCreature; import net.minecraft.entity.passive.EntityAnimal; import net.minecraft.entity.passive.EntityTameable; import net.minecraft.entity.passive.EntityVillager; import net.minecraft.inventory.IInventory; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.tileentity.TileEntityMobSpawner; -import net.minecraft.tileentity.TileEntityNote; -import net.minecraft.tileentity.TileEntitySign; -import net.minecraft.tileentity.TileEntitySkull; +import net.minecraft.tileentity.*; import net.minecraft.util.LongHashMap; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.World; @@ -56,21 +43,11 @@ import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.gen.ChunkProviderServer; -import com.sk89q.worldedit.BiomeType; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.EntityType; -import com.sk89q.worldedit.LocalWorld; -import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.Vector2D; -import com.sk89q.worldedit.blocks.BaseBlock; -import com.sk89q.worldedit.blocks.BaseItemStack; -import com.sk89q.worldedit.blocks.MobSpawnerBlock; -import com.sk89q.worldedit.blocks.NoteBlock; -import com.sk89q.worldedit.blocks.SignBlock; -import com.sk89q.worldedit.blocks.SkullBlock; -import com.sk89q.worldedit.blocks.TileEntityBlock; -import com.sk89q.worldedit.foundation.Block; -import com.sk89q.worldedit.regions.Region; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.util.Iterator; +import java.util.List; +import java.util.Set; public class ForgeWorld extends LocalWorld { private WeakReference world; @@ -255,8 +232,6 @@ public class ForgeWorld extends LocalWorld { TileEntity tile = this.world.get().getBlockTileEntity(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); if (tile != null) { TileEntityBaseBlock block = new TileEntityBaseBlock(type, data, tile); - copyFromWorld(pt, block); - return block; } return new BaseBlock(type, data); @@ -266,7 +241,7 @@ public class ForgeWorld extends LocalWorld { return copyToWorld(pt, block, true); } - public boolean copyToWorld(Vector pt, BaseBlock block, boolean hardcopy) { + public boolean copyToWorld(Vector pt, BaseBlock block, boolean copyData) { if (!(block instanceof TileEntityBlock)) { return false; } @@ -303,19 +278,17 @@ public class ForgeWorld extends LocalWorld { return true; } - if (block instanceof TileEntityBaseBlock) { - TileEntityBaseBlock.set(this.world.get(), pt, (TileEntityBaseBlock) block, hardcopy); + CompoundTag tag = block.getNbtData(); + if (tag != null) { + TileEntityUtils.setTileEntity(this.world.get(), pt, NBTConverter.toNative(tag)); return true; } + return false; } public boolean copyFromWorld(Vector pt, BaseBlock block) { - if (!(block instanceof TileEntityBaseBlock)) { - return false; - } - ((TileEntityBaseBlock) block).setTile(this.world.get().getBlockTileEntity(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ())); - return true; + return false; } public boolean clearContainerBlockContents(Vector pt) { diff --git a/src/forge/java/com/sk89q/worldedit/forge/NBTConverter.java b/src/forge/java/com/sk89q/worldedit/forge/NBTConverter.java new file mode 100644 index 000000000..db6600b53 --- /dev/null +++ b/src/forge/java/com/sk89q/worldedit/forge/NBTConverter.java @@ -0,0 +1,244 @@ +/* + * 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.forge; + +import com.sk89q.jnbt.*; +import net.minecraft.nbt.*; + +import java.util.*; +import java.util.Map.Entry; + +/** + * Converts between JNBT and Minecraft NBT classes. + */ +final class NBTConverter { + + private NBTConverter() { + } + + public static NBTBase toNative(Tag tag) { + if (tag instanceof IntArrayTag) { + return toNative((IntArrayTag) tag); + + } else if (tag instanceof ListTag) { + return toNative((ListTag) tag); + + } else if (tag instanceof EndTag) { + return toNative((EndTag) tag); + + } else if (tag instanceof LongTag) { + return toNative((LongTag) tag); + + } else if (tag instanceof StringTag) { + return toNative((StringTag) tag); + + } else if (tag instanceof IntTag) { + return toNative((IntTag) tag); + + } else if (tag instanceof ByteTag) { + return toNative((ByteTag) tag); + + } else if (tag instanceof ByteArrayTag) { + return toNative((ByteArrayTag) tag); + + } else if (tag instanceof CompoundTag) { + return toNative((CompoundTag) tag); + + } else if (tag instanceof FloatTag) { + return toNative((FloatTag) tag); + + } else if (tag instanceof ShortTag) { + return toNative((ShortTag) tag); + + } else if (tag instanceof DoubleTag) { + return toNative((DoubleTag) tag); + } else { + throw new IllegalArgumentException("Can't convert tag of type " + tag.getClass().getCanonicalName()); + } + } + + public static NBTTagIntArray toNative(IntArrayTag tag) { + int[] value = tag.getValue(); + return new NBTTagIntArray(tag.getName(), Arrays.copyOf(value, value.length)); + } + + public static NBTTagList toNative(ListTag tag) { + NBTTagList list = new NBTTagList(tag.getName()); + for (Tag child : tag.getValue()) { + if (child instanceof EndTag) { + continue; + } + list.appendTag(toNative(child)); + } + return list; + } + + public static NBTTagEnd toNative(EndTag tag) { + return new NBTTagEnd(); + } + + public static NBTTagLong toNative(LongTag tag) { + return new NBTTagLong(tag.getName(), tag.getValue()); + } + + public static NBTTagString toNative(StringTag tag) { + return new NBTTagString(tag.getName(), tag.getValue()); + } + + public static NBTTagInt toNative(IntTag tag) { + return new NBTTagInt(tag.getName(), tag.getValue()); + } + + public static NBTTagByte toNative(ByteTag tag) { + return new NBTTagByte(tag.getName(), tag.getValue()); + } + + public static NBTTagByteArray toNative(ByteArrayTag tag) { + byte[] value = tag.getValue(); + return new NBTTagByteArray(tag.getName(), Arrays.copyOf(value, value.length)); + } + + public static NBTTagCompound toNative(CompoundTag tag) { + NBTTagCompound compound = new NBTTagCompound(tag.getName()); + for (Entry child : tag.getValue().entrySet()) { + compound.setTag(child.getKey(), toNative(child.getValue())); + } + return compound; + } + + public static NBTTagFloat toNative(FloatTag tag) { + return new NBTTagFloat(tag.getName(), tag.getValue()); + } + + public static NBTTagShort toNative(ShortTag tag) { + return new NBTTagShort(tag.getName(), tag.getValue()); + } + + public static NBTTagDouble toNative(DoubleTag tag) { + return new NBTTagDouble(tag.getName(), tag.getValue()); + } + + public static Tag fromNative(NBTBase other) { + if (other instanceof NBTTagIntArray) { + return fromNative((NBTTagIntArray) other); + + } else if (other instanceof NBTTagList) { + return fromNative((NBTTagList) other); + + } else if (other instanceof NBTTagEnd) { + return fromNative((NBTTagEnd) other); + + } else if (other instanceof NBTTagLong) { + return fromNative((NBTTagLong) other); + + } else if (other instanceof NBTTagString) { + return fromNative((NBTTagString) other); + + } else if (other instanceof NBTTagInt) { + return fromNative((NBTTagInt) other); + + } else if (other instanceof NBTTagByte) { + return fromNative((NBTTagByte) other); + + } else if (other instanceof NBTTagByteArray) { + return fromNative((NBTTagByteArray) other); + + } else if (other instanceof NBTTagCompound) { + return fromNative((NBTTagCompound) other); + + } else if (other instanceof NBTTagFloat) { + return fromNative((NBTTagFloat) other); + + } else if (other instanceof NBTTagShort) { + return fromNative((NBTTagShort) other); + + } else if (other instanceof NBTTagDouble) { + return fromNative((NBTTagDouble) other); + } else { + throw new IllegalArgumentException("Can't convert other of type " + other.getClass().getCanonicalName()); + } + } + + public static IntArrayTag fromNative(NBTTagIntArray other) { + int[] value = other.intArray; + return new IntArrayTag(other.getName(), Arrays.copyOf(value, value.length)); + } + + public static ListTag fromNative(NBTTagList other) { + List list = new ArrayList(); + Class listClass = StringTag.class; + for (int i = 0; i < other.tagCount(); i++) { + if (other.tagAt(i) instanceof NBTTagEnd) { + continue; + } + Tag child = fromNative(other.tagAt(i)); + list.add(child); + listClass = child.getClass(); + } + return new ListTag(other.getName(), listClass, list); + } + + public static EndTag fromNative(NBTTagEnd other) { + return new EndTag(); + } + + public static LongTag fromNative(NBTTagLong other) { + return new LongTag(other.getName(), other.data); + } + + public static StringTag fromNative(NBTTagString other) { + return new StringTag(other.getName(), other.data); + } + + public static IntTag fromNative(NBTTagInt other) { + return new IntTag(other.getName(), other.data); + } + + public static ByteTag fromNative(NBTTagByte other) { + return new ByteTag(other.getName(), other.data); + } + + public static ByteArrayTag fromNative(NBTTagByteArray other) { + byte[] value = other.byteArray; + return new ByteArrayTag(other.getName(), Arrays.copyOf(value, value.length)); + } + + public static CompoundTag fromNative(NBTTagCompound other) { + @SuppressWarnings("unchecked") Collection tags = other.getTags(); + Map map = new HashMap(); + for (NBTBase tag : tags) { + map.put(tag.getName(), fromNative(tag)); + } + return new CompoundTag(other.getName(), map); + } + + public static FloatTag fromNative(NBTTagFloat other) { + return new FloatTag(other.getName(), other.data); + } + + public static ShortTag fromNative(NBTTagShort other) { + return new ShortTag(other.getName(), other.data); + } + + public static DoubleTag fromNative(NBTTagDouble other) { + return new DoubleTag(other.getName(), other.data); + } + +} diff --git a/src/forge/java/com/sk89q/worldedit/forge/TileEntityBaseBlock.java b/src/forge/java/com/sk89q/worldedit/forge/TileEntityBaseBlock.java index 48d6fb4ad..1a46607a8 100644 --- a/src/forge/java/com/sk89q/worldedit/forge/TileEntityBaseBlock.java +++ b/src/forge/java/com/sk89q/worldedit/forge/TileEntityBaseBlock.java @@ -19,96 +19,26 @@ package com.sk89q.worldedit.forge; -import java.lang.reflect.Constructor; - -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagInt; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.world.World; - -import com.sk89q.worldedit.Vector; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.TileEntityBlock; +import com.sk89q.worldedit.world.DataException; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; public class TileEntityBaseBlock extends BaseBlock implements TileEntityBlock { - private TileEntity tile; - - public TileEntity getTile() { - return this.tile; - } - - public void setTile(TileEntity tile) { - this.tile = tile; - } public TileEntityBaseBlock(int type, int data, TileEntity tile) { super(type, data); - this.tile = tile; + try { + setNbtData(NBTConverter.fromNative(copyNbtData(tile))); + } catch (DataException ignored) { + } } - private static NBTTagCompound getNewData(Vector pt, TileEntityBaseBlock block) { + private static NBTTagCompound copyNbtData(TileEntity tile) { NBTTagCompound tag = new NBTTagCompound(); - - block.tile.writeToNBT(tag); - - tag.setTag("x", new NBTTagInt("x", pt.getBlockX())); - tag.setTag("y", new NBTTagInt("y", pt.getBlockY())); - tag.setTag("z", new NBTTagInt("z", pt.getBlockZ())); - + tile.writeToNBT(tag); return tag; } - public static void set(World world, Vector pt, TileEntityBaseBlock block) { - // TODO somehow decide if we want to soft or hard copy based on block type - set(world, pt, block, true); - } - - public static void set(World world, Vector pt, TileEntityBaseBlock block, boolean hardcopy) { - TileEntity newTE = constructTEClass(world, pt, block); - if (newTE == null) { - return; - } - if (hardcopy) { - // this causes issues with certain TEs - NBTTagCompound newTag = getNewData(pt, block); - newTE.readFromNBT(newTag); - } - world.setBlockTileEntity(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ(), newTE); - } - - private static TileEntity constructTEClass(World world, Vector pt, TileEntityBaseBlock block) { - Class clazz = block.tile.getClass(); - Constructor baseConstructor; - try { - baseConstructor = clazz.getConstructor(); // creates "blank" TE - } catch (Throwable e) { - return null; // every TE *should* have this constructor, so this isn't necessary - } - TileEntity genericTE; - try { - // downcast here for return while retaining the type - genericTE = (TileEntity) baseConstructor.newInstance(); - } catch (Throwable e) { - return null; - } - /* - genericTE.blockType = Block.blocksList[block.getId()]; - genericTE.blockMetadata = block.getData(); - genericTE.xCoord = pt.getBlockX(); - genericTE.yCoord = pt.getBlockY(); - genericTE.zCoord = pt.getBlockZ(); - genericTE.worldObj = world; - */ // handled by internal code - return genericTE; - } - - public String getNbtId() { - NBTTagCompound tag = new NBTTagCompound(); - try { - this.tile.writeToNBT(tag); - } catch (Exception e) { - return ""; - } - return tag.getString("id"); - } } \ No newline at end of file diff --git a/src/forge/java/com/sk89q/worldedit/forge/TileEntityUtils.java b/src/forge/java/com/sk89q/worldedit/forge/TileEntityUtils.java new file mode 100644 index 000000000..701369061 --- /dev/null +++ b/src/forge/java/com/sk89q/worldedit/forge/TileEntityUtils.java @@ -0,0 +1,142 @@ +/* + * 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.forge; + +import com.sk89q.worldedit.Vector; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagInt; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import java.lang.reflect.Constructor; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility methods for setting tile entities in the world. + */ +class TileEntityUtils { + + private TileEntityUtils() { + } + + /** + * Update the given tag compound with position information. + * + * @param tag the tag + * @param position the position + * @return a tag compound + */ + private static NBTTagCompound updateForSet(NBTTagCompound tag, Vector position) { + checkNotNull(tag); + checkNotNull(position); + + tag.setTag("x", new NBTTagInt("x", position.getBlockX())); + tag.setTag("y", new NBTTagInt("y", position.getBlockY())); + tag.setTag("z", new NBTTagInt("z", position.getBlockZ())); + + return tag; + } + + /** + * Set a tile entity at the given location. + * + * @param world the world + * @param position the position + * @param clazz the tile entity class + * @param tag the tag for the tile entity (may be null to not set NBT data) + */ + static void setTileEntity(World world, Vector position, Class clazz, @Nullable NBTTagCompound tag) { + checkNotNull(world); + checkNotNull(position); + checkNotNull(clazz); + + TileEntity tileEntity = constructTileEntity(world, position, clazz); + + if (tileEntity == null) { + return; + } + + if (tag != null) { + // Set X, Y, Z + updateForSet(tag, position); + tileEntity.readFromNBT(tag); + } + + world.setBlockTileEntity(position.getBlockX(), position.getBlockY(), position.getBlockZ(), tileEntity); + } + + /** + * Set a tile entity at the given location using the tile entity ID from + * the tag. + * + * @param world the world + * @param position the position + * @param tag the tag for the tile entity (may be null to do nothing) + */ + static void setTileEntity(World world, Vector position, @Nullable NBTTagCompound tag) { + if (tag != null) { + updateForSet(tag, position); + TileEntity tileEntity = TileEntity.createAndLoadEntity(tag); + if (tileEntity != null) { + world.setBlockTileEntity(position.getBlockX(), position.getBlockY(), position.getBlockZ(), tileEntity); + } + } + } + + /** + * Construct a tile entity from the given class. + * + * @param world the world + * @param position the position + * @param clazz the class + * @return a tile entity (may be null if it failed) + */ + static @Nullable TileEntity constructTileEntity(World world, Vector position, Class clazz) { + Constructor baseConstructor; + try { + baseConstructor = clazz.getConstructor(); // creates "blank" TE + } catch (Throwable e) { + return null; // every TE *should* have this constructor, so this isn't necessary + } + + TileEntity genericTE; + try { + // Downcast here for return while retaining the type + genericTE = (TileEntity) baseConstructor.newInstance(); + } catch (Throwable e) { + return null; + } + + /* + genericTE.blockType = Block.blocksList[block.getId()]; + genericTE.blockMetadata = block.getData(); + genericTE.xCoord = pt.getBlockX(); + genericTE.yCoord = pt.getBlockY(); + genericTE.zCoord = pt.getBlockZ(); + genericTE.worldObj = world; + */ // handled by internal code + + return genericTE; + } + + +}