[Forge] Made TileEntity NBT handling fit WE spec.

Fixed schematic loading/saving as a result.
This commit is contained in:
sk89q 2014-04-06 01:41:28 -07:00
parent f3e107da90
commit 099fd9a63b
4 changed files with 413 additions and 124 deletions

View File

@ -19,36 +19,23 @@
package com.sk89q.worldedit.forge; package com.sk89q.worldedit.forge;
import java.lang.ref.WeakReference; import com.sk89q.jnbt.CompoundTag;
import java.lang.reflect.Field; import com.sk89q.worldedit.*;
import java.util.Iterator; import com.sk89q.worldedit.blocks.*;
import java.util.List; import com.sk89q.worldedit.foundation.Block;
import java.util.Set; import com.sk89q.worldedit.regions.Region;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityHanging; import net.minecraft.entity.EntityHanging;
import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.IProjectile; import net.minecraft.entity.IProjectile;
import net.minecraft.entity.item.EntityBoat; import net.minecraft.entity.item.*;
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.monster.EntityGolem; import net.minecraft.entity.monster.EntityGolem;
import net.minecraft.entity.passive.EntityAmbientCreature; import net.minecraft.entity.passive.EntityAmbientCreature;
import net.minecraft.entity.passive.EntityAnimal; import net.minecraft.entity.passive.EntityAnimal;
import net.minecraft.entity.passive.EntityTameable; import net.minecraft.entity.passive.EntityTameable;
import net.minecraft.entity.passive.EntityVillager; import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.inventory.IInventory; import net.minecraft.inventory.IInventory;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.*;
import net.minecraft.tileentity.TileEntityMobSpawner;
import net.minecraft.tileentity.TileEntityNote;
import net.minecraft.tileentity.TileEntitySign;
import net.minecraft.tileentity.TileEntitySkull;
import net.minecraft.util.LongHashMap; import net.minecraft.util.LongHashMap;
import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.World; 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.chunk.IChunkProvider;
import net.minecraft.world.gen.ChunkProviderServer; import net.minecraft.world.gen.ChunkProviderServer;
import com.sk89q.worldedit.BiomeType; import java.lang.ref.WeakReference;
import com.sk89q.worldedit.EditSession; import java.lang.reflect.Field;
import com.sk89q.worldedit.EntityType; import java.util.Iterator;
import com.sk89q.worldedit.LocalWorld; import java.util.List;
import com.sk89q.worldedit.Vector; import java.util.Set;
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;
public class ForgeWorld extends LocalWorld { public class ForgeWorld extends LocalWorld {
private WeakReference<World> world; private WeakReference<World> world;
@ -255,8 +232,6 @@ public class ForgeWorld extends LocalWorld {
TileEntity tile = this.world.get().getBlockTileEntity(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); TileEntity tile = this.world.get().getBlockTileEntity(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (tile != null) { if (tile != null) {
TileEntityBaseBlock block = new TileEntityBaseBlock(type, data, tile); TileEntityBaseBlock block = new TileEntityBaseBlock(type, data, tile);
copyFromWorld(pt, block);
return block; return block;
} }
return new BaseBlock(type, data); return new BaseBlock(type, data);
@ -266,7 +241,7 @@ public class ForgeWorld extends LocalWorld {
return copyToWorld(pt, block, true); 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)) { if (!(block instanceof TileEntityBlock)) {
return false; return false;
} }
@ -303,20 +278,18 @@ public class ForgeWorld extends LocalWorld {
return true; return true;
} }
if (block instanceof TileEntityBaseBlock) { CompoundTag tag = block.getNbtData();
TileEntityBaseBlock.set(this.world.get(), pt, (TileEntityBaseBlock) block, hardcopy); if (tag != null) {
TileEntityUtils.setTileEntity(this.world.get(), pt, NBTConverter.toNative(tag));
return true; return true;
} }
return false; return false;
} }
public boolean copyFromWorld(Vector pt, BaseBlock block) { public boolean copyFromWorld(Vector pt, BaseBlock block) {
if (!(block instanceof TileEntityBaseBlock)) {
return false; return false;
} }
((TileEntityBaseBlock) block).setTile(this.world.get().getBlockTileEntity(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()));
return true;
}
public boolean clearContainerBlockContents(Vector pt) { public boolean clearContainerBlockContents(Vector pt) {
TileEntity tile = this.world.get().getBlockTileEntity(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); TileEntity tile = this.world.get().getBlockTileEntity(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());

View File

@ -0,0 +1,244 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, Tag> 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<Tag> list = new ArrayList<Tag>();
Class<? extends Tag> 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<NBTBase> tags = other.getTags();
Map<String, Tag> map = new HashMap<String, Tag>();
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);
}
}

View File

@ -19,96 +19,26 @@
package com.sk89q.worldedit.forge; 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.BaseBlock;
import com.sk89q.worldedit.blocks.TileEntityBlock; 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 { 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) { public TileEntityBaseBlock(int type, int data, TileEntity tile) {
super(type, data); 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(); NBTTagCompound tag = new NBTTagCompound();
tile.writeToNBT(tag);
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()));
return 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<? extends TileEntity> clazz = block.tile.getClass();
Constructor<? extends TileEntity> 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");
}
} }

View File

@ -0,0 +1,142 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<? extends TileEntity> 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<? extends TileEntity> clazz) {
Constructor<? extends TileEntity> 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;
}
}