mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-23 01:37:37 +00:00
Support different NMS block classes depending on version.
- The .class files in the contrib folder of the zip go in plugins/WorldEdit/nmsblocks - This allows us to swap new class files in without releasing a completely new version each time - Whatever version the last release is for has an inbuilt fallback - If the plugin and server are mismatched and you have nothing in nmsblocks you're screwed
This commit is contained in:
parent
83c71f30a8
commit
ad349aecb1
BIN
contrib/nmsblocks/CBXNmsBlock_146.class
Normal file
BIN
contrib/nmsblocks/CBXNmsBlock_146.class
Normal file
Binary file not shown.
442
contrib/nmsblocks/CBXNmsBlock_146.java
Normal file
442
contrib/nmsblocks/CBXNmsBlock_146.java
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
// $Id$
|
||||||
|
/*
|
||||||
|
* This file is a part of WorldEdit.
|
||||||
|
* Copyright (c) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (c) the 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
|
||||||
|
* (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
|
||||||
|
* GNU 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import net.minecraft.server.v1_4_6.NBTBase;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagByte;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagByteArray;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagCompound;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagDouble;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagEnd;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagFloat;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagInt;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagIntArray;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagList;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagLong;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagShort;
|
||||||
|
import net.minecraft.server.v1_4_6.NBTTagString;
|
||||||
|
import net.minecraft.server.v1_4_6.TileEntity;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.craftbukkit.v1_4_6.CraftWorld;
|
||||||
|
|
||||||
|
import com.sk89q.jnbt.ByteArrayTag;
|
||||||
|
import com.sk89q.jnbt.ByteTag;
|
||||||
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
|
import com.sk89q.jnbt.DoubleTag;
|
||||||
|
import com.sk89q.jnbt.EndTag;
|
||||||
|
import com.sk89q.jnbt.FloatTag;
|
||||||
|
import com.sk89q.jnbt.IntArrayTag;
|
||||||
|
import com.sk89q.jnbt.IntTag;
|
||||||
|
import com.sk89q.jnbt.ListTag;
|
||||||
|
import com.sk89q.jnbt.LongTag;
|
||||||
|
import com.sk89q.jnbt.NBTConstants;
|
||||||
|
import com.sk89q.jnbt.ShortTag;
|
||||||
|
import com.sk89q.jnbt.StringTag;
|
||||||
|
import com.sk89q.jnbt.Tag;
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
|
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||||
|
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||||
|
import com.sk89q.worldedit.bukkit.NmsBlock;
|
||||||
|
import com.sk89q.worldedit.data.DataException;
|
||||||
|
import com.sk89q.worldedit.foundation.Block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This version needs to be built on CraftBukkit 1.4.6 R0.1 or higher
|
||||||
|
*/
|
||||||
|
public class CBXNmsBlock_146 extends NmsBlock {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(CBXNmsBlock_146.class.getCanonicalName());
|
||||||
|
private static Field compoundMapField;
|
||||||
|
private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why.
|
||||||
|
private NBTTagCompound nbtData = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Field field;
|
||||||
|
try {
|
||||||
|
field = net.minecraft.server.v1_4_6.Block.class.getDeclaredField("isTileEntity");
|
||||||
|
field.setAccessible(true);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// logger.severe("Could not find NMS block tile entity field!");
|
||||||
|
field = null;
|
||||||
|
}
|
||||||
|
nmsBlock_isTileEntityField = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean verify() {
|
||||||
|
return nmsBlock_isTileEntityField != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with a given type ID, data value, and previous
|
||||||
|
* {@link TileEntityBlock}-implementing object.
|
||||||
|
*
|
||||||
|
* @param type block type ID
|
||||||
|
* @param data data value
|
||||||
|
* @param tileEntityBlock tile entity block
|
||||||
|
*/
|
||||||
|
public CBXNmsBlock_146(int type, int data, TileEntityBlock tileEntityBlock) {
|
||||||
|
super(type, data);
|
||||||
|
|
||||||
|
nbtData = (NBTTagCompound) fromNative(tileEntityBlock.getNbtData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with a given type ID, data value, and raw
|
||||||
|
* {@link NBTTagCompound} copy.
|
||||||
|
*
|
||||||
|
* @param type block type ID
|
||||||
|
* @param data data value
|
||||||
|
* @param nbtData raw NBT data
|
||||||
|
*/
|
||||||
|
public CBXNmsBlock_146(int type, int data, NBTTagCompound nbtData) {
|
||||||
|
super(type, data);
|
||||||
|
|
||||||
|
this.nbtData = nbtData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a {@link NBTTagCompound} that has valid coordinates.
|
||||||
|
*
|
||||||
|
* @param pt coordinates to set
|
||||||
|
* @return the tag compound
|
||||||
|
*/
|
||||||
|
private NBTTagCompound getNmsData(Vector pt) {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
nbtData.set("x", new NBTTagInt("x", pt.getBlockX()));
|
||||||
|
nbtData.set("y", new NBTTagInt("y", pt.getBlockY()));
|
||||||
|
nbtData.set("z", new NBTTagInt("z", pt.getBlockZ()));
|
||||||
|
|
||||||
|
return nbtData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNbtData() {
|
||||||
|
return nbtData != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNbtId() {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return nbtData.getString("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getNbtData() {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return new CompoundTag(getNbtId(),
|
||||||
|
new HashMap<String, Tag>());
|
||||||
|
}
|
||||||
|
return (CompoundTag) toNative(nbtData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNbtData(CompoundTag tag) throws DataException {
|
||||||
|
if (tag == null) {
|
||||||
|
this.nbtData = null;
|
||||||
|
}
|
||||||
|
this.nbtData = (NBTTagCompound) fromNative(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an instance from the given information.
|
||||||
|
*
|
||||||
|
* @param world world to get the block from
|
||||||
|
* @param position position to get the block at
|
||||||
|
* @param type type ID of block
|
||||||
|
* @param data data value of block
|
||||||
|
* @return the block, or null
|
||||||
|
*/
|
||||||
|
public static CBXNmsBlock_146 get(World world, Vector position, int type, int data) {
|
||||||
|
if (!hasTileEntity(type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
|
||||||
|
position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
||||||
|
|
||||||
|
if (te != null) {
|
||||||
|
NBTTagCompound tag = new NBTTagCompound();
|
||||||
|
te.b(tag); // Load data
|
||||||
|
return new CBXNmsBlock_146(type, data, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an instance or a {@link TileEntityBlock} to the given position.
|
||||||
|
*
|
||||||
|
* @param world world to set the block in
|
||||||
|
* @param position position to set the block at
|
||||||
|
* @param block the block to set
|
||||||
|
* @return true if tile entity data was copied to the world
|
||||||
|
*/
|
||||||
|
public static boolean set(World world, Vector position, BaseBlock block) {
|
||||||
|
NBTTagCompound data = null;
|
||||||
|
if (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block instanceof CBXNmsBlock_146) {
|
||||||
|
CBXNmsBlock_146 nmsProxyBlock = (CBXNmsBlock_146) block;
|
||||||
|
data = nmsProxyBlock.getNmsData(position);
|
||||||
|
} else if (block instanceof TileEntityBlock) {
|
||||||
|
CBXNmsBlock_146 nmsProxyBlock = new CBXNmsBlock_146(
|
||||||
|
block.getId(), block.getData(), block);
|
||||||
|
data = nmsProxyBlock.getNmsData(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null) {
|
||||||
|
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
|
||||||
|
position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
||||||
|
if (te != null) {
|
||||||
|
te.a(data); // Load data
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to set a block 'safely', as in setting the block data to the location, and
|
||||||
|
* then triggering physics only at the end.
|
||||||
|
*
|
||||||
|
* @param world world to set the block in
|
||||||
|
* @param position position to set the block at
|
||||||
|
* @param block the block to set
|
||||||
|
* @param notifyAdjacent true to notify physics and what not
|
||||||
|
* @return true if block id or data was changed
|
||||||
|
*/
|
||||||
|
public static boolean setSafely(BukkitWorld world, Vector position,
|
||||||
|
Block block, boolean notifyAdjacent) {
|
||||||
|
|
||||||
|
int x = position.getBlockX();
|
||||||
|
int y = position.getBlockY();
|
||||||
|
int z = position.getBlockZ();
|
||||||
|
|
||||||
|
CraftWorld craftWorld = ((CraftWorld) world.getWorld());
|
||||||
|
|
||||||
|
boolean changed = craftWorld.getHandle().setRawTypeIdAndData(
|
||||||
|
x, y, z, block.getId(), block.getData());
|
||||||
|
|
||||||
|
if (block instanceof BaseBlock) {
|
||||||
|
world.copyToWorld(position, (BaseBlock) block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
if (notifyAdjacent) {
|
||||||
|
craftWorld.getHandle().update(x, y, z, block.getId());
|
||||||
|
} else {
|
||||||
|
craftWorld.getHandle().notify(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasTileEntity(int type) {
|
||||||
|
net.minecraft.server.v1_4_6.Block nmsBlock = getNmsBlock(type);
|
||||||
|
if (nmsBlock == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static net.minecraft.server.v1_4_6.Block getNmsBlock(int type) {
|
||||||
|
if (type < 0 || type >= net.minecraft.server.v1_4_6.Block.byId.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return net.minecraft.server.v1_4_6.Block.byId[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
|
||||||
|
* structure.
|
||||||
|
*
|
||||||
|
* @param foreign non-native NMS NBT structure
|
||||||
|
* @return native WorldEdit NBT structure
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Tag toNative(NBTBase foreign) {
|
||||||
|
if (foreign == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (foreign instanceof NBTTagCompound) {
|
||||||
|
Map<String, Tag> values = new HashMap<String, Tag>();
|
||||||
|
Collection<Object> foreignValues = null;
|
||||||
|
|
||||||
|
if (compoundMapField == null) {
|
||||||
|
try {
|
||||||
|
// Method name may change!
|
||||||
|
foreignValues = ((NBTTagCompound) foreign).c();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
try {
|
||||||
|
logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " +
|
||||||
|
"so we're going to try to get at the 'map' field directly from now on");
|
||||||
|
|
||||||
|
if (compoundMapField == null) {
|
||||||
|
compoundMapField = NBTTagCompound.class.getDeclaredField("map");
|
||||||
|
compoundMapField.setAccessible(true);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Can't do much beyond this
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compoundMapField != null) {
|
||||||
|
try {
|
||||||
|
foreignValues = ((HashMap<Object, Object>) compoundMapField.get(foreign)).values();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Can't do much beyond this
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object obj : foreignValues) {
|
||||||
|
NBTBase base = (NBTBase) obj;
|
||||||
|
values.put(base.getName(), toNative(base));
|
||||||
|
}
|
||||||
|
return new CompoundTag(foreign.getName(), values);
|
||||||
|
} else if (foreign instanceof NBTTagByte) {
|
||||||
|
return new ByteTag(foreign.getName(), ((NBTTagByte) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagByteArray) {
|
||||||
|
return new ByteArrayTag(foreign.getName(),
|
||||||
|
((NBTTagByteArray) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagDouble) {
|
||||||
|
return new DoubleTag(foreign.getName(),
|
||||||
|
((NBTTagDouble) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagFloat) {
|
||||||
|
return new FloatTag(foreign.getName(), ((NBTTagFloat) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagInt) {
|
||||||
|
return new IntTag(foreign.getName(), ((NBTTagInt) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagIntArray) {
|
||||||
|
return new IntArrayTag(foreign.getName(),
|
||||||
|
((NBTTagIntArray) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagList) {
|
||||||
|
List<Tag> values = new ArrayList<Tag>();
|
||||||
|
NBTTagList foreignList = (NBTTagList) foreign;
|
||||||
|
int type = NBTConstants.TYPE_BYTE;
|
||||||
|
for (int i = 0; i < foreignList.size(); i++) {
|
||||||
|
NBTBase foreignTag = foreignList.get(i);
|
||||||
|
values.add(toNative(foreignTag));
|
||||||
|
type = foreignTag.getTypeId();
|
||||||
|
}
|
||||||
|
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
|
||||||
|
return new ListTag(foreign.getName(), cls, values);
|
||||||
|
} else if (foreign instanceof NBTTagLong) {
|
||||||
|
return new LongTag(foreign.getName(), ((NBTTagLong) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagShort) {
|
||||||
|
return new ShortTag(foreign.getName(), ((NBTTagShort) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagString) {
|
||||||
|
return new StringTag(foreign.getName(),
|
||||||
|
((NBTTagString) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagEnd) {
|
||||||
|
return new EndTag();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to make native "
|
||||||
|
+ foreign.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a WorldEdit-native NBT structure to a NMS structure.
|
||||||
|
*
|
||||||
|
* @param foreign structure to convert
|
||||||
|
* @return non-native structure
|
||||||
|
*/
|
||||||
|
private static NBTBase fromNative(Tag foreign) {
|
||||||
|
if (foreign == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (foreign instanceof CompoundTag) {
|
||||||
|
NBTTagCompound tag = new NBTTagCompound(foreign.getName());
|
||||||
|
for (Map.Entry<String, Tag> entry : ((CompoundTag) foreign)
|
||||||
|
.getValue().entrySet()) {
|
||||||
|
tag.set(entry.getKey(), fromNative(entry.getValue()));
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
} else if (foreign instanceof ByteTag) {
|
||||||
|
return new NBTTagByte(foreign.getName(),
|
||||||
|
((ByteTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ByteArrayTag) {
|
||||||
|
return new NBTTagByteArray(foreign.getName(),
|
||||||
|
((ByteArrayTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof DoubleTag) {
|
||||||
|
return new NBTTagDouble(foreign.getName(),
|
||||||
|
((DoubleTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof FloatTag) {
|
||||||
|
return new NBTTagFloat(foreign.getName(),
|
||||||
|
((FloatTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof IntTag) {
|
||||||
|
return new NBTTagInt(foreign.getName(),
|
||||||
|
((IntTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof IntArrayTag) {
|
||||||
|
return new NBTTagIntArray(foreign.getName(),
|
||||||
|
((IntArrayTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ListTag) {
|
||||||
|
NBTTagList tag = new NBTTagList(foreign.getName());
|
||||||
|
ListTag foreignList = (ListTag) foreign;
|
||||||
|
for (Tag t : foreignList.getValue()) {
|
||||||
|
tag.add(fromNative(t));
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
} else if (foreign instanceof LongTag) {
|
||||||
|
return new NBTTagLong(foreign.getName(),
|
||||||
|
((LongTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ShortTag) {
|
||||||
|
return new NBTTagShort(foreign.getName(),
|
||||||
|
((ShortTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof StringTag) {
|
||||||
|
return new NBTTagString(foreign.getName(),
|
||||||
|
((StringTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof EndTag) {
|
||||||
|
return new NBTTagEnd();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to make NMS "
|
||||||
|
+ foreign.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidBlockType(int type) throws NoClassDefFoundError {
|
||||||
|
return type == 0 || (type >= 1 && type < net.minecraft.server.v1_4_6.Block.byId.length
|
||||||
|
&& net.minecraft.server.v1_4_6.Block.byId[type] != null);
|
||||||
|
}
|
||||||
|
}
|
BIN
contrib/nmsblocks/MCPCPXNmsBlock.class
Normal file
BIN
contrib/nmsblocks/MCPCPXNmsBlock.class
Normal file
Binary file not shown.
449
contrib/nmsblocks/MCPCPXNmsBlock.java
Normal file
449
contrib/nmsblocks/MCPCPXNmsBlock.java
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
// $Id$
|
||||||
|
/*
|
||||||
|
* This file is a part of WorldEdit.
|
||||||
|
* Copyright (c) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (c) the 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
|
||||||
|
* (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
|
||||||
|
* GNU 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTBase;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagByte;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagByteArray;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagCompound;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagDouble;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagEnd;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagFloat;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagInt;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagIntArray;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagList;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagLong;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagShort;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagString;
|
||||||
|
import net.minecraft.server.v1_4_R1.TileEntity;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.craftbukkit.v1_4_R1.CraftWorld;
|
||||||
|
|
||||||
|
import com.sk89q.jnbt.ByteArrayTag;
|
||||||
|
import com.sk89q.jnbt.ByteTag;
|
||||||
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
|
import com.sk89q.jnbt.DoubleTag;
|
||||||
|
import com.sk89q.jnbt.EndTag;
|
||||||
|
import com.sk89q.jnbt.FloatTag;
|
||||||
|
import com.sk89q.jnbt.IntArrayTag;
|
||||||
|
import com.sk89q.jnbt.IntTag;
|
||||||
|
import com.sk89q.jnbt.ListTag;
|
||||||
|
import com.sk89q.jnbt.LongTag;
|
||||||
|
import com.sk89q.jnbt.NBTConstants;
|
||||||
|
import com.sk89q.jnbt.ShortTag;
|
||||||
|
import com.sk89q.jnbt.StringTag;
|
||||||
|
import com.sk89q.jnbt.Tag;
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
|
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||||
|
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||||
|
import com.sk89q.worldedit.bukkit.NmsBlock;
|
||||||
|
import com.sk89q.worldedit.data.DataException;
|
||||||
|
import com.sk89q.worldedit.foundation.Block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class needs to be obfuscated and compiled in a forge environment
|
||||||
|
*/
|
||||||
|
public class MCPCPXNmsBlock extends NmsBlock {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(MCPCPXNmsBlock.class.getCanonicalName());
|
||||||
|
private static Field compoundMapField;
|
||||||
|
private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why.
|
||||||
|
private NBTTagCompound nbtData = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Field field;
|
||||||
|
try {
|
||||||
|
// this part is important, has to be updated manually whenever it changes
|
||||||
|
field = net.minecraft.server.v1_4_R1.Block.class.getDeclaredField("cs"); // isTileEntity or isContainerBlock
|
||||||
|
field.setAccessible(true);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// logger.severe("Could not find NMS block tile entity field!");
|
||||||
|
field = null;
|
||||||
|
}
|
||||||
|
nmsBlock_isTileEntityField = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean verify() {
|
||||||
|
try {
|
||||||
|
Class.forName("org.bukkit.craftbukkit.v1_4_R1.CraftWorld");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return nmsBlock_isTileEntityField != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with a given type ID, data value, and previous
|
||||||
|
* {@link TileEntityBlock}-implementing object.
|
||||||
|
*
|
||||||
|
* @param type block type ID
|
||||||
|
* @param data data value
|
||||||
|
* @param tileEntityBlock tile entity block
|
||||||
|
*/
|
||||||
|
public MCPCPXNmsBlock(int type, int data, TileEntityBlock tileEntityBlock) {
|
||||||
|
super(type, data);
|
||||||
|
|
||||||
|
nbtData = (NBTTagCompound) fromNative(tileEntityBlock.getNbtData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with a given type ID, data value, and raw
|
||||||
|
* {@link NBTTagCompound} copy.
|
||||||
|
*
|
||||||
|
* @param type block type ID
|
||||||
|
* @param data data value
|
||||||
|
* @param nbtData raw NBT data
|
||||||
|
*/
|
||||||
|
public MCPCPXNmsBlock(int type, int data, NBTTagCompound nbtData) {
|
||||||
|
super(type, data);
|
||||||
|
|
||||||
|
this.nbtData = nbtData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a {@link NBTTagCompound} that has valid coordinates.
|
||||||
|
*
|
||||||
|
* @param pt coordinates to set
|
||||||
|
* @return the tag compound
|
||||||
|
*/
|
||||||
|
private NBTTagCompound getNmsData(Vector pt) {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
nbtData.set("x", new NBTTagInt("x", pt.getBlockX()));
|
||||||
|
nbtData.set("y", new NBTTagInt("y", pt.getBlockY()));
|
||||||
|
nbtData.set("z", new NBTTagInt("z", pt.getBlockZ()));
|
||||||
|
|
||||||
|
return nbtData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNbtData() {
|
||||||
|
return nbtData != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNbtId() {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return nbtData.getString("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getNbtData() {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return new CompoundTag(getNbtId(),
|
||||||
|
new HashMap<String, Tag>());
|
||||||
|
}
|
||||||
|
return (CompoundTag) toNative(nbtData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNbtData(CompoundTag tag) throws DataException {
|
||||||
|
if (tag == null) {
|
||||||
|
this.nbtData = null;
|
||||||
|
}
|
||||||
|
this.nbtData = (NBTTagCompound) fromNative(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an instance from the given information.
|
||||||
|
*
|
||||||
|
* @param world world to get the block from
|
||||||
|
* @param position position to get the block at
|
||||||
|
* @param type type ID of block
|
||||||
|
* @param data data value of block
|
||||||
|
* @return the block, or null
|
||||||
|
*/
|
||||||
|
public static MCPCPXNmsBlock get(World world, Vector position, int type, int data) {
|
||||||
|
if (!hasTileEntity(type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
|
||||||
|
position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
||||||
|
|
||||||
|
if (te != null) {
|
||||||
|
NBTTagCompound tag = new NBTTagCompound();
|
||||||
|
te.b(tag); // Load data
|
||||||
|
return new MCPCPXNmsBlock(type, data, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an instance or a {@link TileEntityBlock} to the given position.
|
||||||
|
*
|
||||||
|
* @param world world to set the block in
|
||||||
|
* @param position position to set the block at
|
||||||
|
* @param block the block to set
|
||||||
|
* @return true if tile entity data was copied to the world
|
||||||
|
*/
|
||||||
|
public static boolean set(World world, Vector position, BaseBlock block) {
|
||||||
|
NBTTagCompound data = null;
|
||||||
|
if (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block instanceof MCPCPXNmsBlock) {
|
||||||
|
MCPCPXNmsBlock nmsProxyBlock = (MCPCPXNmsBlock) block;
|
||||||
|
data = nmsProxyBlock.getNmsData(position);
|
||||||
|
} else if (block instanceof TileEntityBlock) {
|
||||||
|
MCPCPXNmsBlock nmsProxyBlock = new MCPCPXNmsBlock(
|
||||||
|
block.getId(), block.getData(), block);
|
||||||
|
data = nmsProxyBlock.getNmsData(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null) {
|
||||||
|
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
|
||||||
|
position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
||||||
|
if (te != null) {
|
||||||
|
te.a(data); // Load data
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to set a block 'safely', as in setting the block data to the location, and
|
||||||
|
* then triggering physics only at the end.
|
||||||
|
*
|
||||||
|
* @param world world to set the block in
|
||||||
|
* @param position position to set the block at
|
||||||
|
* @param block the block to set
|
||||||
|
* @param notifyAdjacent true to notify physics and what not
|
||||||
|
* @return true if block id or data was changed
|
||||||
|
*/
|
||||||
|
public static boolean setSafely(BukkitWorld world, Vector position,
|
||||||
|
Block block, boolean notifyAdjacent) {
|
||||||
|
|
||||||
|
int x = position.getBlockX();
|
||||||
|
int y = position.getBlockY();
|
||||||
|
int z = position.getBlockZ();
|
||||||
|
|
||||||
|
CraftWorld craftWorld = ((CraftWorld) world.getWorld());
|
||||||
|
|
||||||
|
boolean changed = craftWorld.getHandle().setRawTypeIdAndData(
|
||||||
|
x, y, z, block.getId(), block.getData());
|
||||||
|
|
||||||
|
if (block instanceof BaseBlock) {
|
||||||
|
world.copyToWorld(position, (BaseBlock) block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
if (notifyAdjacent) {
|
||||||
|
craftWorld.getHandle().update(x, y, z, block.getId());
|
||||||
|
} else {
|
||||||
|
craftWorld.getHandle().notify(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasTileEntity(int type) {
|
||||||
|
net.minecraft.server.v1_4_R1.Block nmsBlock = getNmsBlock(type);
|
||||||
|
if (nmsBlock == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static net.minecraft.server.v1_4_R1.Block getNmsBlock(int type) {
|
||||||
|
if (type < 0 || type >= net.minecraft.server.v1_4_R1.Block.byId.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return net.minecraft.server.v1_4_R1.Block.byId[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
|
||||||
|
* structure.
|
||||||
|
*
|
||||||
|
* @param foreign non-native NMS NBT structure
|
||||||
|
* @return native WorldEdit NBT structure
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Tag toNative(NBTBase foreign) {
|
||||||
|
if (foreign == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (foreign instanceof NBTTagCompound) {
|
||||||
|
Map<String, Tag> values = new HashMap<String, Tag>();
|
||||||
|
Collection<Object> foreignValues = null;
|
||||||
|
|
||||||
|
if (compoundMapField == null) {
|
||||||
|
try {
|
||||||
|
// Method name may change!
|
||||||
|
foreignValues = ((NBTTagCompound) foreign).c();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
try {
|
||||||
|
// this shouldn't happen here since we are reobfuscating to the correct name
|
||||||
|
logger.warning("WorldEdit: Couldn't get NBTTagCompound.getLists(), " +
|
||||||
|
"so we're going to try to get at the 'map' field directly from now on");
|
||||||
|
|
||||||
|
if (compoundMapField == null) {
|
||||||
|
compoundMapField = NBTTagCompound.class.getDeclaredField("map");
|
||||||
|
compoundMapField.setAccessible(true);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Can't do much beyond this
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compoundMapField != null) {
|
||||||
|
try {
|
||||||
|
foreignValues = ((HashMap<Object, Object>) compoundMapField.get(foreign)).values();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Can't do much beyond this
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object obj : foreignValues) {
|
||||||
|
NBTBase base = (NBTBase) obj;
|
||||||
|
values.put(base.getName(), toNative(base));
|
||||||
|
}
|
||||||
|
return new CompoundTag(foreign.getName(), values);
|
||||||
|
} else if (foreign instanceof NBTTagByte) {
|
||||||
|
return new ByteTag(foreign.getName(), ((NBTTagByte) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagByteArray) {
|
||||||
|
return new ByteArrayTag(foreign.getName(),
|
||||||
|
((NBTTagByteArray) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagDouble) {
|
||||||
|
return new DoubleTag(foreign.getName(),
|
||||||
|
((NBTTagDouble) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagFloat) {
|
||||||
|
return new FloatTag(foreign.getName(), ((NBTTagFloat) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagInt) {
|
||||||
|
return new IntTag(foreign.getName(), ((NBTTagInt) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagIntArray) {
|
||||||
|
return new IntArrayTag(foreign.getName(),
|
||||||
|
((NBTTagIntArray) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagList) {
|
||||||
|
List<Tag> values = new ArrayList<Tag>();
|
||||||
|
NBTTagList foreignList = (NBTTagList) foreign;
|
||||||
|
int type = NBTConstants.TYPE_BYTE;
|
||||||
|
for (int i = 0; i < foreignList.size(); i++) {
|
||||||
|
NBTBase foreignTag = foreignList.get(i);
|
||||||
|
values.add(toNative(foreignTag));
|
||||||
|
type = foreignTag.getTypeId();
|
||||||
|
}
|
||||||
|
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
|
||||||
|
return new ListTag(foreign.getName(), cls, values);
|
||||||
|
} else if (foreign instanceof NBTTagLong) {
|
||||||
|
return new LongTag(foreign.getName(), ((NBTTagLong) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagShort) {
|
||||||
|
return new ShortTag(foreign.getName(), ((NBTTagShort) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagString) {
|
||||||
|
return new StringTag(foreign.getName(),
|
||||||
|
((NBTTagString) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagEnd) {
|
||||||
|
return new EndTag();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to make native "
|
||||||
|
+ foreign.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a WorldEdit-native NBT structure to a NMS structure.
|
||||||
|
*
|
||||||
|
* @param foreign structure to convert
|
||||||
|
* @return non-native structure
|
||||||
|
*/
|
||||||
|
private static NBTBase fromNative(Tag foreign) {
|
||||||
|
if (foreign == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (foreign instanceof CompoundTag) {
|
||||||
|
NBTTagCompound tag = new NBTTagCompound(foreign.getName());
|
||||||
|
for (Map.Entry<String, Tag> entry : ((CompoundTag) foreign)
|
||||||
|
.getValue().entrySet()) {
|
||||||
|
tag.set(entry.getKey(), fromNative(entry.getValue()));
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
} else if (foreign instanceof ByteTag) {
|
||||||
|
return new NBTTagByte(foreign.getName(),
|
||||||
|
((ByteTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ByteArrayTag) {
|
||||||
|
return new NBTTagByteArray(foreign.getName(),
|
||||||
|
((ByteArrayTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof DoubleTag) {
|
||||||
|
return new NBTTagDouble(foreign.getName(),
|
||||||
|
((DoubleTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof FloatTag) {
|
||||||
|
return new NBTTagFloat(foreign.getName(),
|
||||||
|
((FloatTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof IntTag) {
|
||||||
|
return new NBTTagInt(foreign.getName(),
|
||||||
|
((IntTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof IntArrayTag) {
|
||||||
|
return new NBTTagIntArray(foreign.getName(),
|
||||||
|
((IntArrayTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ListTag) {
|
||||||
|
NBTTagList tag = new NBTTagList(foreign.getName());
|
||||||
|
ListTag foreignList = (ListTag) foreign;
|
||||||
|
for (Tag t : foreignList.getValue()) {
|
||||||
|
tag.add(fromNative(t));
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
} else if (foreign instanceof LongTag) {
|
||||||
|
return new NBTTagLong(foreign.getName(),
|
||||||
|
((LongTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ShortTag) {
|
||||||
|
return new NBTTagShort(foreign.getName(),
|
||||||
|
((ShortTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof StringTag) {
|
||||||
|
return new NBTTagString(foreign.getName(),
|
||||||
|
((StringTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof EndTag) {
|
||||||
|
return new NBTTagEnd();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to make NMS "
|
||||||
|
+ foreign.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidBlockType(int type) throws NoClassDefFoundError {
|
||||||
|
return type == 0 || (type >= 1 && type < net.minecraft.server.v1_4_R1.Block.byId.length
|
||||||
|
&& net.minecraft.server.v1_4_R1.Block.byId[type] != null);
|
||||||
|
}
|
||||||
|
}
|
2
pom.xml
2
pom.xml
@ -90,7 +90,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bukkit</groupId>
|
<groupId>org.bukkit</groupId>
|
||||||
<artifactId>craftbukkit</artifactId>
|
<artifactId>craftbukkit</artifactId>
|
||||||
<version>1.4.6-R0.1</version>
|
<version>1.4.7-R0.1</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
<include>LICENSE.txt</include>
|
<include>LICENSE.txt</include>
|
||||||
<include>CHANGELOG.txt</include>
|
<include>CHANGELOG.txt</include>
|
||||||
<include>contrib/craftscripts/*</include>
|
<include>contrib/craftscripts/*</include>
|
||||||
|
<include>contrib/nmsblocks/*.class</include>
|
||||||
</includes>
|
</includes>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
</fileSets>
|
</fileSets>
|
||||||
|
@ -19,6 +19,11 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.bukkit;
|
package com.sk89q.worldedit.bukkit;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -30,6 +35,7 @@ import java.util.UUID;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Effect;
|
import org.bukkit.Effect;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@ -67,6 +73,7 @@ import org.bukkit.entity.Villager;
|
|||||||
import org.bukkit.inventory.DoubleChestInventory;
|
import org.bukkit.inventory.DoubleChestInventory;
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import com.sk89q.worldedit.BiomeType;
|
import com.sk89q.worldedit.BiomeType;
|
||||||
import com.sk89q.worldedit.BlockVector2D;
|
import com.sk89q.worldedit.BlockVector2D;
|
||||||
@ -92,18 +99,128 @@ import com.sk89q.worldedit.util.TreeGenerator;
|
|||||||
|
|
||||||
public class BukkitWorld extends LocalWorld {
|
public class BukkitWorld extends LocalWorld {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(BukkitWorld.class.getCanonicalName());
|
private static final Logger logger = WorldEdit.logger;
|
||||||
private World world;
|
private World world;
|
||||||
private boolean skipNmsAccess = false;
|
private boolean skipNmsAccess = false;
|
||||||
private boolean skipNmsSafeSet = false;
|
private boolean skipNmsSafeSet = false;
|
||||||
private boolean skipNmsValidBlockCheck = false;
|
private boolean skipNmsValidBlockCheck = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* holder for the nmsblock class that we should use
|
||||||
|
*/
|
||||||
|
private static Class<? extends NmsBlock> nmsBlockType;
|
||||||
|
private static Method nmsSetMethod;
|
||||||
|
private static Method nmsValidBlockMethod;
|
||||||
|
private static Method nmsGetMethod;
|
||||||
|
private static Method nmsSetSafeMethod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct the object.
|
* Construct the object.
|
||||||
* @param world
|
* @param world
|
||||||
*/
|
*/
|
||||||
public BukkitWorld(World world) {
|
public BukkitWorld(World world) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
|
|
||||||
|
// check if we have a class we can use for nms access
|
||||||
|
if (nmsBlockType != null) return;
|
||||||
|
Plugin plugin = Bukkit.getPluginManager().getPlugin("WorldEdit");
|
||||||
|
if (!(plugin instanceof WorldEditPlugin)) return; // hopefully never happens
|
||||||
|
WorldEditPlugin wePlugin = ((WorldEditPlugin) plugin);
|
||||||
|
File nmsBlocksDir = new File(wePlugin.getDataFolder() + File.separator + "nmsblocks" + File.separator);
|
||||||
|
if (nmsBlocksDir.listFiles() == null) { // no files to use
|
||||||
|
skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
NmsBlockClassLoader loader = new NmsBlockClassLoader(BukkitWorld.class.getClassLoader(), nmsBlocksDir);
|
||||||
|
String filename;
|
||||||
|
for (File f : nmsBlocksDir.listFiles()) {
|
||||||
|
if (!f.isFile()) continue;
|
||||||
|
filename = f.getName();
|
||||||
|
Class<?> testBlock = loader.loadClass("CL-NMS" + filename);
|
||||||
|
filename = filename.replaceFirst(".class$", ""); // get rid of extension
|
||||||
|
if (NmsBlock.class.isAssignableFrom(testBlock)) {
|
||||||
|
// got a NmsBlock, test it now
|
||||||
|
Class<? extends NmsBlock> nmsClass = (Class<? extends NmsBlock>) testBlock;
|
||||||
|
boolean canUse = false;
|
||||||
|
try {
|
||||||
|
canUse = (Boolean) nmsClass.getMethod("verify", null).invoke(null, null);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!canUse) continue; // not for this server
|
||||||
|
nmsBlockType = nmsClass;
|
||||||
|
nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class);
|
||||||
|
nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class);
|
||||||
|
nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class);
|
||||||
|
nmsSetSafeMethod = nmsBlockType.getMethod("setSafely",
|
||||||
|
BukkitWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class);
|
||||||
|
// phew
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nmsBlockType != null) {
|
||||||
|
// logger.info("Found nms block class, using: " + nmsBlockType);
|
||||||
|
} else {
|
||||||
|
// try our default
|
||||||
|
try {
|
||||||
|
nmsBlockType = (Class<? extends NmsBlock>) Class.forName("com.sk89q.worldedit.bukkit.DefaultNmsBlock");
|
||||||
|
boolean canUse = (Boolean) nmsBlockType.getMethod("verify", null).invoke(null, null);
|
||||||
|
if (canUse) {
|
||||||
|
nmsSetMethod = nmsBlockType.getMethod("set", World.class, Vector.class, BaseBlock.class);
|
||||||
|
nmsValidBlockMethod = nmsBlockType.getMethod("isValidBlockType", int.class);
|
||||||
|
nmsGetMethod = nmsBlockType.getMethod("get", World.class, Vector.class, int.class, int.class);
|
||||||
|
nmsSetSafeMethod = nmsBlockType.getMethod("setSafely",
|
||||||
|
BukkitWorld.class, Vector.class, com.sk89q.worldedit.foundation.Block.class, boolean.class);
|
||||||
|
logger.info("[WorldEdit] Using inbuilt NmsBlock for this version of WorldEdit.");
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// OMG DEVS WAI U NO SUPPORT <xyz> SERVER
|
||||||
|
skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
|
||||||
|
logger.warning("[WorldEdit] No compatible nms block class found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.warning("[WorldEdit] Unable to load NmsBlock classes, make sure they are installed correctly.");
|
||||||
|
e.printStackTrace();
|
||||||
|
skipNmsAccess = true; skipNmsSafeSet = true; skipNmsValidBlockCheck = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NmsBlockClassLoader extends ClassLoader {
|
||||||
|
public File searchDir;
|
||||||
|
public NmsBlockClassLoader(ClassLoader parent, File searchDir) {
|
||||||
|
super(parent);
|
||||||
|
this.searchDir = searchDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||||
|
if (!name.startsWith("CL-NMS")) {
|
||||||
|
return super.loadClass(name);
|
||||||
|
} else {
|
||||||
|
name = name.replace("CL-NMS", ""); // hacky lol
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
URL url = new File(searchDir, name).toURI().toURL();
|
||||||
|
InputStream input = url.openConnection().getInputStream();
|
||||||
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
int data = input.read();
|
||||||
|
while (data != -1) {
|
||||||
|
buffer.write(data);
|
||||||
|
data = input.read();
|
||||||
|
}
|
||||||
|
input.close();
|
||||||
|
|
||||||
|
byte[] classData = buffer.toByteArray();
|
||||||
|
|
||||||
|
return defineClass(name.replaceFirst(".class$", ""), classData, 0, classData.length);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new ClassNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -455,7 +572,7 @@ public class BukkitWorld extends LocalWorld {
|
|||||||
|
|
||||||
if (!skipNmsAccess) {
|
if (!skipNmsAccess) {
|
||||||
try {
|
try {
|
||||||
return NmsBlock.set(world, pt, block);
|
return (Boolean) nmsSetMethod.invoke(null, world, pt, block);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.log(Level.WARNING, "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
|
logger.log(Level.WARNING, "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
|
||||||
skipNmsAccess = true;
|
skipNmsAccess = true;
|
||||||
@ -1064,7 +1181,7 @@ public class BukkitWorld extends LocalWorld {
|
|||||||
public boolean isValidBlockType(int type) {
|
public boolean isValidBlockType(int type) {
|
||||||
if (!skipNmsValidBlockCheck) {
|
if (!skipNmsValidBlockCheck) {
|
||||||
try {
|
try {
|
||||||
return NmsBlock.isValidBlockType(type);
|
return (Boolean) nmsValidBlockMethod.invoke(null, type);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
skipNmsValidBlockCheck = true;
|
skipNmsValidBlockCheck = true;
|
||||||
}
|
}
|
||||||
@ -1180,7 +1297,8 @@ public class BukkitWorld extends LocalWorld {
|
|||||||
default:
|
default:
|
||||||
if (!skipNmsAccess) {
|
if (!skipNmsAccess) {
|
||||||
try {
|
try {
|
||||||
NmsBlock block = NmsBlock.get(world, pt, type, data);
|
NmsBlock block = null;
|
||||||
|
block = (NmsBlock) nmsGetMethod.invoke(null, getWorld(), pt, type, data);
|
||||||
if (block != null) {
|
if (block != null) {
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
@ -1199,10 +1317,9 @@ public class BukkitWorld extends LocalWorld {
|
|||||||
public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) {
|
public boolean setBlock(Vector pt, com.sk89q.worldedit.foundation.Block block, boolean notifyAdjacent) {
|
||||||
if (!skipNmsSafeSet) {
|
if (!skipNmsSafeSet) {
|
||||||
try {
|
try {
|
||||||
return NmsBlock.setSafely(this, pt, block, notifyAdjacent);
|
return (Boolean) nmsSetSafeMethod.invoke(null, this, pt, block, notifyAdjacent);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.log(Level.WARNING,
|
logger.log(Level.WARNING, "WorldEdit: Failed to do NMS safe block set", t);
|
||||||
"WorldEdit: Failed to do NMS safe block set", t);
|
|
||||||
skipNmsSafeSet = true;
|
skipNmsSafeSet = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
446
src/main/java/com/sk89q/worldedit/bukkit/DefaultNmsBlock.java
Normal file
446
src/main/java/com/sk89q/worldedit/bukkit/DefaultNmsBlock.java
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
package com.sk89q.worldedit.bukkit;
|
||||||
|
// $Id$
|
||||||
|
/*
|
||||||
|
* This file is a part of WorldEdit.
|
||||||
|
* Copyright (c) sk89q <http://www.sk89q.com>
|
||||||
|
* Copyright (c) the 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
|
||||||
|
* (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
|
||||||
|
* GNU 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTBase;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagByte;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagByteArray;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagCompound;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagDouble;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagEnd;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagFloat;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagInt;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagIntArray;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagList;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagLong;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagShort;
|
||||||
|
import net.minecraft.server.v1_4_R1.NBTTagString;
|
||||||
|
import net.minecraft.server.v1_4_R1.TileEntity;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.craftbukkit.v1_4_R1.CraftWorld;
|
||||||
|
|
||||||
|
import com.sk89q.jnbt.ByteArrayTag;
|
||||||
|
import com.sk89q.jnbt.ByteTag;
|
||||||
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
|
import com.sk89q.jnbt.DoubleTag;
|
||||||
|
import com.sk89q.jnbt.EndTag;
|
||||||
|
import com.sk89q.jnbt.FloatTag;
|
||||||
|
import com.sk89q.jnbt.IntArrayTag;
|
||||||
|
import com.sk89q.jnbt.IntTag;
|
||||||
|
import com.sk89q.jnbt.ListTag;
|
||||||
|
import com.sk89q.jnbt.LongTag;
|
||||||
|
import com.sk89q.jnbt.NBTConstants;
|
||||||
|
import com.sk89q.jnbt.ShortTag;
|
||||||
|
import com.sk89q.jnbt.StringTag;
|
||||||
|
import com.sk89q.jnbt.Tag;
|
||||||
|
import com.sk89q.worldedit.Vector;
|
||||||
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||||
|
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||||
|
import com.sk89q.worldedit.data.DataException;
|
||||||
|
import com.sk89q.worldedit.foundation.Block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A blind handler of blocks with TileEntity data that directly access Minecraft's
|
||||||
|
* classes through CraftBukkit.
|
||||||
|
* </p>
|
||||||
|
* Usage of this class may break terribly in the future, and therefore usage should
|
||||||
|
* be trapped in a handler for {@link Throwable}.
|
||||||
|
*/
|
||||||
|
public class DefaultNmsBlock extends NmsBlock {
|
||||||
|
|
||||||
|
private static final Logger logger = WorldEdit.logger;
|
||||||
|
private static Field compoundMapField;
|
||||||
|
private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why.
|
||||||
|
private NBTTagCompound nbtData = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Field field;
|
||||||
|
try {
|
||||||
|
field = net.minecraft.server.v1_4_R1.Block.class.getDeclaredField("isTileEntity");
|
||||||
|
field.setAccessible(true);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// logger.severe("Could not find NMS block tile entity field!");
|
||||||
|
field = null;
|
||||||
|
}
|
||||||
|
nmsBlock_isTileEntityField = field;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean verify() {
|
||||||
|
return nmsBlock_isTileEntityField != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with a given type ID, data value, and previous
|
||||||
|
* {@link TileEntityBlock}-implementing object.
|
||||||
|
*
|
||||||
|
* @param type block type ID
|
||||||
|
* @param data data value
|
||||||
|
* @param tileEntityBlock tile entity block
|
||||||
|
*/
|
||||||
|
public DefaultNmsBlock(int type, int data, TileEntityBlock tileEntityBlock) {
|
||||||
|
super(type, data);
|
||||||
|
|
||||||
|
nbtData = (NBTTagCompound) fromNative(tileEntityBlock.getNbtData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with a given type ID, data value, and raw
|
||||||
|
* {@link NBTTagCompound} copy.
|
||||||
|
*
|
||||||
|
* @param type block type ID
|
||||||
|
* @param data data value
|
||||||
|
* @param nbtData raw NBT data
|
||||||
|
*/
|
||||||
|
public DefaultNmsBlock(int type, int data, NBTTagCompound nbtData) {
|
||||||
|
super(type, data);
|
||||||
|
|
||||||
|
this.nbtData = nbtData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a {@link NBTTagCompound} that has valid coordinates.
|
||||||
|
*
|
||||||
|
* @param pt coordinates to set
|
||||||
|
* @return the tag compound
|
||||||
|
*/
|
||||||
|
private NBTTagCompound getNmsData(Vector pt) {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
nbtData.set("x", new NBTTagInt("x", pt.getBlockX()));
|
||||||
|
nbtData.set("y", new NBTTagInt("y", pt.getBlockY()));
|
||||||
|
nbtData.set("z", new NBTTagInt("z", pt.getBlockZ()));
|
||||||
|
|
||||||
|
return nbtData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNbtData() {
|
||||||
|
return nbtData != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNbtId() {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return nbtData.getString("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getNbtData() {
|
||||||
|
if (nbtData == null) {
|
||||||
|
return new CompoundTag(getNbtId(),
|
||||||
|
new HashMap<String, Tag>());
|
||||||
|
}
|
||||||
|
return (CompoundTag) toNative(nbtData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNbtData(CompoundTag tag) throws DataException {
|
||||||
|
if (tag == null) {
|
||||||
|
this.nbtData = null;
|
||||||
|
}
|
||||||
|
this.nbtData = (NBTTagCompound) fromNative(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an instance from the given information.
|
||||||
|
*
|
||||||
|
* @param world world to get the block from
|
||||||
|
* @param position position to get the block at
|
||||||
|
* @param type type ID of block
|
||||||
|
* @param data data value of block
|
||||||
|
* @return the block, or null
|
||||||
|
*/
|
||||||
|
public static DefaultNmsBlock get(World world, Vector position, int type, int data) {
|
||||||
|
if (!hasTileEntity(type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
|
||||||
|
position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
||||||
|
|
||||||
|
if (te != null) {
|
||||||
|
NBTTagCompound tag = new NBTTagCompound();
|
||||||
|
te.b(tag); // Load data
|
||||||
|
return new DefaultNmsBlock(type, data, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an instance or a {@link TileEntityBlock} to the given position.
|
||||||
|
*
|
||||||
|
* @param world world to set the block in
|
||||||
|
* @param position position to set the block at
|
||||||
|
* @param block the block to set
|
||||||
|
* @return true if tile entity data was copied to the world
|
||||||
|
*/
|
||||||
|
public static boolean set(World world, Vector position, BaseBlock block) {
|
||||||
|
NBTTagCompound data = null;
|
||||||
|
if (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block instanceof DefaultNmsBlock) {
|
||||||
|
DefaultNmsBlock nmsProxyBlock = (DefaultNmsBlock) block;
|
||||||
|
data = nmsProxyBlock.getNmsData(position);
|
||||||
|
} else if (block instanceof TileEntityBlock) {
|
||||||
|
DefaultNmsBlock nmsProxyBlock = new DefaultNmsBlock(
|
||||||
|
block.getId(), block.getData(), block);
|
||||||
|
data = nmsProxyBlock.getNmsData(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null) {
|
||||||
|
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
|
||||||
|
position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
||||||
|
if (te != null) {
|
||||||
|
te.a(data); // Load data
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to set a block 'safely', as in setting the block data to the location, and
|
||||||
|
* then triggering physics only at the end.
|
||||||
|
*
|
||||||
|
* @param world world to set the block in
|
||||||
|
* @param position position to set the block at
|
||||||
|
* @param block the block to set
|
||||||
|
* @param notifyAdjacent true to notify physics and what not
|
||||||
|
* @return true if block id or data was changed
|
||||||
|
*/
|
||||||
|
public static boolean setSafely(BukkitWorld world, Vector position,
|
||||||
|
Block block, boolean notifyAdjacent) {
|
||||||
|
|
||||||
|
int x = position.getBlockX();
|
||||||
|
int y = position.getBlockY();
|
||||||
|
int z = position.getBlockZ();
|
||||||
|
|
||||||
|
CraftWorld craftWorld = ((CraftWorld) world.getWorld());
|
||||||
|
|
||||||
|
boolean changed = craftWorld.getHandle().setRawTypeIdAndData(
|
||||||
|
x, y, z, block.getId(), block.getData());
|
||||||
|
|
||||||
|
if (block instanceof BaseBlock) {
|
||||||
|
world.copyToWorld(position, (BaseBlock) block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
if (notifyAdjacent) {
|
||||||
|
craftWorld.getHandle().update(x, y, z, block.getId());
|
||||||
|
} else {
|
||||||
|
craftWorld.getHandle().notify(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasTileEntity(int type) {
|
||||||
|
net.minecraft.server.v1_4_R1.Block nmsBlock = getNmsBlock(type);
|
||||||
|
if (nmsBlock == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static net.minecraft.server.v1_4_R1.Block getNmsBlock(int type) {
|
||||||
|
if (type < 0 || type >= net.minecraft.server.v1_4_R1.Block.byId.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return net.minecraft.server.v1_4_R1.Block.byId[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
|
||||||
|
* structure.
|
||||||
|
*
|
||||||
|
* @param foreign non-native NMS NBT structure
|
||||||
|
* @return native WorldEdit NBT structure
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Tag toNative(NBTBase foreign) {
|
||||||
|
if (foreign == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (foreign instanceof NBTTagCompound) {
|
||||||
|
Map<String, Tag> values = new HashMap<String, Tag>();
|
||||||
|
Collection<Object> foreignValues = null;
|
||||||
|
|
||||||
|
if (compoundMapField == null) {
|
||||||
|
try {
|
||||||
|
// Method name may change!
|
||||||
|
foreignValues = ((NBTTagCompound) foreign).c();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
try {
|
||||||
|
logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " +
|
||||||
|
"so we're going to try to get at the 'map' field directly from now on");
|
||||||
|
|
||||||
|
if (compoundMapField == null) {
|
||||||
|
compoundMapField = NBTTagCompound.class.getDeclaredField("map");
|
||||||
|
compoundMapField.setAccessible(true);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Can't do much beyond this
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compoundMapField != null) {
|
||||||
|
try {
|
||||||
|
foreignValues = ((HashMap<Object, Object>) compoundMapField.get(foreign)).values();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Can't do much beyond this
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object obj : foreignValues) {
|
||||||
|
NBTBase base = (NBTBase) obj;
|
||||||
|
values.put(base.getName(), toNative(base));
|
||||||
|
}
|
||||||
|
return new CompoundTag(foreign.getName(), values);
|
||||||
|
} else if (foreign instanceof NBTTagByte) {
|
||||||
|
return new ByteTag(foreign.getName(), ((NBTTagByte) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagByteArray) {
|
||||||
|
return new ByteArrayTag(foreign.getName(),
|
||||||
|
((NBTTagByteArray) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagDouble) {
|
||||||
|
return new DoubleTag(foreign.getName(),
|
||||||
|
((NBTTagDouble) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagFloat) {
|
||||||
|
return new FloatTag(foreign.getName(), ((NBTTagFloat) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagInt) {
|
||||||
|
return new IntTag(foreign.getName(), ((NBTTagInt) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagIntArray) {
|
||||||
|
return new IntArrayTag(foreign.getName(),
|
||||||
|
((NBTTagIntArray) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagList) {
|
||||||
|
List<Tag> values = new ArrayList<Tag>();
|
||||||
|
NBTTagList foreignList = (NBTTagList) foreign;
|
||||||
|
int type = NBTConstants.TYPE_BYTE;
|
||||||
|
for (int i = 0; i < foreignList.size(); i++) {
|
||||||
|
NBTBase foreignTag = foreignList.get(i);
|
||||||
|
values.add(toNative(foreignTag));
|
||||||
|
type = foreignTag.getTypeId();
|
||||||
|
}
|
||||||
|
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
|
||||||
|
return new ListTag(foreign.getName(), cls, values);
|
||||||
|
} else if (foreign instanceof NBTTagLong) {
|
||||||
|
return new LongTag(foreign.getName(), ((NBTTagLong) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagShort) {
|
||||||
|
return new ShortTag(foreign.getName(), ((NBTTagShort) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagString) {
|
||||||
|
return new StringTag(foreign.getName(),
|
||||||
|
((NBTTagString) foreign).data);
|
||||||
|
} else if (foreign instanceof NBTTagEnd) {
|
||||||
|
return new EndTag();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to make native "
|
||||||
|
+ foreign.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a WorldEdit-native NBT structure to a NMS structure.
|
||||||
|
*
|
||||||
|
* @param foreign structure to convert
|
||||||
|
* @return non-native structure
|
||||||
|
*/
|
||||||
|
private static NBTBase fromNative(Tag foreign) {
|
||||||
|
if (foreign == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (foreign instanceof CompoundTag) {
|
||||||
|
NBTTagCompound tag = new NBTTagCompound(foreign.getName());
|
||||||
|
for (Map.Entry<String, Tag> entry : ((CompoundTag) foreign)
|
||||||
|
.getValue().entrySet()) {
|
||||||
|
tag.set(entry.getKey(), fromNative(entry.getValue()));
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
} else if (foreign instanceof ByteTag) {
|
||||||
|
return new NBTTagByte(foreign.getName(),
|
||||||
|
((ByteTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ByteArrayTag) {
|
||||||
|
return new NBTTagByteArray(foreign.getName(),
|
||||||
|
((ByteArrayTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof DoubleTag) {
|
||||||
|
return new NBTTagDouble(foreign.getName(),
|
||||||
|
((DoubleTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof FloatTag) {
|
||||||
|
return new NBTTagFloat(foreign.getName(),
|
||||||
|
((FloatTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof IntTag) {
|
||||||
|
return new NBTTagInt(foreign.getName(),
|
||||||
|
((IntTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof IntArrayTag) {
|
||||||
|
return new NBTTagIntArray(foreign.getName(),
|
||||||
|
((IntArrayTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ListTag) {
|
||||||
|
NBTTagList tag = new NBTTagList(foreign.getName());
|
||||||
|
ListTag foreignList = (ListTag) foreign;
|
||||||
|
for (Tag t : foreignList.getValue()) {
|
||||||
|
tag.add(fromNative(t));
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
} else if (foreign instanceof LongTag) {
|
||||||
|
return new NBTTagLong(foreign.getName(),
|
||||||
|
((LongTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof ShortTag) {
|
||||||
|
return new NBTTagShort(foreign.getName(),
|
||||||
|
((ShortTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof StringTag) {
|
||||||
|
return new NBTTagString(foreign.getName(),
|
||||||
|
((StringTag) foreign).getValue());
|
||||||
|
} else if (foreign instanceof EndTag) {
|
||||||
|
return new NBTTagEnd();
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Don't know how to make NMS "
|
||||||
|
+ foreign.getClass().getCanonicalName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValidBlockType(int type) throws NoClassDefFoundError {
|
||||||
|
return type == 0 || (type >= 1 && type < net.minecraft.server.v1_4_R1.Block.byId.length
|
||||||
|
&& net.minecraft.server.v1_4_R1.Block.byId[type] != null);
|
||||||
|
}
|
||||||
|
}
|
@ -1,442 +1,42 @@
|
|||||||
// $Id$
|
|
||||||
/*
|
|
||||||
* This file is a part of WorldEdit.
|
|
||||||
* Copyright (c) sk89q <http://www.sk89q.com>
|
|
||||||
* Copyright (c) the 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
|
|
||||||
* (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
|
|
||||||
* GNU 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.bukkit;
|
package com.sk89q.worldedit.bukkit;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import net.minecraft.server.v1_4_6.NBTBase;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagByte;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagByteArray;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagCompound;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagDouble;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagEnd;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagFloat;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagInt;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagIntArray;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagList;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagLong;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagShort;
|
|
||||||
import net.minecraft.server.v1_4_6.NBTTagString;
|
|
||||||
import net.minecraft.server.v1_4_6.TileEntity;
|
|
||||||
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.v1_4_6.CraftWorld;
|
|
||||||
|
|
||||||
import com.sk89q.jnbt.ByteArrayTag;
|
|
||||||
import com.sk89q.jnbt.ByteTag;
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
|
||||||
import com.sk89q.jnbt.DoubleTag;
|
|
||||||
import com.sk89q.jnbt.EndTag;
|
|
||||||
import com.sk89q.jnbt.FloatTag;
|
|
||||||
import com.sk89q.jnbt.IntArrayTag;
|
|
||||||
import com.sk89q.jnbt.IntTag;
|
|
||||||
import com.sk89q.jnbt.ListTag;
|
|
||||||
import com.sk89q.jnbt.LongTag;
|
|
||||||
import com.sk89q.jnbt.NBTConstants;
|
|
||||||
import com.sk89q.jnbt.ShortTag;
|
|
||||||
import com.sk89q.jnbt.StringTag;
|
|
||||||
import com.sk89q.jnbt.Tag;
|
|
||||||
import com.sk89q.worldedit.Vector;
|
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.data.DataException;
|
|
||||||
import com.sk89q.worldedit.foundation.Block;
|
import com.sk89q.worldedit.foundation.Block;
|
||||||
|
|
||||||
/**
|
public abstract class NmsBlock extends BaseBlock {
|
||||||
* A blind handler of blocks with TileEntity data that directly access Minecraft's
|
|
||||||
* classes through CraftBukkit.
|
|
||||||
* </p>
|
|
||||||
* Usage of this class may break terribly in the future, and therefore usage should
|
|
||||||
* be trapped in a handler for {@link Throwable}.
|
|
||||||
*/
|
|
||||||
class NmsBlock extends BaseBlock implements TileEntityBlock {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(NmsBlock.class.getCanonicalName());
|
protected NmsBlock(int type) {
|
||||||
private static Field compoundMapField;
|
super(type);
|
||||||
private static final Field nmsBlock_isTileEntityField; // The field is deobfuscated but the method isn't. No idea why.
|
|
||||||
private NBTTagCompound nbtData = null;
|
|
||||||
|
|
||||||
static {
|
|
||||||
Field field;
|
|
||||||
try {
|
|
||||||
field = net.minecraft.server.v1_4_6.Block.class.getDeclaredField("isTileEntity");
|
|
||||||
field.setAccessible(true);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
logger.severe("Could not find NMS block tile entity field!");
|
|
||||||
field = null;
|
|
||||||
}
|
|
||||||
nmsBlock_isTileEntityField = field;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected NmsBlock(int type, int data) {
|
||||||
* Create a new instance with a given type ID, data value, and previous
|
|
||||||
* {@link TileEntityBlock}-implementing object.
|
|
||||||
*
|
|
||||||
* @param type block type ID
|
|
||||||
* @param data data value
|
|
||||||
* @param tileEntityBlock tile entity block
|
|
||||||
*/
|
|
||||||
public NmsBlock(int type, int data, TileEntityBlock tileEntityBlock) {
|
|
||||||
super(type, data);
|
super(type, data);
|
||||||
|
|
||||||
nbtData = (NBTTagCompound) fromNative(tileEntityBlock.getNbtData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static boolean verify() {
|
||||||
* Create a new instance with a given type ID, data value, and raw
|
|
||||||
* {@link NBTTagCompound} copy.
|
|
||||||
*
|
|
||||||
* @param type block type ID
|
|
||||||
* @param data data value
|
|
||||||
* @param nbtData raw NBT data
|
|
||||||
*/
|
|
||||||
public NmsBlock(int type, int data, NBTTagCompound nbtData) {
|
|
||||||
super(type, data);
|
|
||||||
|
|
||||||
this.nbtData = nbtData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a {@link NBTTagCompound} that has valid coordinates.
|
|
||||||
*
|
|
||||||
* @param pt coordinates to set
|
|
||||||
* @return the tag compound
|
|
||||||
*/
|
|
||||||
private NBTTagCompound getNmsData(Vector pt) {
|
|
||||||
if (nbtData == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
nbtData.set("x", new NBTTagInt("x", pt.getBlockX()));
|
|
||||||
nbtData.set("y", new NBTTagInt("y", pt.getBlockY()));
|
|
||||||
nbtData.set("z", new NBTTagInt("z", pt.getBlockZ()));
|
|
||||||
|
|
||||||
return nbtData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNbtData() {
|
|
||||||
return nbtData != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getNbtId() {
|
|
||||||
if (nbtData == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return nbtData.getString("id");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getNbtData() {
|
|
||||||
if (nbtData == null) {
|
|
||||||
return new CompoundTag(getNbtId(),
|
|
||||||
new HashMap<String, Tag>());
|
|
||||||
}
|
|
||||||
return (CompoundTag) toNative(nbtData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setNbtData(CompoundTag tag) throws DataException {
|
|
||||||
if (tag == null) {
|
|
||||||
this.nbtData = null;
|
|
||||||
}
|
|
||||||
this.nbtData = (NBTTagCompound) fromNative(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build an instance from the given information.
|
|
||||||
*
|
|
||||||
* @param world world to get the block from
|
|
||||||
* @param position position to get the block at
|
|
||||||
* @param type type ID of block
|
|
||||||
* @param data data value of block
|
|
||||||
* @return the block, or null
|
|
||||||
*/
|
|
||||||
public static NmsBlock get(World world, Vector position, int type, int data) {
|
|
||||||
if (!hasTileEntity(type)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
|
|
||||||
position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
|
||||||
|
|
||||||
if (te != null) {
|
|
||||||
NBTTagCompound tag = new NBTTagCompound();
|
|
||||||
te.b(tag); // Load data
|
|
||||||
return new NmsBlock(type, data, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set an instance or a {@link TileEntityBlock} to the given position.
|
|
||||||
*
|
|
||||||
* @param world world to set the block in
|
|
||||||
* @param position position to set the block at
|
|
||||||
* @param block the block to set
|
|
||||||
* @return true if tile entity data was copied to the world
|
|
||||||
*/
|
|
||||||
public static boolean set(World world, Vector position, BaseBlock block) {
|
|
||||||
NBTTagCompound data = null;
|
|
||||||
if (!hasTileEntity(world.getBlockTypeIdAt(position.getBlockX(), position.getBlockY(), position.getBlockZ()))) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block instanceof NmsBlock) {
|
public static NmsBlock get(World world, Vector vector, int type, int data) {
|
||||||
NmsBlock nmsProxyBlock = (NmsBlock) block;
|
return null;
|
||||||
data = nmsProxyBlock.getNmsData(position);
|
|
||||||
} else if (block instanceof TileEntityBlock) {
|
|
||||||
NmsBlock nmsProxyBlock = new NmsBlock(
|
|
||||||
block.getType(), block.getData(), block);
|
|
||||||
data = nmsProxyBlock.getNmsData(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
TileEntity te = ((CraftWorld) world).getHandle().getTileEntity(
|
|
||||||
position.getBlockX(), position.getBlockY(), position.getBlockZ());
|
|
||||||
if (te != null) {
|
|
||||||
te.a(data); // Load data
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean set(World world, Vector vector, Block block) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static boolean setSafely(World world, Vector vector, Block block, boolean notify) {
|
||||||
* Tries to set a block 'safely', as in setting the block data to the location, and
|
return false;
|
||||||
* then triggering physics only at the end.
|
|
||||||
*
|
|
||||||
* @param world world to set the block in
|
|
||||||
* @param position position to set the block at
|
|
||||||
* @param block the block to set
|
|
||||||
* @param notifyAdjacent true to notify physics and what not
|
|
||||||
* @return true if block id or data was changed
|
|
||||||
*/
|
|
||||||
public static boolean setSafely(BukkitWorld world, Vector position,
|
|
||||||
Block block, boolean notifyAdjacent) {
|
|
||||||
|
|
||||||
int x = position.getBlockX();
|
|
||||||
int y = position.getBlockY();
|
|
||||||
int z = position.getBlockZ();
|
|
||||||
|
|
||||||
CraftWorld craftWorld = ((CraftWorld) world.getWorld());
|
|
||||||
|
|
||||||
boolean changed = craftWorld.getHandle().setRawTypeIdAndData(
|
|
||||||
x, y, z, block.getId(), block.getData());
|
|
||||||
|
|
||||||
if (block instanceof BaseBlock) {
|
|
||||||
world.copyToWorld(position, (BaseBlock) block);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
if (notifyAdjacent) {
|
|
||||||
craftWorld.getHandle().update(x, y, z, block.getId());
|
|
||||||
} else {
|
|
||||||
craftWorld.getHandle().notify(x, y, z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasTileEntity(int type) {
|
public static boolean hasTileEntity(int type) {
|
||||||
net.minecraft.server.v1_4_6.Block nmsBlock = getNmsBlock(type);
|
|
||||||
if (nmsBlock == null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
public static boolean isValidBlockType(int type) {
|
||||||
return nmsBlock_isTileEntityField.getBoolean(nmsBlock); // Once we have the field stord, gets are fast
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static net.minecraft.server.v1_4_6.Block getNmsBlock(int type) {
|
|
||||||
if (type < 0 || type >= net.minecraft.server.v1_4_6.Block.byId.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return net.minecraft.server.v1_4_6.Block.byId[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
|
|
||||||
* structure.
|
|
||||||
*
|
|
||||||
* @param foreign non-native NMS NBT structure
|
|
||||||
* @return native WorldEdit NBT structure
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private static Tag toNative(NBTBase foreign) {
|
|
||||||
if (foreign == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (foreign instanceof NBTTagCompound) {
|
|
||||||
Map<String, Tag> values = new HashMap<String, Tag>();
|
|
||||||
Collection<Object> foreignValues = null;
|
|
||||||
|
|
||||||
if (compoundMapField == null) {
|
|
||||||
try {
|
|
||||||
// Method name may change!
|
|
||||||
foreignValues = ((NBTTagCompound) foreign).c();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
try {
|
|
||||||
logger.warning("WorldEdit: Couldn't get NBTTagCompound.c(), " +
|
|
||||||
"so we're going to try to get at the 'map' field directly from now on");
|
|
||||||
|
|
||||||
if (compoundMapField == null) {
|
|
||||||
compoundMapField = NBTTagCompound.class.getDeclaredField("map");
|
|
||||||
compoundMapField.setAccessible(true);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// Can't do much beyond this
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compoundMapField != null) {
|
|
||||||
try {
|
|
||||||
foreignValues = ((HashMap<Object, Object>) compoundMapField.get(foreign)).values();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// Can't do much beyond this
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Object obj : foreignValues) {
|
|
||||||
NBTBase base = (NBTBase) obj;
|
|
||||||
values.put(base.getName(), toNative(base));
|
|
||||||
}
|
|
||||||
return new CompoundTag(foreign.getName(), values);
|
|
||||||
} else if (foreign instanceof NBTTagByte) {
|
|
||||||
return new ByteTag(foreign.getName(), ((NBTTagByte) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagByteArray) {
|
|
||||||
return new ByteArrayTag(foreign.getName(),
|
|
||||||
((NBTTagByteArray) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagDouble) {
|
|
||||||
return new DoubleTag(foreign.getName(),
|
|
||||||
((NBTTagDouble) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagFloat) {
|
|
||||||
return new FloatTag(foreign.getName(), ((NBTTagFloat) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagInt) {
|
|
||||||
return new IntTag(foreign.getName(), ((NBTTagInt) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagIntArray) {
|
|
||||||
return new IntArrayTag(foreign.getName(),
|
|
||||||
((NBTTagIntArray) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagList) {
|
|
||||||
List<Tag> values = new ArrayList<Tag>();
|
|
||||||
NBTTagList foreignList = (NBTTagList) foreign;
|
|
||||||
int type = NBTConstants.TYPE_BYTE;
|
|
||||||
for (int i = 0; i < foreignList.size(); i++) {
|
|
||||||
NBTBase foreignTag = foreignList.get(i);
|
|
||||||
values.add(toNative(foreignTag));
|
|
||||||
type = foreignTag.getTypeId();
|
|
||||||
}
|
|
||||||
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
|
|
||||||
return new ListTag(foreign.getName(), cls, values);
|
|
||||||
} else if (foreign instanceof NBTTagLong) {
|
|
||||||
return new LongTag(foreign.getName(), ((NBTTagLong) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagShort) {
|
|
||||||
return new ShortTag(foreign.getName(), ((NBTTagShort) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagString) {
|
|
||||||
return new StringTag(foreign.getName(),
|
|
||||||
((NBTTagString) foreign).data);
|
|
||||||
} else if (foreign instanceof NBTTagEnd) {
|
|
||||||
return new EndTag();
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Don't know how to make native "
|
|
||||||
+ foreign.getClass().getCanonicalName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a WorldEdit-native NBT structure to a NMS structure.
|
|
||||||
*
|
|
||||||
* @param foreign structure to convert
|
|
||||||
* @return non-native structure
|
|
||||||
*/
|
|
||||||
private static NBTBase fromNative(Tag foreign) {
|
|
||||||
if (foreign == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (foreign instanceof CompoundTag) {
|
|
||||||
NBTTagCompound tag = new NBTTagCompound(foreign.getName());
|
|
||||||
for (Map.Entry<String, Tag> entry : ((CompoundTag) foreign)
|
|
||||||
.getValue().entrySet()) {
|
|
||||||
tag.set(entry.getKey(), fromNative(entry.getValue()));
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
} else if (foreign instanceof ByteTag) {
|
|
||||||
return new NBTTagByte(foreign.getName(),
|
|
||||||
((ByteTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof ByteArrayTag) {
|
|
||||||
return new NBTTagByteArray(foreign.getName(),
|
|
||||||
((ByteArrayTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof DoubleTag) {
|
|
||||||
return new NBTTagDouble(foreign.getName(),
|
|
||||||
((DoubleTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof FloatTag) {
|
|
||||||
return new NBTTagFloat(foreign.getName(),
|
|
||||||
((FloatTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof IntTag) {
|
|
||||||
return new NBTTagInt(foreign.getName(),
|
|
||||||
((IntTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof IntArrayTag) {
|
|
||||||
return new NBTTagIntArray(foreign.getName(),
|
|
||||||
((IntArrayTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof ListTag) {
|
|
||||||
NBTTagList tag = new NBTTagList(foreign.getName());
|
|
||||||
ListTag foreignList = (ListTag) foreign;
|
|
||||||
for (Tag t : foreignList.getValue()) {
|
|
||||||
tag.add(fromNative(t));
|
|
||||||
}
|
|
||||||
return tag;
|
|
||||||
} else if (foreign instanceof LongTag) {
|
|
||||||
return new NBTTagLong(foreign.getName(),
|
|
||||||
((LongTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof ShortTag) {
|
|
||||||
return new NBTTagShort(foreign.getName(),
|
|
||||||
((ShortTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof StringTag) {
|
|
||||||
return new NBTTagString(foreign.getName(),
|
|
||||||
((StringTag) foreign).getValue());
|
|
||||||
} else if (foreign instanceof EndTag) {
|
|
||||||
return new NBTTagEnd();
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Don't know how to make NMS "
|
|
||||||
+ foreign.getClass().getCanonicalName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isValidBlockType(int type) throws NoClassDefFoundError {
|
|
||||||
return type == 0 || (type >= 1 && type < net.minecraft.server.v1_4_6.Block.byId.length
|
|
||||||
&& net.minecraft.server.v1_4_6.Block.byId[type] != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -28,6 +28,7 @@ import java.util.jar.JarFile;
|
|||||||
import java.util.logging.Handler;
|
import java.util.logging.Handler;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@ -95,6 +96,7 @@ public class WorldEditPlugin extends JavaPlugin {
|
|||||||
|
|
||||||
// Make the data folders that WorldEdit uses
|
// Make the data folders that WorldEdit uses
|
||||||
getDataFolder().mkdirs();
|
getDataFolder().mkdirs();
|
||||||
|
new File(getDataFolder() + File.separator + "nmsblocks").mkdir();
|
||||||
|
|
||||||
// Create the default configuration file
|
// Create the default configuration file
|
||||||
createDefaultConfiguration("config.yml");
|
createDefaultConfiguration("config.yml");
|
||||||
@ -110,15 +112,16 @@ public class WorldEditPlugin extends JavaPlugin {
|
|||||||
// Setup interfaces
|
// Setup interfaces
|
||||||
server = new BukkitServerInterface(this, getServer());
|
server = new BukkitServerInterface(this, getServer());
|
||||||
controller = new WorldEdit(server, config);
|
controller = new WorldEdit(server, config);
|
||||||
|
WorldEdit.getInstance().logger.setParent(Bukkit.getLogger());
|
||||||
api = new WorldEditAPI(this);
|
api = new WorldEditAPI(this);
|
||||||
getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this));
|
getServer().getMessenger().registerIncomingPluginChannel(this, CUI_PLUGIN_CHANNEL, new CUIChannelListener(this));
|
||||||
getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL);
|
getServer().getMessenger().registerOutgoingPluginChannel(this, CUI_PLUGIN_CHANNEL);
|
||||||
|
|
||||||
// Now we can register events!
|
// Now we can register events!
|
||||||
getServer().getPluginManager().registerEvents(new WorldEditListener(this), this);
|
getServer().getPluginManager().registerEvents(new WorldEditListener(this), this);
|
||||||
|
|
||||||
getServer().getScheduler().scheduleAsyncRepeatingTask(this,
|
getServer().getScheduler().scheduleAsyncRepeatingTask(this,
|
||||||
new SessionTimer(controller, getServer()), 120, 120);
|
new SessionTimer(controller, getServer()), 120, 120);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user