Add new Bukkit implementation adapter system to access MC internals.

Replaces the old NMSBlocks.
This commit is contained in:
sk89q 2014-07-14 12:10:38 -07:00
parent c535ad8682
commit f033d87098
34 changed files with 528 additions and 1580 deletions

16
pom.xml
View File

@ -542,16 +542,6 @@
<type>jar</type> <type>jar</type>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- NMS blocks -->
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
<version>1.7.9-R0.1-SNAPSHOT</version>
<scope>compile</scope>
<type>jar</type>
<optional>true</optional>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -578,11 +568,11 @@
<!-- NMS support in Bukkit --> <!-- NMS support in Bukkit -->
<resource> <resource>
<targetPath>nmsblocks/</targetPath> <targetPath>.</targetPath>
<filtering>false</filtering> <filtering>false</filtering>
<directory>${basedir}/src/bukkit/resources/nmsblocks/</directory> <directory>${basedir}/src/bukkit/resources/</directory>
<includes> <includes>
<include>*.class</include> <include>**/*.class</include>
</includes> </includes>
</resource> </resource>
</resources> </resources>

View File

@ -98,6 +98,21 @@ final class BukkitAdapter {
(float) Math.toDegrees(location.getPitch())); (float) Math.toDegrees(location.getPitch()));
} }
/**
* Create a Bukkit location from a WorldEdit position with a Bukkit world.
*
* @param world the Bukkit world
* @param position the WorldEdit position
* @return a Bukkit location
*/
public static org.bukkit.Location adapt(org.bukkit.World world, Vector position) {
checkNotNull(world);
checkNotNull(position);
return new org.bukkit.Location(
world,
position.getX(), position.getY(), position.getZ());
}
/** /**
* Create a WorldEdit entity from a Bukkit entity. * Create a WorldEdit entity from a Bukkit entity.
* *

View File

@ -31,36 +31,23 @@ import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.blocks.ContainerBlock;
import com.sk89q.worldedit.blocks.FurnaceBlock;
import com.sk89q.worldedit.blocks.LazyBlock; import com.sk89q.worldedit.blocks.LazyBlock;
import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.blocks.NoteBlock;
import com.sk89q.worldedit.blocks.SignBlock;
import com.sk89q.worldedit.blocks.SkullBlock;
import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Enums;
import com.sk89q.worldedit.util.TreeGenerator; import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.registry.LegacyWorldData; import com.sk89q.worldedit.world.registry.LegacyWorldData;
import com.sk89q.worldedit.world.registry.WorldData; import com.sk89q.worldedit.world.registry.WorldData;
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;
import org.bukkit.SkullType;
import org.bukkit.TreeType; import org.bukkit.TreeType;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState; import org.bukkit.block.BlockState;
import org.bukkit.block.Chest; import org.bukkit.block.Chest;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.block.Furnace;
import org.bukkit.block.Sign;
import org.bukkit.block.Skull;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Ambient; import org.bukkit.entity.Ambient;
import org.bukkit.entity.Animals; import org.bukkit.entity.Animals;
import org.bukkit.entity.Boat; import org.bukkit.entity.Boat;
@ -82,15 +69,9 @@ 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 javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
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;
@ -107,118 +88,26 @@ import static com.google.common.base.Preconditions.checkNotNull;
public class BukkitWorld extends LocalWorld { public class BukkitWorld extends LocalWorld {
private static final Logger logger = WorldEdit.logger; private static final Logger logger = WorldEdit.logger;
private final WeakReference<World> worldRef; private static final org.bukkit.entity.EntityType tntMinecartType =
private static boolean skipNmsAccess = false; Enums.findByValue(org.bukkit.entity.EntityType.class, "MINECART_TNT");
private static boolean skipNmsSafeSet = false;
private static boolean skipNmsValidBlockCheck = false;
/* private static final Map<Integer, Effect> effects = new HashMap<Integer, Effect>();
* holder for the nmsblock class that we should use static {
*/ for (Effect effect : Effect.values()) {
private static Class<? extends NmsBlock> nmsBlockType; effects.put(effect.getId(), effect);
private static Method nmsSetMethod;
private static Method nmsValidBlockMethod;
private static Method nmsGetMethod;
private static Method nmsSetSafeMethod;
// copied from WG
private static <T extends Enum<T>> T tryEnum(Class<T> enumType, String ... values) {
for (String val : values) {
try {
return Enum.valueOf(enumType, val);
} catch (IllegalArgumentException e) {}
} }
return null;
} }
private static org.bukkit.entity.EntityType tntMinecartType;
private static boolean checkMinecartType = true; private final WeakReference<World> worldRef;
/** /**
* Construct the object. * Construct the object.
* @param world *
* @param world the world
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public BukkitWorld(World world) { public BukkitWorld(World world) {
this.worldRef = new WeakReference<World>(world); this.worldRef = new WeakReference<World>(world);
if (checkMinecartType) {
tntMinecartType = tryEnum(org.bukkit.entity.EntityType.class, "MINECART_TNT");
checkMinecartType = false;
}
// check if we have a class we can use for nms access
// only run once per server startup
if (nmsBlockType != null || skipNmsAccess || skipNmsSafeSet || skipNmsValidBlockCheck) 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 {
// make a classloader that can handle our blocks
NmsBlockClassLoader loader = new NmsBlockClassLoader(BukkitWorld.class.getClassLoader(), nmsBlocksDir);
String filename;
for (File f : nmsBlocksDir.listFiles()) {
if (!f.isFile()) continue;
filename = f.getName();
// load class using magic keyword
Class<?> testBlock = null;
try {
testBlock = loader.loadClass("CL-NMS" + filename);
} catch (Throwable e) {
// someone is putting things where they don't belong
continue;
}
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").invoke(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("[WorldEdit] Using external NmsBlock for this version: " + nmsBlockType.getName());
} else {
// try our default
try {
nmsBlockType = (Class<? extends NmsBlock>) Class.forName("com.sk89q.worldedit.bukkit.DefaultNmsBlock");
boolean canUse = (Boolean) nmsBlockType.getMethod("verify").invoke(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.");
}
} 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;
}
} }
@Override @Override
@ -256,41 +145,6 @@ public class BukkitWorld extends LocalWorld {
throw new UnsupportedOperationException("Not implemented yet"); throw new UnsupportedOperationException("Not implemented yet");
} }
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) {
throw new ClassNotFoundException();
}
}
}
/** /**
* Get the world handle. * Get the world handle.
* *
@ -318,66 +172,6 @@ public class BukkitWorld extends LocalWorld {
return getWorld().getName(); return getWorld().getName();
} }
/**
* Set block type.
*
* @param pt
* @param type
* @return
*/
@Override
public boolean setBlockType(Vector pt, int type) {
return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type);
}
/**
* Set block type.
*
* @param pt
* @param type
* @return
*/
@Override
public boolean setBlockTypeFast(Vector pt, int type) {
return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeId(type, false);
}
@Override
public boolean setTypeIdAndData(Vector pt, int type, int data) {
return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, true);
}
@Override
public boolean setTypeIdAndDataFast(Vector pt, int type, int data) {
return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setTypeIdAndData(type, (byte) data, false);
}
/**
* Get block type.
*
* @param pt
* @return
*/
@Override
public int getBlockType(Vector pt) {
return getWorld().getBlockTypeIdAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
}
@Override
public void setBlockData(Vector pt, int data) {
getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data);
}
@Override
public void setBlockDataFast(Vector pt, int data) {
getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).setData((byte) data, false);
}
@Override
public int getBlockData(Vector pt) {
return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getData();
}
@Override @Override
public int getBlockLightLevel(Vector pt) { public int getBlockLightLevel(Vector pt) {
return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel(); return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel();
@ -423,7 +217,7 @@ public class BukkitWorld extends LocalWorld {
try { try {
getWorld().regenerateChunk(chunk.getBlockX(), chunk.getBlockZ()); getWorld().regenerateChunk(chunk.getBlockX(), chunk.getBlockZ());
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); logger.log(Level.WARNING, "Chunk generation via Bukkit raised an error", t);
} }
// Then restore // Then restore
@ -448,303 +242,6 @@ public class BukkitWorld extends LocalWorld {
return true; return true;
} }
@Override
public boolean copyToWorld(Vector pt, BaseBlock block) {
World world = getWorld();
if (block instanceof SignBlock) {
// Signs
setSignText(pt, ((SignBlock) block).getText());
return true;
}
if (block instanceof FurnaceBlock) {
// Furnaces
Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (bukkitBlock == null) return false;
BlockState state = bukkitBlock.getState();
if (!(state instanceof Furnace)) return false;
Furnace bukkit = (Furnace) state;
FurnaceBlock we = (FurnaceBlock) block;
bukkit.setBurnTime(we.getBurnTime());
bukkit.setCookTime(we.getCookTime());
return setContainerBlockContents(pt, ((ContainerBlock) block).getItems());
}
if (block instanceof ContainerBlock) {
// Chests/dispenser
return setContainerBlockContents(pt, ((ContainerBlock) block).getItems());
}
if (block instanceof MobSpawnerBlock) {
// Mob spawners
Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (bukkitBlock == null) return false;
BlockState state = bukkitBlock.getState();
if (!(state instanceof CreatureSpawner)) return false;
CreatureSpawner bukkit = (CreatureSpawner) state;
MobSpawnerBlock we = (MobSpawnerBlock) block;
bukkit.setCreatureTypeByName(we.getMobType());
bukkit.setDelay(we.getDelay());
return true;
}
if (block instanceof NoteBlock) {
// Note block
Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (bukkitBlock == null) return false;
BlockState state = bukkitBlock.getState();
if (!(state instanceof org.bukkit.block.NoteBlock)) return false;
org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state;
NoteBlock we = (NoteBlock) block;
bukkit.setRawNote(we.getNote());
return true;
}
if (block instanceof SkullBlock) {
// Skull block
Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (bukkitBlock == null) return false;
BlockState state = bukkitBlock.getState();
if (!(state instanceof org.bukkit.block.Skull)) return false;
Skull bukkit = (Skull) state;
SkullBlock we = (SkullBlock) block;
// this is dumb
SkullType skullType = SkullType.SKELETON;
switch (we.getSkullType()) {
case 0:
skullType = SkullType.SKELETON;
break;
case 1:
skullType = SkullType.WITHER;
break;
case 2:
skullType = SkullType.ZOMBIE;
break;
case 3:
skullType = SkullType.PLAYER;
break;
case 4:
skullType = SkullType.CREEPER;
break;
}
bukkit.setSkullType(skullType);
BlockFace rotation;
switch (we.getRot()) {
// soooo dumb
case 0:
rotation = BlockFace.NORTH;
break;
case 1:
rotation = BlockFace.NORTH_NORTH_EAST;
break;
case 2:
rotation = BlockFace.NORTH_EAST;
break;
case 3:
rotation = BlockFace.EAST_NORTH_EAST;
break;
case 4:
rotation = BlockFace.EAST;
break;
case 5:
rotation = BlockFace.EAST_SOUTH_EAST;
break;
case 6:
rotation = BlockFace.SOUTH_EAST;
break;
case 7:
rotation = BlockFace.SOUTH_SOUTH_EAST;
break;
case 8:
rotation = BlockFace.SOUTH;
break;
case 9:
rotation = BlockFace.SOUTH_SOUTH_WEST;
break;
case 10:
rotation = BlockFace.SOUTH_WEST;
break;
case 11:
rotation = BlockFace.WEST_SOUTH_WEST;
break;
case 12:
rotation = BlockFace.WEST;
break;
case 13:
rotation = BlockFace.WEST_NORTH_WEST;
break;
case 14:
rotation = BlockFace.NORTH_WEST;
break;
case 15:
rotation = BlockFace.NORTH_NORTH_WEST;
break;
default:
rotation = BlockFace.NORTH;
break;
}
bukkit.setRotation(rotation);
if (we.getOwner() != null && !we.getOwner().isEmpty()) bukkit.setOwner(we.getOwner());
bukkit.update(true);
return true;
}
if (!skipNmsAccess) {
try {
return (Boolean) nmsSetMethod.invoke(null, world, pt, block);
} catch (Throwable t) {
logger.log(Level.WARNING, "WorldEdit: Failed to do NMS access for direct NBT data copy", t);
skipNmsAccess = true;
}
}
return false;
}
@Override
public boolean copyFromWorld(Vector pt, BaseBlock block) {
World world = getWorld();
if (block instanceof SignBlock) {
// Signs
((SignBlock) block).setText(getSignText(pt));
return true;
}
if (block instanceof FurnaceBlock) {
// Furnaces
Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (bukkitBlock == null) return false;
BlockState state = bukkitBlock.getState();
if (!(state instanceof Furnace)) return false;
Furnace bukkit = (Furnace) state;
FurnaceBlock we = (FurnaceBlock) block;
we.setBurnTime(bukkit.getBurnTime());
we.setCookTime(bukkit.getCookTime());
((ContainerBlock) block).setItems(getContainerBlockContents(pt));
return true;
}
if (block instanceof ContainerBlock) {
// Chests/dispenser
((ContainerBlock) block).setItems(getContainerBlockContents(pt));
return true;
}
if (block instanceof MobSpawnerBlock) {
// Mob spawners
Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (bukkitBlock == null) return false;
BlockState state = bukkitBlock.getState();
if (!(state instanceof CreatureSpawner)) return false;
CreatureSpawner bukkit = (CreatureSpawner) state;
MobSpawnerBlock we = (MobSpawnerBlock) block;
we.setMobType(bukkit.getCreatureTypeName());
we.setDelay((short) bukkit.getDelay());
return true;
}
if (block instanceof NoteBlock) {
// Note block
Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (bukkitBlock == null) return false;
BlockState state = bukkitBlock.getState();
if (!(state instanceof org.bukkit.block.NoteBlock)) return false;
org.bukkit.block.NoteBlock bukkit = (org.bukkit.block.NoteBlock) state;
NoteBlock we = (NoteBlock) block;
we.setNote(bukkit.getRawNote());
return true;
}
if (block instanceof SkullBlock) {
// Skull block
Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (bukkitBlock == null) return false;
BlockState state = bukkitBlock.getState();
if (!(state instanceof org.bukkit.block.Skull)) return false;
Skull bukkit = (Skull) state;
SkullBlock we = (SkullBlock) block;
byte skullType = 0;
switch (bukkit.getSkullType()) {
// this is dumb but whoever wrote the class is stupid
case SKELETON:
skullType = 0;
break;
case WITHER:
skullType = 1;
break;
case ZOMBIE:
skullType = 2;
break;
case PLAYER:
skullType = 3;
break;
case CREEPER:
skullType = 4;
break;
}
we.setSkullType(skullType);
byte rot = 0;
switch (bukkit.getRotation()) {
// this is even more dumb, hurray for copy/paste
case NORTH:
rot = (byte) 0;
break;
case NORTH_NORTH_EAST:
rot = (byte) 1;
break;
case NORTH_EAST:
rot = (byte) 2;
break;
case EAST_NORTH_EAST:
rot = (byte) 3;
break;
case EAST:
rot = (byte) 4;
break;
case EAST_SOUTH_EAST:
rot = (byte) 5;
break;
case SOUTH_EAST:
rot = (byte) 6;
break;
case SOUTH_SOUTH_EAST:
rot = (byte) 7;
break;
case SOUTH:
rot = (byte) 8;
break;
case SOUTH_SOUTH_WEST:
rot = (byte) 9;
break;
case SOUTH_WEST:
rot = (byte) 10;
break;
case WEST_SOUTH_WEST:
rot = (byte) 11;
break;
case WEST:
rot = (byte) 12;
break;
case WEST_NORTH_WEST:
rot = (byte) 13;
break;
case NORTH_WEST:
rot = (byte) 14;
break;
case NORTH_NORTH_WEST:
rot = (byte) 15;
break;
}
we.setRot(rot);
we.setOwner(bukkit.hasOwner() ? bukkit.getOwner() : "");
return true;
}
return false;
}
/** /**
* Gets the single block inventory for a potentially double chest. * Gets the single block inventory for a potentially double chest.
* Handles people who have an old version of Bukkit. * Handles people who have an old version of Bukkit.
@ -1032,159 +529,9 @@ public class BukkitWorld extends LocalWorld {
return num; return num;
} }
/** @SuppressWarnings("deprecation")
* Set a sign's text.
*
* @param pt
* @param text
* @return
*/
private boolean setSignText(Vector pt, String[] text) {
World world = getWorld();
Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (block == null) return false;
BlockState state = block.getState();
if (state == null || !(state instanceof Sign)) return false;
Sign sign = (Sign) state;
sign.setLine(0, text[0]);
sign.setLine(1, text[1]);
sign.setLine(2, text[2]);
sign.setLine(3, text[3]);
sign.update();
return true;
}
/**
* Get a sign's text.
*
* @param pt
* @return
*/
private String[] getSignText(Vector pt) {
World world = getWorld();
Block block = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (block == null) return new String[] { "", "", "", "" };
BlockState state = block.getState();
if (state == null || !(state instanceof Sign)) return new String[] { "", "", "", "" };
Sign sign = (Sign) state;
String line0 = sign.getLine(0);
String line1 = sign.getLine(1);
String line2 = sign.getLine(2);
String line3 = sign.getLine(3);
return new String[] {
line0 != null ? line0 : "",
line1 != null ? line1 : "",
line2 != null ? line2 : "",
line3 != null ? line3 : "",
};
}
/**
* Get a container block's contents.
*
* @param pt
* @return
*/
private BaseItemStack[] getContainerBlockContents(Vector pt) {
Block block = getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (block == null) {
return new BaseItemStack[0];
}
BlockState state = block.getState();
if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
return new BaseItemStack[0];
}
org.bukkit.inventory.InventoryHolder container = (org.bukkit.inventory.InventoryHolder) state;
Inventory inven = container.getInventory();
if (container instanceof Chest) {
inven = getBlockInventory((Chest) container);
}
int size = inven.getSize();
BaseItemStack[] contents = new BaseItemStack[size];
for (int i = 0; i < size; ++i) {
ItemStack bukkitStack = inven.getItem(i);
if (bukkitStack != null && bukkitStack.getTypeId() > 0) {
contents[i] = new BaseItemStack(
bukkitStack.getTypeId(),
bukkitStack.getAmount(),
bukkitStack.getDurability());
try {
for (Map.Entry<Enchantment, Integer> entry : bukkitStack.getEnchantments().entrySet()) {
contents[i].getEnchantments().put(entry.getKey().getId(), entry.getValue());
}
} catch (Throwable ignore) {}
}
}
return contents;
}
/**
* Set a container block's contents.
*
* @param pt
* @param contents
* @return
*/
private boolean setContainerBlockContents(Vector pt, BaseItemStack[] contents) {
Block block = getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
if (block == null) {
return false;
}
BlockState state = block.getState();
if (!(state instanceof org.bukkit.inventory.InventoryHolder)) {
return false;
}
org.bukkit.inventory.InventoryHolder chest = (org.bukkit.inventory.InventoryHolder) state;
Inventory inven = chest.getInventory();
if (chest instanceof Chest) {
inven = getBlockInventory((Chest) chest);
}
int size = inven.getSize();
for (int i = 0; i < size; ++i) {
if (i >= contents.length) {
break;
}
if (contents[i] != null) {
ItemStack toAdd = new ItemStack(contents[i].getType(),
contents[i].getAmount(),
contents[i].getData());
try {
for (Map.Entry<Integer, Integer> entry : contents[i].getEnchantments().entrySet()) {
toAdd.addEnchantment(Enchantment.getById(entry.getKey()), entry.getValue());
}
} catch (Throwable ignore) {}
inven.setItem(i, toAdd);
} else {
inven.setItem(i, null);
}
}
return true;
}
/**
* Returns whether a block has a valid ID.
*
* @param type
* @return
*/
@Override @Override
public boolean isValidBlockType(int type) { public boolean isValidBlockType(int type) {
if (!skipNmsValidBlockCheck) {
try {
return (Boolean) nmsValidBlockMethod.invoke(null, type);
} catch (Throwable e) {
skipNmsValidBlockCheck = true;
}
}
return Material.getMaterial(type) != null && Material.getMaterial(type).isBlock(); return Material.getMaterial(type) != null && Material.getMaterial(type).isBlock();
} }
@ -1228,13 +575,6 @@ public class BukkitWorld extends LocalWorld {
} }
} }
private static final Map<Integer, Effect> effects = new HashMap<Integer, Effect>();
static {
for (Effect effect : Effect.values()) {
effects.put(effect.getId(), effect);
}
}
@Override @Override
public boolean playEffect(Vector position, int type, int data) { public boolean playEffect(Vector position, int type, int data) {
World world = getWorld(); World world = getWorld();
@ -1278,38 +618,25 @@ public class BukkitWorld extends LocalWorld {
} }
@Override @Override
public BaseBlock getBlock(Vector pt) { public BaseBlock getBlock(Vector position) {
int type = getBlockType(pt); BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
int data = getBlockData(pt); if (adapter != null) {
return adapter.getBlock(BukkitAdapter.adapt(getWorld(), position));
switch (type) { } else {
case BlockID.WALL_SIGN: Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
case BlockID.SIGN_POST: return new BaseBlock(bukkitBlock.getTypeId(), bukkitBlock.getData());
//case BlockID.CHEST: // Prevent data loss for now
//case BlockID.FURNACE:
//case BlockID.BURNING_FURNACE:
//case BlockID.DISPENSER:
//case BlockID.MOB_SPAWNER:
case BlockID.NOTE_BLOCK:
case BlockID.HEAD:
return super.getBlock(pt);
default:
if (!skipNmsAccess) {
try {
NmsBlock block = null;
block = (NmsBlock) nmsGetMethod.invoke(null, getWorld(), pt, type, data);
if (block != null) {
return block;
}
} catch (Throwable t) {
logger.log(Level.WARNING,
"WorldEdit: Failed to do NMS access for direct NBT data copy", t);
skipNmsAccess = true;
}
}
} }
}
return super.getBlock(pt); @Override
public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
return adapter.setBlock(BukkitAdapter.adapt(getWorld(), position), block, notifyAndLight);
} else {
Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
return bukkitBlock.setTypeIdAndData(block.getType(), (byte) block.getData(), notifyAndLight);
}
} }
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@ -1320,20 +647,6 @@ public class BukkitWorld extends LocalWorld {
return new LazyBlock(bukkitBlock.getTypeId(), bukkitBlock.getData(), this, position); return new LazyBlock(bukkitBlock.getTypeId(), bukkitBlock.getData(), this, position);
} }
@Override
public boolean setBlock(Vector pt, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
if (!skipNmsSafeSet) {
try {
return (Boolean) nmsSetSafeMethod.invoke(null, this, pt, block, notifyAdjacent);
} catch (Throwable t) {
logger.log(Level.WARNING, "WorldEdit: Failed to do NMS safe block set", t);
skipNmsSafeSet = true;
}
}
return super.setBlock(pt, block, notifyAdjacent);
}
/** /**
* @deprecated Use {@link #setBlock(Vector, BaseBlock, boolean)} * @deprecated Use {@link #setBlock(Vector, BaseBlock, boolean)}
*/ */

View File

@ -1,472 +0,0 @@
/*
* 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.bukkit;
import com.sk89q.jnbt.*;
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.world.DataException;
import com.sk89q.worldedit.foundation.Block;
import net.minecraft.server.v1_7_R3.*;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_7_R3.CraftWorld;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Logger;
/**
* 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_7_R3.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(pt.getBlockX()));
nbtData.set("y", new NBTTagInt(pt.getBlockY()));
nbtData.set("z", new NBTTagInt(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) {
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());
// TileEntity te = craftWorld.getHandle().getTileEntity(x, y, z);
// craftWorld.getHandle().tileEntityList.remove(te);
boolean changed = craftWorld.getHandle().setTypeAndData(x, y, z, getNmsBlock(block.getId()), block.getData(), 0);
if (block instanceof BaseBlock) {
world.copyToWorld(position, (BaseBlock) block);
}
changed = craftWorld.getHandle().setData(x, y, z, block.getData(), 0) || changed;
if (changed && notifyAdjacent) {
craftWorld.getHandle().notify(x, y, z);
craftWorld.getHandle().update(x, y, z, getNmsBlock(block.getId()));
}
return changed;
}
public static boolean hasTileEntity(int type) {
net.minecraft.server.v1_7_R3.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_7_R3.Block getNmsBlock(int type) {
return net.minecraft.server.v1_7_R3.Block.e(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
*/
private static Tag toNative(NBTBase foreign) {
// temporary fix since mojang removed names from tags
// our nbt spec will need to be updated to theirs
return toNative(getTagName(foreign.getTypeId()), foreign);
}
// seriously these two methods are hacky - our jnbt spec needs updating
// copied from NMS 1.7.5- code, since it was removed in 1.7.8
private static String getTagName(int i) {
switch (i) {
case 0:
return "TAG_End";
case 1:
return "TAG_Byte";
case 2:
return "TAG_Short";
case 3:
return "TAG_Int";
case 4:
return "TAG_Long";
case 5:
return "TAG_Float";
case 6:
return "TAG_Double";
case 7:
return "TAG_Byte_Array";
case 8:
return "TAG_String";
case 9:
return "TAG_List";
case 10:
return "TAG_Compound";
case 11:
return "TAG_Int_Array";
case 99:
return "Any Numeric Tag";
default:
return "UNKNOWN";
}
}
/**
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
* structure.
*
* @param foreign non-native NMS NBT structure
* @param name name for the tag, if it has one
* @return native WorldEdit NBT structure
*/
@SuppressWarnings("unchecked")
private static Tag toNative(String name, NBTBase foreign) {
if (foreign == null) {
return null;
}
if (foreign instanceof NBTTagCompound) {
Map<String, Tag> values = new HashMap<String, Tag>();
Collection<Object> foreignKeys = null;
if (compoundMapField == null) {
try {
// Method name may change!
foreignKeys = ((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 {
foreignKeys = ((HashMap<Object, Object>) compoundMapField.get(foreign)).keySet();
} catch (Throwable e) {
// Can't do much beyond this
throw new RuntimeException(e);
}
}
for (Object obj : foreignKeys) {
String key = (String) obj;
NBTBase base = (NBTBase) ((NBTTagCompound) foreign).get(key);
values.put(key, toNative(key, base));
}
return new CompoundTag(name, values);
} else if (foreign instanceof NBTTagByte) {
return new ByteTag(name, ((NBTTagByte) foreign).f()); // getByte
} else if (foreign instanceof NBTTagByteArray) {
return new ByteArrayTag(name,
((NBTTagByteArray) foreign).c()); // data
} else if (foreign instanceof NBTTagDouble) {
return new DoubleTag(name,
((NBTTagDouble) foreign).g()); // getDouble
} else if (foreign instanceof NBTTagFloat) {
return new FloatTag(name, ((NBTTagFloat) foreign).h()); // getFloat
} else if (foreign instanceof NBTTagInt) {
return new IntTag(name, ((NBTTagInt) foreign).d()); // getInt
} else if (foreign instanceof NBTTagIntArray) {
return new IntArrayTag(name,
((NBTTagIntArray) foreign).c()); // data
} else if (foreign instanceof NBTTagList) {
try {
return transmorgifyList(name, (NBTTagList) foreign);
} catch (NoSuchFieldException e) {
} catch (SecurityException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {}
return new ListTag(name, ByteTag.class, new ArrayList<ByteTag>());
} else if (foreign instanceof NBTTagLong) {
return new LongTag(name, ((NBTTagLong) foreign).c()); // getLong
} else if (foreign instanceof NBTTagShort) {
return new ShortTag(name, ((NBTTagShort) foreign).e()); // getShort
} else if (foreign instanceof NBTTagString) {
return new StringTag(name,
((NBTTagString) foreign).a_()); // data
} else if (foreign instanceof NBTTagEnd) {
return new EndTag();
} else {
throw new IllegalArgumentException("Don't know how to make native "
+ foreign.getClass().getCanonicalName());
}
}
private static ListTag transmorgifyList(String name, NBTTagList foreign)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
List<Tag> values = new ArrayList<Tag>();
int type = foreign.d();
Field listField = NBTTagList.class.getDeclaredField("list");
listField.setAccessible(true);
List foreignList;
foreignList = (List) listField.get(foreign);
for (int i = 0; i < foreign.size(); i++) {
NBTBase element = (NBTBase) foreignList.get(i);
values.add(toNative(null, element)); // list elements shouldn't have names
}
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
return new ListTag(name, cls, values);
}
/**
* 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();
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(((ByteTag) foreign).getValue());
} else if (foreign instanceof ByteArrayTag) {
return new NBTTagByteArray(((ByteArrayTag) foreign).getValue());
} else if (foreign instanceof DoubleTag) {
return new NBTTagDouble(((DoubleTag) foreign).getValue());
} else if (foreign instanceof FloatTag) {
return new NBTTagFloat(((FloatTag) foreign).getValue());
} else if (foreign instanceof IntTag) {
return new NBTTagInt(((IntTag) foreign).getValue());
} else if (foreign instanceof IntArrayTag) {
return new NBTTagIntArray(((IntArrayTag) foreign).getValue());
} else if (foreign instanceof ListTag) {
NBTTagList tag = new NBTTagList();
ListTag foreignList = (ListTag) foreign;
for (Tag t : foreignList.getValue()) {
tag.add(fromNative(t));
}
return tag;
} else if (foreign instanceof LongTag) {
return new NBTTagLong(((LongTag) foreign).getValue());
} else if (foreign instanceof ShortTag) {
return new NBTTagShort(((ShortTag) foreign).getValue());
} else if (foreign instanceof StringTag) {
return new NBTTagString(((StringTag) foreign).getValue());
} else if (foreign instanceof EndTag) {
try {
Method tagMaker = NBTBase.class.getDeclaredMethod("createTag", byte.class);
tagMaker.setAccessible(true);
return (NBTBase) tagMaker.invoke(null, (byte) 0);
} catch (Exception e) {
return null;
}
} 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 && net.minecraft.server.v1_7_R3.Block.e(type) != null);
}
}

View File

@ -1,61 +0,0 @@
/*
* 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.bukkit;
import org.bukkit.World;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.foundation.Block;
public abstract class NmsBlock extends BaseBlock {
protected NmsBlock(int type) {
super(type);
}
protected NmsBlock(int type, int data) {
super(type, data);
}
public static boolean verify() {
return false;
}
public static NmsBlock get(World world, Vector vector, int type, int data) {
return null;
}
public static boolean set(World world, Vector vector, Block block) {
return false;
}
public static boolean setSafely(World world, Vector vector, Block block, boolean notify) {
return false;
}
public static boolean hasTileEntity(int type) {
return false;
}
public static boolean isValidBlockType(int type) {
return false;
}
}

View File

@ -22,7 +22,16 @@ package com.sk89q.worldedit.bukkit;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.sk89q.util.yaml.YAMLProcessor; import com.sk89q.util.yaml.YAMLProcessor;
import com.sk89q.wepif.PermissionsResolverManager; import com.sk89q.wepif.PermissionsResolverManager;
import com.sk89q.worldedit.*; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalPlayer;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.ServerInterface;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditOperation;
import com.sk89q.worldedit.bukkit.adapter.AdapterLoadException;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplLoader;
import com.sk89q.worldedit.bukkit.selections.CuboidSelection; import com.sk89q.worldedit.bukkit.selections.CuboidSelection;
import com.sk89q.worldedit.bukkit.selections.CylinderSelection; import com.sk89q.worldedit.bukkit.selections.CylinderSelection;
import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection; import com.sk89q.worldedit.bukkit.selections.Polygonal2DSelection;
@ -32,7 +41,11 @@ import com.sk89q.worldedit.event.platform.CommandSuggestionEvent;
import com.sk89q.worldedit.event.platform.PlatformReadyEvent; import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.inventory.BlockBag; import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.regions.*; import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.CylinderRegion;
import com.sk89q.worldedit.regions.Polygonal2DRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionSelector;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -40,13 +53,20 @@ import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import java.io.*; import javax.annotation.Nullable;
import java.util.Enumeration; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* Plugin for Bukkit. * Plugin for Bukkit.
* *
@ -54,27 +74,13 @@ import java.util.zip.ZipEntry;
*/ */
public class WorldEditPlugin extends JavaPlugin implements TabCompleter { public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
/** private static final Logger log = Logger.getLogger(WorldEditPlugin.class.getCanonicalName());
* The name of the CUI's plugin channel registration
*/
public static final String CUI_PLUGIN_CHANNEL = "WECUI"; public static final String CUI_PLUGIN_CHANNEL = "WECUI";
private static WorldEditPlugin INSTANCE;
/** private BukkitImplAdapter bukkitAdapter;
* The server interface that all server-related API goes through.
*/
private BukkitServerInterface server; private BukkitServerInterface server;
/** private final WorldEditAPI api = new WorldEditAPI(this);
* Main WorldEdit instance.
*/
private WorldEdit controller;
/**
* Deprecated API.
*/
private WorldEditAPI api;
/**
* Holds the configuration for WorldEdit.
*/
private BukkitConfiguration config; private BukkitConfiguration config;
/** /**
@ -82,41 +88,27 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
*/ */
@Override @Override
public void onEnable() { public void onEnable() {
final String pluginYmlVersion = getDescription().getVersion(); this.INSTANCE = this;
final String manifestVersion = WorldEdit.getVersion();
if (!manifestVersion.equalsIgnoreCase(pluginYmlVersion)) { WorldEdit worldEdit = WorldEdit.getInstance();
WorldEdit.setVersion(manifestVersion + " (" + pluginYmlVersion + ")");
}
// Make the data folders that WorldEdit uses loadAdapter(); // Need an adapter to work with special blocks with NBT data
getDataFolder().mkdirs(); loadConfig(); // Load configuration
File targetDir = new File(getDataFolder() + File.separator + "nmsblocks"); PermissionsResolverManager.initialize(this); // Setup permission resolver
targetDir.mkdir();
copyNmsBlockClasses(targetDir);
// Create the default configuration file // Setup platform
createDefaultConfiguration("config.yml");
// Set up configuration and such, including the permissions
// resolver
config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "config.yml"), true), this);
PermissionsResolverManager.initialize(this);
// Load the configuration
config.load();
// Setup interfaces
server = new BukkitServerInterface(this, getServer()); server = new BukkitServerInterface(this, getServer());
controller = WorldEdit.getInstance(); worldEdit.getPlatformManager().register(server);
controller.getPlatformManager().register(server);
api = new WorldEditAPI(this); // Register CUI
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().runTaskTimerAsynchronously(this, new SessionTimer(controller, getServer()), 120, 120); // Register session timer
getServer().getScheduler().runTaskTimerAsynchronously(this, new SessionTimer(worldEdit, getServer()), 120, 120);
// If we are on MCPC+/Cauldron, then Forge will have already loaded // If we are on MCPC+/Cauldron, then Forge will have already loaded
// Forge WorldEdit and there's (probably) not going to be any other // Forge WorldEdit and there's (probably) not going to be any other
@ -124,31 +116,34 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent());
} }
private void copyNmsBlockClasses(File target) { private void loadConfig() {
createDefaultConfiguration("config.yml"); // Create the default configuration file
config = new BukkitConfiguration(new YAMLProcessor(new File(getDataFolder(), "config.yml"), true), this);
config.load();
}
private void loadAdapter() {
// Attempt to load a Bukkit adapter
BukkitImplLoader adapterLoader = new BukkitImplLoader();
try { try {
JarFile jar = new JarFile(getFile()); adapterLoader.addFromPath(getClass().getClassLoader());
@SuppressWarnings("rawtypes") } catch (IOException e) {
Enumeration entries = jar.entries(); log.log(Level.WARNING, "Failed to search path for Bukkit adapters");
while (entries.hasMoreElements()) { }
JarEntry jarEntry = (JarEntry) entries.nextElement();
if (!jarEntry.getName().startsWith("nmsblocks") || jarEntry.isDirectory()) continue;
File file = new File(target + File.separator + jarEntry.getName().replace("nmsblocks", "")); try {
if (file.exists()) continue; adapterLoader.addFromJar(getFile());
} catch (IOException e) {
InputStream is = jar.getInputStream(jarEntry); log.log(Level.WARNING, "Failed to search " + getFile() + " for Bukkit adapters", e);
FileOutputStream fos = new FileOutputStream(file); }
try {
fos = new FileOutputStream(file); bukkitAdapter = adapterLoader.loadAdapter();
byte[] buf = new byte[8192]; log.log(Level.INFO, "Using " + bukkitAdapter.getClass().getCanonicalName() + " as the Bukkit adapter");
int length = 0; } catch (AdapterLoadException e) {
while ((length = is.read(buf)) > 0) { log.log(Level.WARNING, e.getMessage());
fos.write(buf, 0, length); }
}
fos.close();
is.close();
}
} catch (Throwable e) {}
} }
/** /**
@ -156,10 +151,13 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
*/ */
@Override @Override
public void onDisable() { public void onDisable() {
if (controller != null) { WorldEdit worldEdit = WorldEdit.getInstance();
controller.clearSessions(); worldEdit.clearSessions();
controller.getPlatformManager().unregister(server); worldEdit.getPlatformManager().unregister(server);
if (config != null) {
config.unload(); config.unload();
}
if (server != null) {
server.unregisterCommands(); server.unregisterCommands();
} }
this.getServer().getScheduler().cancelTasks(this); this.getServer().getScheduler().cancelTasks(this);
@ -177,7 +175,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
/** /**
* Create a default configuration file from the .jar. * Create a default configuration file from the .jar.
* *
* @param name * @param name the filename
*/ */
protected void createDefaultConfiguration(String name) { protected void createDefaultConfiguration(String name) {
File actual = new File(getDataFolder(), name); File actual = new File(getDataFolder(), name);
@ -257,7 +255,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
* @return * @return
*/ */
public LocalSession getSession(Player player) { public LocalSession getSession(Player player) {
return controller.getSession(wrapPlayer(player)); return WorldEdit.getInstance().getSession(wrapPlayer(player));
} }
/** /**
@ -268,10 +266,10 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
*/ */
public EditSession createEditSession(Player player) { public EditSession createEditSession(Player player) {
LocalPlayer wePlayer = wrapPlayer(player); LocalPlayer wePlayer = wrapPlayer(player);
LocalSession session = controller.getSession(wePlayer); LocalSession session = WorldEdit.getInstance().getSession(wePlayer);
BlockBag blockBag = session.getBlockBag(wePlayer); BlockBag blockBag = session.getBlockBag(wePlayer);
EditSession editSession = controller.getEditSessionFactory() EditSession editSession = WorldEdit.getInstance().getEditSessionFactory()
.getEditSession(wePlayer.getWorld(), session.getBlockChangeLimit(), blockBag, wePlayer); .getEditSession(wePlayer.getWorld(), session.getBlockChangeLimit(), blockBag, wePlayer);
editSession.enableQueue(); editSession.enableQueue();
@ -286,12 +284,12 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
*/ */
public void remember(Player player, EditSession editSession) { public void remember(Player player, EditSession editSession) {
LocalPlayer wePlayer = wrapPlayer(player); LocalPlayer wePlayer = wrapPlayer(player);
LocalSession session = controller.getSession(wePlayer); LocalSession session = WorldEdit.getInstance().getSession(wePlayer);
session.remember(editSession); session.remember(editSession);
editSession.flushQueue(); editSession.flushQueue();
controller.flushBlockBag(wePlayer, editSession); WorldEdit.getInstance().flushBlockBag(wePlayer, editSession);
} }
/** /**
@ -301,10 +299,9 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
* @param op * @param op
* @throws Throwable * @throws Throwable
*/ */
public void perform(Player player, WorldEditOperation op) public void perform(Player player, WorldEditOperation op) throws Throwable {
throws Throwable {
LocalPlayer wePlayer = wrapPlayer(player); LocalPlayer wePlayer = wrapPlayer(player);
LocalSession session = controller.getSession(wePlayer); LocalSession session = WorldEdit.getInstance().getSession(wePlayer);
EditSession editSession = createEditSession(player); EditSession editSession = createEditSession(player);
try { try {
@ -379,7 +376,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
* @return * @return
*/ */
public WorldEdit getWorldEdit() { public WorldEdit getWorldEdit() {
return controller; return WorldEdit.getInstance();
} }
/** /**
@ -396,7 +393,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
throw new IllegalArgumentException("Offline player not allowed"); throw new IllegalArgumentException("Offline player not allowed");
} }
LocalSession session = controller.getSession(wrapPlayer(player)); LocalSession session = WorldEdit.getInstance().getSession(wrapPlayer(player));
RegionSelector selector = session.getRegionSelector(BukkitUtil.getLocalWorld(player.getWorld())); RegionSelector selector = session.getRegionSelector(BukkitUtil.getLocalWorld(player.getWorld()));
try { try {
@ -434,9 +431,30 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
throw new IllegalArgumentException("Null selection not allowed"); throw new IllegalArgumentException("Null selection not allowed");
} }
LocalSession session = controller.getSession(wrapPlayer(player)); LocalSession session = WorldEdit.getInstance().getSession(wrapPlayer(player));
RegionSelector sel = selection.getRegionSelector(); RegionSelector sel = selection.getRegionSelector();
session.setRegionSelector(BukkitUtil.getLocalWorld(player.getWorld()), sel); session.setRegionSelector(BukkitUtil.getLocalWorld(player.getWorld()), sel);
session.dispatchCUISelection(wrapPlayer(player)); session.dispatchCUISelection(wrapPlayer(player));
} }
/**
* Gets the instance of this plugin.
*
* @return an instance of the plugin
* @throws NullPointerException if the plugin hasn't been enabled
*/
static WorldEditPlugin getInstance() {
return checkNotNull(INSTANCE);
}
/**
* Get the Bukkit implementation adapter.
*
* @return the adapter
*/
@Nullable
BukkitImplAdapter getBukkitImplAdapter() {
return bukkitAdapter;
}
} }

View File

@ -0,0 +1,41 @@
/*
* 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.bukkit.adapter;
/**
* Thrown when no adapter can be found.
*/
public class AdapterLoadException extends Exception {
public AdapterLoadException() {
}
public AdapterLoadException(String message) {
super(message);
}
public AdapterLoadException(String message, Throwable cause) {
super(message, cause);
}
public AdapterLoadException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.bukkit.adapter;
import com.sk89q.worldedit.blocks.BaseBlock;
import org.bukkit.Location;
/**
* An interface for adapters of various Bukkit implementations.
*/
public interface BukkitImplAdapter {
/**
* Get the block at the given location.
*
* @param location the location
* @return the block
*/
BaseBlock getBlock(Location location);
/**
* Set the block at the given location.
*
* @param location the location
* @param state the block
* @param notifyAndLight notify and light if set
* @return true if a block was likely changed
*/
boolean setBlock(Location location, BaseBlock state, boolean notifyAndLight);
}

View File

@ -0,0 +1,197 @@
/*
* 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.bukkit.adapter;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.util.io.Closer;
import org.bukkit.Bukkit;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Loads Bukkit implementation adapters.
*/
public class BukkitImplLoader {
private static final Logger log = Logger.getLogger(BukkitImplLoader.class.getCanonicalName());
private final List<String> adapterCandidates = new ArrayList<String>();
private String customCandidate;
private static final String SEARCH_PACKAGE = "com.sk89q.worldedit.bukkit.adapter.impl";
private static final String SEARCH_PACKAGE_DOT = SEARCH_PACKAGE + ".";
private static final String SEARCH_PATH = SEARCH_PACKAGE.replace(".", "/");
private static final String CLASS_SUFFIX = ".class";
private static final String LOAD_ERROR_MESSAGE =
"Failed to find an adapter for Bukkit!\n\n" +
"This version of WorldEdit (%s) does not fully support your version of Bukkit (%s).\n\n" +
"What this means:\n" +
"1) Block operations will work, but chests will be empty, signs will be blank, and so on.\n" +
"2) You won't be able to save and load chests, signs, etc. with .schematic files.\n" +
"3) You won't be able to work with entities properly.\n" +
"4) Undo will will not be able to restore chests, signs, and etc.\n\n" +
"Possible solutions:\n" +
"1) If this is a new version of Minecraft, please wait for us to update. " +
"You can also put in a ticket at http://youtrack.sk89q.com (check for an existing ticket first).\n" +
"2) If you are using an older version of Minecraft, you may need to downgrade WorldEdit.\n" +
"3) If you are using an older version of WorldEdit, you may need to update your WorldEdit.\n" +
"4) If you are not using CraftBukkit, then report this issue to http://youtrack.sk89q.com " +
"(check for an existing ticket first).\n" +
"5) If you are developing WorldEdit, you can force an adapter with " +
"-Dworldedit.bukkit.adapter=the_class_name.\n\n" +
"Can I ignore this error? Yes! Just be aware of the undo issue.\n" +
"Am I using CraftBukkit? %s.\n";
/**
* Create a new instance.
*/
public BukkitImplLoader() {
addDefaults();
}
/**
* Add default candidates, such as any defined with
* {@code -Dworldedit.bukkit.adapter}.
*/
private void addDefaults() {
String className = System.getProperty("worldedit.bukkit.adapter");
if (className != null) {
customCandidate = className;
adapterCandidates.add(className);
log.log(Level.INFO, "-Dworldedit.bukkit.adapter used to add " + className + " to the list of available Bukkit adapters");
}
}
/**
* Search the given JAR for candidate implementations.
*
* @param file the file
* @throws IOException thrown on I/O error
*/
public void addFromJar(File file) throws IOException {
Closer closer = Closer.create();
JarFile jar = closer.register(new JarFile(file));
try {
Enumeration entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) entries.nextElement();
String className = jarEntry.getName().replaceAll("[/\\\\]+", ".");
if (!className.startsWith(SEARCH_PACKAGE_DOT) || jarEntry.isDirectory()) continue;
int beginIndex = 0;
int endIndex = className.length() - CLASS_SUFFIX.length();
className = className.substring(beginIndex, endIndex);
adapterCandidates.add(className);
}
} finally {
closer.close();
}
}
/**
* Search for classes stored as separate files available via the given
* class loader.
*
* @param classLoader the class loader
* @throws IOException thrown on error
*/
public void addFromPath(ClassLoader classLoader) throws IOException {
Enumeration<URL> resources = classLoader.getResources(SEARCH_PATH);
while (resources.hasMoreElements()) {
File file = new File(resources.nextElement().getFile());
addFromPath(file);
}
}
/**
* Search for classes stored as separate files available via the given
* path.
*
* @param file the path
*/
private void addFromPath(File file) {
String resource = SEARCH_PACKAGE_DOT + file.getName();
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File child : files) {
addFromPath(child);
}
}
} else if (resource.endsWith(CLASS_SUFFIX)) {
int beginIndex = 0;
int endIndex = resource.length() - CLASS_SUFFIX.length();
String className = resource.substring(beginIndex, endIndex);
adapterCandidates.add(className);
}
}
/**
* Iterate through the list of candidates and load an adapter.
*
* @return an adapter
* @throws AdapterLoadException thrown if no adapter could be found
*/
public BukkitImplAdapter loadAdapter() throws AdapterLoadException {
for (String className : adapterCandidates) {
try {
Class<?> cls = Class.forName(className);
if (BukkitImplAdapter.class.isAssignableFrom(cls)) {
return (BukkitImplAdapter) cls.newInstance();
} else {
log.log(Level.WARNING, "Failed to load the Bukkit adapter class '" + className +
"' because it does not implement " + BukkitImplAdapter.class.getCanonicalName());
}
} catch (ClassNotFoundException e) {
log.log(Level.WARNING, "Failed to load the Bukkit adapter class '" + className +
"' that is not supposed to be missing", e);
} catch (IllegalAccessException e) {
log.log(Level.WARNING, "Failed to load the Bukkit adapter class '" + className +
"' that is not supposed to be raising this error", e);
} catch (Throwable e) {
if (className.equals(customCandidate)) {
log.log(Level.WARNING, "Failed to load the Bukkit adapter class '" + className + "'", e);
}
}
}
String weVersion = WorldEdit.getVersion();
String bukkitVersion = Bukkit.getBukkitVersion() + " implemented by " + Bukkit.getName() + " " + Bukkit.getVersion();
String usingCraftBukkit =
Bukkit.getName().equals("CraftBukkit")
? "Probably (if you got it from dl.bukkit.org, then yes)"
: "No! You are using " + Bukkit.getName();
throw new AdapterLoadException(
String.format(LOAD_ERROR_MESSAGE, weVersion, bukkitVersion, usingCraftBukkit));
}
}

View File

@ -60,115 +60,11 @@ public abstract class LocalWorld extends AbstractWorld {
@Deprecated @Deprecated
protected Random random = new Random(); protected Random random = new Random();
@Override
public BaseBlock getBlock(Vector pt) {
checkLoadedChunk(pt);
@SuppressWarnings("deprecation") int type = getBlockType(pt);
@SuppressWarnings("deprecation") int data = getBlockData(pt);
switch (type) {
case BlockID.WALL_SIGN:
case BlockID.SIGN_POST: {
SignBlock block = new SignBlock(type, data);
copyFromWorld(pt, block);
return block;
}
case BlockID.CHEST: {
ChestBlock block = new ChestBlock(data);
copyFromWorld(pt, block);
return block;
}
case BlockID.FURNACE:
case BlockID.BURNING_FURNACE: {
FurnaceBlock block = new FurnaceBlock(type, data);
copyFromWorld(pt, block);
return block;
}
case BlockID.DISPENSER: {
DispenserBlock block = new DispenserBlock(data);
copyFromWorld(pt, block);
return block;
}
case BlockID.MOB_SPAWNER: {
MobSpawnerBlock block = new MobSpawnerBlock(data);
copyFromWorld(pt, block);
return block;
}
case BlockID.NOTE_BLOCK: {
NoteBlock block = new NoteBlock(data);
copyFromWorld(pt, block);
return block;
}
case BlockID.HEAD: {
SkullBlock block = new SkullBlock(data);
copyFromWorld(pt, block);
return block;
}
default:
return new BaseBlock(type, data);
}
}
/**
* Given a block and a position, copy data from the world to the block
* based on the type of block.
* </p>
* The provided {@link BaseBlock} should match that of the one in the
* world.
*
* @param position the position
* @param block the block
* @return true if the copy operation succeeded, false otherwise
*/
public abstract boolean copyFromWorld(Vector position, BaseBlock block);
@Override @Override
public BaseBlock getLazyBlock(Vector position) { public BaseBlock getLazyBlock(Vector position) {
return getBlock(position); return getBlock(position);
} }
@Override
public boolean setBlock(Vector pt, BaseBlock block, boolean notifyAdjacent) throws WorldEditException {
boolean successful;
// Default implementation will call the old deprecated methods
if (notifyAdjacent) {
successful = setTypeIdAndData(pt, block.getId(), block.getData());
} else {
successful = setTypeIdAndDataFast(pt, block.getId(), block.getData());
}
copyToWorld(pt, block);
return successful;
}
/**
* Given a block and a position, copy data to the world from the block
* based on the type of block.
* </p>
* The provided {@link BaseBlock} should match that of the one in the
* world.
*
* @param position the position
* @param block the block
* @return true if the copy operation succeeded, false otherwise
*/
public abstract boolean copyToWorld(Vector position, BaseBlock block);
@Override
public boolean setBlock(Vector pt, BaseBlock block) throws WorldEditException {
return setBlock(pt, block, true);
}
@Override @Override
public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector pt) throws MaxChangedBlocksException { public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector pt) throws MaxChangedBlocksException {
switch (type) { switch (type) {

View File

@ -100,42 +100,6 @@ public class LocalWorldAdapter extends LocalWorld {
return world.setBlock(position, block, notifyAndLight); return world.setBlock(position, block, notifyAndLight);
} }
@Override
@Deprecated
public boolean setBlockType(Vector position, int type) {
return world.setBlockType(position, type);
}
@Override
@Deprecated
public boolean setBlockTypeFast(Vector position, int type) {
return world.setBlockTypeFast(position, type);
}
@Override
@Deprecated
public void setBlockData(Vector position, int data) {
world.setBlockData(position, data);
}
@Override
@Deprecated
public void setBlockDataFast(Vector position, int data) {
world.setBlockDataFast(position, data);
}
@Override
@Deprecated
public boolean setTypeIdAndData(Vector position, int type, int data) {
return world.setTypeIdAndData(position, type, data);
}
@Override
@Deprecated
public boolean setTypeIdAndDataFast(Vector position, int type, int data) {
return world.setTypeIdAndDataFast(position, type, data);
}
@Override @Override
public int getBlockLightLevel(Vector position) { public int getBlockLightLevel(Vector position) {
return world.getBlockLightLevel(position); return world.getBlockLightLevel(position);
@ -298,30 +262,11 @@ public class LocalWorldAdapter extends LocalWorld {
return world.getBlock(position); return world.getBlock(position);
} }
@Override
public boolean copyFromWorld(Vector position, BaseBlock block) {
return false;
}
@Override
public boolean copyToWorld(Vector position, BaseBlock block) {
return false;
}
@Override @Override
public BaseBlock getLazyBlock(Vector position) { public BaseBlock getLazyBlock(Vector position) {
return world.getLazyBlock(position); return world.getLazyBlock(position);
} }
@Override
public boolean setBlock(Vector position, BaseBlock block) {
try {
return world.setBlock(position, block);
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
@Override @Override
@Nullable @Nullable
public Operation commit() { public Operation commit() {

View File

@ -150,7 +150,7 @@ public class WorldEditExceptionConverter extends ExceptionConverterHelper {
@ExceptionMatch @ExceptionMatch
public void convert(WorldEditException e) throws CommandException { public void convert(WorldEditException e) throws CommandException {
throw new CommandException(e.getMessage()); throw new CommandException(e.getMessage(), e);
} }
} }

View File

@ -0,0 +1,54 @@
/*
* 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.util;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Helper methods for enums.
*/
public final class Enums {
private Enums() {
}
/**
* Search the given enum for a value that is equal to the one of the
* given values, searching in an ascending manner.
*
* @param enumType the enum type
* @param values the list of values
* @param <T> the type of enum
* @return the found value or null
*/
@Nullable
public static <T extends Enum<T>> T findByValue(Class<T> enumType, String... values) {
checkNotNull(enumType);
checkNotNull(values);
for (String val : values) {
try {
return Enum.valueOf(enumType, val);
} catch (IllegalArgumentException ignored) {}
}
return null;
}
}

View File

@ -47,6 +47,37 @@ public abstract class AbstractWorld implements World {
private final PriorityQueue<QueuedEffect> effectQueue = new PriorityQueue<QueuedEffect>(); private final PriorityQueue<QueuedEffect> effectQueue = new PriorityQueue<QueuedEffect>();
private int taskId = -1; private int taskId = -1;
@Override
public final boolean setBlockType(Vector position, int type) {
try {
return setBlock(position, new BaseBlock(type));
} catch (WorldEditException ignored) {
return false;
}
}
@Override
public final void setBlockData(Vector position, int data) {
try {
setBlock(position, new BaseBlock(getLazyBlock(position).getType(), data));
} catch (WorldEditException ignored) {
}
}
@Override
public final boolean setTypeIdAndData(Vector position, int type, int data) {
try {
return setBlock(position, new BaseBlock(type, data));
} catch (WorldEditException ignored) {
return false;
}
}
@Override
public final boolean setBlock(Vector pt, BaseBlock block) throws WorldEditException {
return setBlock(pt, block, true);
}
@Override @Override
public int getMaxY() { public int getMaxY() {
return getMaximumPoint().getBlockY(); return getMaximumPoint().getBlockY();
@ -82,56 +113,6 @@ public abstract class AbstractWorld implements World {
return getLazyBlock(pt).getData(); return getLazyBlock(pt).getData();
} }
@Override
public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException {
return setBlock(position, block, true);
}
@Override
public boolean setBlockType(Vector position, int type) {
try {
return setBlock(position, new BaseBlock(type));
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
@Override
public void setBlockData(Vector position, int data) {
try {
setBlock(position, new BaseBlock(getLazyBlock(position).getId(), data));
} catch (WorldEditException e) {
throw new RuntimeException(e);
}
}
@Override
public void setBlockDataFast(Vector position, int data) {
setBlockData(position, data);
}
@SuppressWarnings("deprecation")
@Override
public boolean setBlockTypeFast(Vector pt, int type) {
return setBlockType(pt, type);
}
@SuppressWarnings("deprecation")
@Override
public boolean setTypeIdAndData(Vector pt, int type, int data) {
boolean ret = setBlockType(pt, type);
setBlockData(pt, data);
return ret;
}
@SuppressWarnings("deprecation")
@Override
public boolean setTypeIdAndDataFast(Vector pt, int type, int data) {
boolean ret = setBlockTypeFast(pt, type);
setBlockDataFast(pt, data);
return ret;
}
@Override @Override
public void dropItem(Vector pt, BaseItemStack item, int times) { public void dropItem(Vector pt, BaseItemStack item, int times) {
for (int i = 0; i < times; ++i) { for (int i = 0; i < times; ++i) {

View File

@ -122,36 +122,18 @@ public interface World extends Extent {
@Deprecated @Deprecated
boolean setBlockType(Vector position, int type); boolean setBlockType(Vector position, int type);
/**
* @deprecated Use {@link #setBlock(Vector, BaseBlock)}
*/
@Deprecated
boolean setBlockTypeFast(Vector position, int type);
/** /**
* @deprecated Use {@link #setBlock(Vector, BaseBlock)} * @deprecated Use {@link #setBlock(Vector, BaseBlock)}
*/ */
@Deprecated @Deprecated
void setBlockData(Vector position, int data); void setBlockData(Vector position, int data);
/**
* @deprecated Use {@link #setBlock(Vector, BaseBlock)}
*/
@Deprecated
void setBlockDataFast(Vector position, int data);
/** /**
* @deprecated Use {@link #setBlock(Vector, BaseBlock)} * @deprecated Use {@link #setBlock(Vector, BaseBlock)}
*/ */
@Deprecated @Deprecated
boolean setTypeIdAndData(Vector position, int type, int data); boolean setTypeIdAndData(Vector position, int type, int data);
/**
* @deprecated Use {@link #setBlock(Vector, BaseBlock)}
*/
@Deprecated
boolean setTypeIdAndDataFast(Vector position, int type, int data);
/** /**
* Get the light level at the given block. * Get the light level at the given block.
* *