Initial abstraction.

This commit is contained in:
sk89q
2010-12-31 17:06:42 -08:00
parent 162fd3148d
commit ecce855db2
11 changed files with 1196 additions and 749 deletions

View File

@ -0,0 +1,528 @@
package com.sk89q.worldedit;
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.blocks.*;
import com.sk89q.worldedit.data.*;
import org.jnbt.*;
import java.io.*;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
/**
* The clipboard remembers the state of a cuboid region.
*
* @author sk89q
*/
public class CuboidClipboard {
/**
* Flip direction.
*/
public enum FlipDirection {
NORTH_SOUTH,
WEST_EAST,
UP_DOWN
}
private BaseBlock[][][] data;
private Vector offset;
private Vector origin;
private Vector size;
/**
* Constructs the clipboard.
*
* @param size
*/
public CuboidClipboard(Vector size) {
this.size = size;
data = new BaseBlock[size.getBlockX()][size.getBlockY()][size.getBlockZ()];
origin = new Vector();
offset = new Vector();
}
/**
* Constructs the clipboard.
*
* @param size
* @param origin
*/
public CuboidClipboard(Vector size, Vector origin) {
this.size = size;
data = new BaseBlock[size.getBlockX()][size.getBlockY()][size.getBlockZ()];
this.origin = origin;
offset = new Vector();
}
/**
* Constructs the clipboard.
*
* @param size
* @param origin
*/
public CuboidClipboard(Vector size, Vector origin, Vector offset) {
this.size = size;
data = new BaseBlock[size.getBlockX()][size.getBlockY()][size.getBlockZ()];
this.origin = origin;
this.offset = offset;
}
/**
* Get the width (X-direction) of the clipboard.
*
* @return width
*/
public int getWidth() {
return size.getBlockX();
}
/**
* Get the length (Z-direction) of the clipboard.
*
* @return length
*/
public int getLength() {
return size.getBlockZ();
}
/**
* Get the height (Y-direction) of the clipboard.
*
* @return height
*/
public int getHeight() {
return size.getBlockY();
}
/**
* Rotate the clipboard in 2D. It can only rotate by angles divisible by 90.
*
* @param angle in degrees
*/
public void rotate2D(int angle) {
angle = angle % 360;
if (angle % 90 != 0) { // Can only rotate 90 degrees at the moment
return;
}
int width = getWidth();
int length = getLength();
int height = getHeight();
Vector sizeRotated = size.transform2D(angle, 0, 0, 0, 0);
int shiftX = sizeRotated.getX() < 0 ? -sizeRotated.getBlockX() - 1 : 0;
int shiftZ = sizeRotated.getZ() < 0 ? -sizeRotated.getBlockZ() - 1 : 0;
BaseBlock newData[][][] = new BaseBlock
[Math.abs(sizeRotated.getBlockX())]
[Math.abs(sizeRotated.getBlockY())]
[Math.abs(sizeRotated.getBlockZ())];
for (int x = 0; x < width; x++) {
for (int z = 0; z < length; z++) {
Vector v = (new Vector(x, 0, z)).transform2D(angle, 0, 0, 0, 0);
int newX = v.getBlockX();
int newZ = v.getBlockZ();
for (int y = 0; y < height; y++) {
newData[shiftX + newX][y][shiftZ + newZ] = data[x][y][z];
}
}
}
data = newData;
size = new Vector(Math.abs(sizeRotated.getBlockX()),
Math.abs(sizeRotated.getBlockY()),
Math.abs(sizeRotated.getBlockZ()));
offset = offset.transform2D(angle, 0, 0, 0, 0)
.subtract(shiftX, 0, shiftZ);;
}
/**
* Flip the clipboard.
*
* @param dir
*/
public void flip(FlipDirection dir) {
int width = getWidth();
int length = getLength();
int height = getHeight();
if (dir == FlipDirection.NORTH_SOUTH) {
int len = (int)Math.floor(width / 2);
for (int xs = 0; xs < len; xs++) {
for (int z = 0; z < length; z++) {
for (int y = 0; y < height; y++) {
BaseBlock old = data[xs][y][z];
data[xs][y][z] = data[width - xs - 1][y][z];
data[width - xs - 1][y][z] = old;
}
}
}
} else if (dir == FlipDirection.WEST_EAST) {
int len = (int)Math.floor(length / 2);
for (int zs = 0; zs < len; zs++) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
BaseBlock old = data[x][y][zs];
data[x][y][zs] = data[x][y][length - zs - 1];
data[x][y][length - zs - 1] = old;
}
}
}
} else if (dir == FlipDirection.UP_DOWN) {
int len = (int)Math.floor(height / 2);
for (int ys = 0; ys < len; ys++) {
for (int x = 0; x < width; x++) {
for (int z = 0; z < length; z++) {
BaseBlock old = data[x][ys][z];
data[x][ys][z] = data[x][height - ys - 1][z];
data[x][height - ys - 1][z] = old;
}
}
}
}
}
/**
* Copy to the clipboard.
*
* @param editSession
*/
public void copy(EditSession editSession) {
for (int x = 0; x < size.getBlockX(); x++) {
for (int y = 0; y < size.getBlockY(); y++) {
for (int z = 0; z < size.getBlockZ(); z++) {
data[x][y][z] =
editSession.getBlock(new Vector(x, y, z).add(getOrigin()));
}
}
}
}
/**
* Paste from the clipboard.
*
* @param editSession
* @param newOrigin Position to paste it from
* @param noAir True to not paste air
* @throws MaxChangedBlocksException
*/
public void paste(EditSession editSession, Vector newOrigin, boolean noAir)
throws MaxChangedBlocksException {
place(editSession, newOrigin.add(offset), noAir);
}
/**
* Places the blocks in a position from the minimum corner.
*
* @param editSession
* @param pos
* @param noAir
* @throws MaxChangedBlocksException
*/
public void place(EditSession editSession, Vector pos, boolean noAir)
throws MaxChangedBlocksException {
for (int x = 0; x < size.getBlockX(); x++) {
for (int y = 0; y < size.getBlockY(); y++) {
for (int z = 0; z < size.getBlockZ(); z++) {
if (noAir && data[x][y][z].isAir())
continue;
editSession.setBlock(new Vector(x, y, z).add(pos),
data[x][y][z]);
}
}
}
}
/**
* Saves the clipboard data to a .schematic-format file.
*
* @param path
* @throws IOException
* @throws DataException
*/
public void saveSchematic(String path) throws IOException, DataException {
int width = getWidth();
int height = getHeight();
int length = getLength();
if (width > 65535) {
throw new DataException("Width of region too large for a .schematic");
}
if (height > 65535) {
throw new DataException("Height of region too large for a .schematic");
}
if (length > 65535) {
throw new DataException("Length of region too large for a .schematic");
}
HashMap<String,Tag> schematic = new HashMap<String,Tag>();
schematic.put("Width", new ShortTag("Width", (short)width));
schematic.put("Length", new ShortTag("Length", (short)length));
schematic.put("Height", new ShortTag("Height", (short)height));
schematic.put("Materials", new StringTag("Materials", "Alpha"));
schematic.put("WEOriginX", new IntTag("WEOriginX", getOrigin().getBlockX()));
schematic.put("WEOriginY", new IntTag("WEOriginY", getOrigin().getBlockY()));
schematic.put("WEOriginZ", new IntTag("WEOriginZ", getOrigin().getBlockZ()));
schematic.put("WEOffsetX", new IntTag("WEOffsetX", getOffset().getBlockX()));
schematic.put("WEOffsetY", new IntTag("WEOffsetY", getOffset().getBlockY()));
schematic.put("WEOffsetZ", new IntTag("WEOffsetZ", getOffset().getBlockZ()));
// Copy
byte[] blocks = new byte[width * height * length];
byte[] blockData = new byte[width * height * length];
ArrayList<Tag> tileEntities = new ArrayList<Tag>();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < length; z++) {
int index = y * width * length + z * width + x;
blocks[index] = (byte)data[x][y][z].getID();
blockData[index] = (byte)data[x][y][z].getData();
// Store TileEntity data
if (data[x][y][z] instanceof TileEntityBlock) {
TileEntityBlock tileEntityBlock =
(TileEntityBlock)data[x][y][z];
// Get the list of key/values from the block
Map<String,Tag> values = tileEntityBlock.toTileEntityNBT();
if (values != null) {
values.put("id", new StringTag("id",
tileEntityBlock.getTileEntityID()));
values.put("x", new IntTag("x", x));
values.put("y", new IntTag("y", y));
values.put("z", new IntTag("z", z));
CompoundTag tileEntityTag =
new CompoundTag("TileEntity", values);
tileEntities.add(tileEntityTag);
}
}
}
}
}
schematic.put("Blocks", new ByteArrayTag("Blocks", blocks));
schematic.put("Data", new ByteArrayTag("Data", blockData));
schematic.put("Entities", new ListTag("Entities", CompoundTag.class, new ArrayList<Tag>()));
schematic.put("TileEntities", new ListTag("TileEntities", CompoundTag.class, tileEntities));
// Build and output
CompoundTag schematicTag = new CompoundTag("Schematic", schematic);
NBTOutputStream stream = new NBTOutputStream(new FileOutputStream(path));
stream.writeTag(schematicTag);
stream.close();
}
/**
* Load a .schematic file into a clipboard.
*
* @param path
* @param origin
* @return clipboard
* @throws DataException
* @throws IOException
*/
public static CuboidClipboard loadSchematic(String path)
throws DataException, IOException {
FileInputStream stream = new FileInputStream(path);
NBTInputStream nbtStream = new NBTInputStream(stream);
Vector origin = new Vector();
Vector offset = new Vector();
// Schematic tag
CompoundTag schematicTag = (CompoundTag)nbtStream.readTag();
if (!schematicTag.getName().equals("Schematic")) {
throw new DataException("Tag \"Schematic\" does not exist or is not first");
}
// Check
Map<String,Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Blocks")) {
throw new DataException("Schematic file is missing a \"Blocks\" tag");
}
// Get information
short width = (Short)getChildTag(schematic, "Width", ShortTag.class).getValue();
short length = (Short)getChildTag(schematic, "Length", ShortTag.class).getValue();
short height = (Short)getChildTag(schematic, "Height", ShortTag.class).getValue();
try {
int originX = (Integer)getChildTag(schematic, "WEOriginX", IntTag.class).getValue();
int originY = (Integer)getChildTag(schematic, "WEOriginY", IntTag.class).getValue();
int originZ = (Integer)getChildTag(schematic, "WEOriginZ", IntTag.class).getValue();
origin = new Vector(originX, originY, originZ);
} catch (DataException e) {
// No origin data
}
try {
int offsetX = (Integer)getChildTag(schematic, "WEOffsetX", IntTag.class).getValue();
int offsetY = (Integer)getChildTag(schematic, "WEOffsetY", IntTag.class).getValue();
int offsetZ = (Integer)getChildTag(schematic, "WEOffsetZ", IntTag.class).getValue();
offset = new Vector(offsetX, offsetY, offsetZ);
} catch (DataException e) {
// No offset data
}
// Check type of Schematic
String materials = (String)getChildTag(schematic, "Materials", StringTag.class).getValue();
if (!materials.equals("Alpha")) {
throw new DataException("Schematic file is not an Alpha schematic");
}
// Get blocks
byte[] blocks = (byte[])getChildTag(schematic, "Blocks", ByteArrayTag.class).getValue();
byte[] blockData = (byte[])getChildTag(schematic, "Data", ByteArrayTag.class).getValue();
// Need to pull out tile entities
List<Tag> tileEntities = (List<Tag>)((ListTag)getChildTag(schematic, "TileEntities", ListTag.class))
.getValue();
Map<BlockVector,Map<String,Tag>> tileEntitiesMap =
new HashMap<BlockVector,Map<String,Tag>>();
for (Tag tag : tileEntities) {
if (!(tag instanceof CompoundTag)) continue;
CompoundTag t = (CompoundTag)tag;
int x = 0;
int y = 0;
int z = 0;
Map<String,Tag> values = new HashMap<String,Tag>();
for (Map.Entry<String,Tag> entry : t.getValue().entrySet()) {
if (entry.getKey().equals("x")) {
if (entry.getValue() instanceof IntTag) {
x = ((IntTag)entry.getValue()).getValue();
}
} else if (entry.getKey().equals("y")) {
if (entry.getValue() instanceof IntTag) {
y = ((IntTag)entry.getValue()).getValue();
}
} else if (entry.getKey().equals("z")) {
if (entry.getValue() instanceof IntTag) {
z = ((IntTag)entry.getValue()).getValue();
}
}
values.put(entry.getKey(), entry.getValue());
}
BlockVector vec = new BlockVector(x, y, z);
tileEntitiesMap.put(vec, values);
}
Vector size = new Vector(width, height, length);
CuboidClipboard clipboard = new CuboidClipboard(size);
clipboard.setOrigin(origin);
clipboard.setOffset(offset);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < length; z++) {
int index = y * width * length + z * width + x;
BlockVector pt = new BlockVector(x, y, z);
BaseBlock block;
if (blocks[index] == 63 || blocks[index] == 68) { // Signs
block = new SignBlock(blocks[index], blockData[index]);
if (tileEntitiesMap.containsKey(pt)) {
((TileEntityBlock)block).fromTileEntityNBT(
tileEntitiesMap.get(pt));
}
} else if(blocks[index] == 54) { // Chest
block = new ChestBlock();
if (tileEntitiesMap.containsKey(pt)) {
((TileEntityBlock)block).fromTileEntityNBT(
tileEntitiesMap.get(pt));
}
} else if(blocks[index] == 52) { // Mob spawner
block = new MobSpawnerBlock();
if (tileEntitiesMap.containsKey(pt)) {
((TileEntityBlock)block).fromTileEntityNBT(
tileEntitiesMap.get(pt));
}
} else {
block = new BaseBlock(blocks[index], blockData[index]);
}
clipboard.data[x][y][z] = block;
}
}
}
return clipboard;
}
/**
* Get child tag of a NBT structure.
*
* @param items
* @param key
* @param expected
* @return child tag
* @throws DataException
*/
private static Tag getChildTag(Map<String,Tag> items, String key,
Class<? extends Tag> expected) throws DataException {
if (!items.containsKey(key)) {
throw new DataException("Schematic file is missing a \"" + key + "\" tag");
}
Tag tag = items.get(key);
if (!expected.isInstance(tag)) {
throw new DataException(
key + " tag is not of tag type " + expected.getName());
}
return tag;
}
/**
* @return the origin
*/
public Vector getOrigin() {
return origin;
}
/**
* @param origin the origin to set
*/
public void setOrigin(Vector origin) {
this.origin = origin;
}
/**
* @return the offset
*/
public Vector getOffset() {
return offset;
}
/**
* @param origin the offset to set
*/
public void setOffset(Vector offset) {
this.offset = offset;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,194 @@
package com.sk89q.worldedit;
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.blocks.BaseItemStack;
/**
*
* @author sk89q
*/
public abstract class ServerInterface {
/**
* Instance.
*/
private static ServerInterface instance;
/**
* Get the current instance.
*
* @return
*/
public static ServerInterface getInstance() {
return instance;
}
/**
* Set up an instance.
* @param instance
*/
public static void setup(ServerInterface instance) {
ServerInterface.instance = instance;
}
/**
* Set block type.
*
* @param pt
* @param type
* @return
*/
public abstract boolean setBlockType(Vector pt, int type);
/**
* Get block type.
*
* @param pt
* @return
*/
public abstract int getBlockType(Vector pt);
/**
* Set block data.
*
* @param pt
* @param data
* @return
*/
public abstract void setBlockData(Vector pt, int data);
/**
* Get block data.
*
* @param pt
* @return
*/
public abstract int getBlockData(Vector pt);
/**
* Set sign text.
*
* @param pt
* @param text
*/
public abstract void setSignText(Vector pt, String[] text);
/**
* Get sign text.
*
* @param pt
* @return
*/
public abstract String[] getSignText(Vector pt);
/**
* Gets the contents of chests. Will return null if the chest does not
* really exist or it is the second block for a double chest.
*
* @param pt
* @return
*/
public abstract BaseItemStack[] getChestContents(Vector pt);
/**
* Sets a chest slot.
*
* @param pt
* @param contents
* @return
*/
public abstract boolean setChestContents(Vector pt, BaseItemStack[] contents);
/**
* Clear a chest's contents.
*
* @param pt
*/
public abstract boolean clearChest(Vector pt);
/**
* Checks if a mob type is valid.
*
* @param type
* @return
*/
public abstract boolean isValidMobType(String type);
/**
* Set mob spawner mob type.
*
* @param pt
* @param mobType
*/
public abstract void setMobSpawnerType(Vector pt, String mobType);
/**
* Get mob spawner mob type. May return an empty string.
*
* @param pt
* @param mobType
*/
public abstract String getMobSpawnerType(Vector pt);
/**
* Generate a tree at a location.
*
* @param pt
* @return
*/
public abstract boolean generateTree(EditSession editSession, Vector pt);
/**
* Drop an item.
*
* @param pt
* @param type
* @param count
* @param times
*/
public abstract void dropItem(Vector pt, int type, int count, int times);
/**
* Drop an item.
*
* @param pt
* @param type
* @param count
* @param times
*/
public abstract void dropItem(Vector pt, int type, int count);
/**
* Drop an item.
*
* @param pt
* @param type
* @param count
* @param times
*/
public abstract void dropItem(Vector pt, int type);
/**
* Simulate a block being mined.
*
* @param pt
*/
public abstract void simulateBlockMine(Vector pt);
}

View File

@ -0,0 +1,490 @@
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit;
import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.blocks.BlockType;
/**
*
* @author sk89q
*/
public abstract class WorldEditPlayer {
/**
* Directions.
*/
public enum DIRECTION {
NORTH,
NORTH_EAST,
EAST,
SOUTH_EAST,
SOUTH,
SOUTH_WEST,
WEST,
NORTH_WEST
};
/**
* Server.
*/
protected ServerInterface server;
/**
* Construct the object.
*/
protected WorldEditPlayer() {
server = ServerInterface.getInstance();
}
/**
* Returns true if the player is holding a pick axe.
*
* @return whether a pick axe is held
*/
public boolean isHoldingPickAxe() {
int item = getItemInHand();
return item == 257 || item == 270 || item == 274 || item == 278
|| item == 285;
}
/**
* Find a position for the player to stand that is not inside a block.
* Blocks above the player will be iteratively tested until there is
* a series of two free blocks. The player will be teleported to
* that free position.
*
* @param searchPos search position
*/
public void findFreePosition(Vector searchPos) {
int x = searchPos.getBlockX();
int y = Math.max(0, searchPos.getBlockY());
int origY = y;
int z = searchPos.getBlockZ();
byte free = 0;
while (y <= 129) {
if (BlockType.canPassThrough(server.getBlockType(new Vector(x, y, z)))) {
free++;
} else {
free = 0;
}
if (free == 2) {
if (y - 1 != origY) {
setPosition(new Vector(x + 0.5, y - 1, z + 0.5));
}
return;
}
y++;
}
}
/**
* Find a position for the player to stand that is not inside a block.
* Blocks above the player will be iteratively tested until there is
* a series of two free blocks. The player will be teleported to
* that free position.
*/
public void findFreePosition() {
findFreePosition(getBlockIn());
}
/**
* Go up one level to the next free space above.
*
* @return true if a spot was found
*/
public boolean ascendLevel() {
Vector pos = getBlockIn();
int x = pos.getBlockX();
int y = Math.max(0, pos.getBlockY());
int z = pos.getBlockZ();
byte free = 0;
byte spots = 0;
while (y <= 129) {
if (BlockType.canPassThrough(server.getBlockType(new Vector(x, y, z)))) {
free++;
} else {
free = 0;
}
if (free == 2) {
spots++;
if (spots == 2) {
int type = server.getBlockType(new Vector(x, y - 2, z));
// Don't get put in lava!
if (type == 10 || type == 11) {
return false;
}
setPosition(new Vector(x + 0.5, y - 1, z + 0.5));
return true;
}
}
y++;
}
return false;
}
/**
* Go up one level to the next free space above.
*
* @return true if a spot was found
*/
public boolean descendLevel() {
Vector pos = getBlockIn();
int x = pos.getBlockX();
int y = Math.max(0, pos.getBlockY() - 1);
int z = pos.getBlockZ();
byte free = 0;
while (y >= 1) {
if (BlockType.canPassThrough(server.getBlockType(new Vector(x, y, z)))) {
free++;
} else {
free = 0;
}
if (free == 2) {
// So we've found a spot, but we have to drop the player
// lightly and also check to see if there's something to
// stand upon
while (y >= 0) {
int type = server.getBlockType(new Vector(x, y, z));
// Don't want to end up in lava
if (type != 0 && type != 10 && type != 11) {
// Found a block!
setPosition(new Vector(x + 0.5, y + 1, z + 0.5));
return true;
}
y--;
}
return false;
}
y--;
}
return false;
}
/**
* Ascend to the ceiling above.
*
* @param clearance
* @return whether the player was moved
*/
public boolean ascendToCeiling(int clearance) {
Vector pos = getBlockIn();
int x = pos.getBlockX();
int initialY = Math.max(0, pos.getBlockY());
int y = Math.max(0, pos.getBlockY() + 2);
int z = pos.getBlockZ();
// No free space above
if (server.getBlockType(new Vector(x, y, z)) != 0) {
return false;
}
while (y <= 127) {
// Found a ceiling!
if (!BlockType.canPassThrough(server.getBlockType(new Vector(x, y, z)))) {
int platformY = Math.max(initialY, y - 3 - clearance);
server.setBlockType(new Vector(x, platformY, z),
BlockType.GLASS.getID());
setPosition(new Vector(x + 0.5, platformY + 1, z + 0.5));
return true;
}
y++;
}
return false;
}
/**
* Just go up.
*
* @param distance
* @return whether the player was moved
*/
public boolean ascendUpwards(int distance) {
Vector pos = getBlockIn();
int x = pos.getBlockX();
int initialY = Math.max(0, pos.getBlockY());
int y = Math.max(0, pos.getBlockY() + 1);
int z = pos.getBlockZ();
int maxY = Math.min(128, initialY + distance);
while (y <= 129) {
if (!BlockType.canPassThrough(server.getBlockType(new Vector(x, y, z)))) {
break; // Hit something
} else if (y > maxY + 1) {
break;
} else if (y == maxY + 1) {
server.setBlockType(new Vector(x, y - 2, z),
BlockType.GLASS.getID());
setPosition(new Vector(x + 0.5, y - 1, z + 0.5));
return true;
}
y++;
}
return false;
}
/**
* Get the point of the block that is being stood in.
*
* @return point
*/
public Vector getBlockIn() {
return getPosition().toBlockVector();
}
/**
* Get the point of the block that is being stood upon.
*
* @return point
*/
public Vector getBlockOn() {
return getPosition().subtract(0, 1, 0).toBlockVector();
}
/**
* Get the point of the block being looked at. May return null.
*
* @param range
* @return point
*/
public abstract Vector getBlockTrace(int range);
/**
* Get the point of the block being looked at. May return null.
*
* @param range
* @return point
*/
public abstract Vector getSolidBlockTrace(int range);
/**
* Get the player's cardinal direction (N, W, NW, etc.). May return null.
*
* @return
*/
public WorldEditPlayer.DIRECTION getCardinalDirection() {
// From hey0's code
double rot = (getYaw() - 90) % 360;
if (rot < 0) {
rot += 360.0;
}
return getDirection(rot);
}
/**
* Returns direction according to rotation. May return null.
*
* @param rot
* @return
*/
private static WorldEditPlayer.DIRECTION getDirection(double rot) {
if (0 <= rot && rot < 22.5) {
return WorldEditPlayer.DIRECTION.NORTH;
} else if (22.5 <= rot && rot < 67.5) {
return WorldEditPlayer.DIRECTION.NORTH_EAST;
} else if (67.5 <= rot && rot < 112.5) {
return WorldEditPlayer.DIRECTION.EAST;
} else if (112.5 <= rot && rot < 157.5) {
return WorldEditPlayer.DIRECTION.SOUTH_EAST;
} else if (157.5 <= rot && rot < 202.5) {
return WorldEditPlayer.DIRECTION.SOUTH;
} else if (202.5 <= rot && rot < 247.5) {
return WorldEditPlayer.DIRECTION.SOUTH_WEST;
} else if (247.5 <= rot && rot < 292.5) {
return WorldEditPlayer.DIRECTION.WEST;
} else if (292.5 <= rot && rot < 337.5) {
return WorldEditPlayer.DIRECTION.NORTH_WEST;
} else if (337.5 <= rot && rot < 360.0) {
return WorldEditPlayer.DIRECTION.NORTH;
} else {
return null;
}
}
/**
* Get the ID of the item that the player is holding.
*
* @return
*/
public abstract int getItemInHand();
/**
* Get the name of the player.
*
* @return String
*/
public abstract String getName();
/**
* Get the player's position.
*
* @return point
*/
public abstract Vector getPosition();
/**
* Get the player's view pitch.
*
* @return pitch
*/
/**
* Get the player's view pitch.
*
* @return pitch
*/
public abstract double getPitch();
/**
* Get the player's view yaw.
*
* @return yaw
*/
/**
* Get the player's view yaw.
*
* @return yaw
*/
public abstract double getYaw();
/**
* Gives the player an item.
*
* @param type
* @param amt
*/
public abstract void giveItem(int type, int amt);
/**
* Pass through the wall that you are looking at.
*
* @param range
* @return whether the player was pass through
*/
public abstract boolean passThroughForwardWall(int range);
/**
* Print a message.
*
* @param msg
*/
public abstract void printRaw(String msg);
/**
* Print a WorldEdit message.
*
* @param msg
*/
public abstract void print(String msg);
/**
* Print a WorldEdit error.
*
* @param msg
*/
public abstract void printError(String msg);
/**
* Move the player.
*
* @param pos
* @param pitch
* @param yaw
*/
public abstract void setPosition(Vector pos, float pitch, float yaw);
/**
* Move the player.
*
* @param pos
*/
public void setPosition(Vector pos) {
setPosition(pos, (float)getPitch(), (float)getYaw());
}
/**
* Get a player's list of groups.
*
* @return
*/
public abstract String[] getGroups();
/**
* Get this player's block bag.
*
* @return
*/
public abstract BlockBag getInventoryBlockBag();
/**
* Checks if a player has permission.
*
* @param perm
* @return
*/
public abstract boolean hasPermission(String perm);
/**
* Returns true if equal.
*
* @param other
* @return whether the other object is equivalent
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof WorldEditPlayer)) {
return false;
}
WorldEditPlayer other2 = (WorldEditPlayer)other;
return other2.getName().equals(getName());
}
/**
* Gets the hash code.
*
* @return hash code
*/
@Override
public int hashCode() {
return getName().hashCode();
}
}

View File

@ -0,0 +1,430 @@
package com.sk89q.worldedit;
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import com.sk89q.worldedit.snapshots.Snapshot;
import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.*;
import java.util.LinkedList;
/**
*
* @author sk89q
*/
public class WorldEditSession {
/**
* List of super pick axe modes.
*/
public static enum SuperPickaxeMode {
SINGLE,
SAME_TYPE_RECURSIVE,
SAME_TYPE_AREA
};
/**
* List of tools.
*/
public static enum Tool {
NONE,
INFO,
TREE,
}
public static final int MAX_HISTORY_SIZE = 15;
private boolean placeAtPos1 = false;
private Vector pos1, pos2;
private Region region;
private LinkedList<EditSession> history = new LinkedList<EditSession>();
private int historyPointer = 0;
private CuboidClipboard clipboard;
private boolean toolControl = true;
private boolean superPickAxe = false;
private SuperPickaxeMode superPickaxeMode = SuperPickaxeMode.SINGLE;
private Tool tool = Tool.NONE;
private int superPickaxeRange = 3;
private int maxBlocksChanged = -1;
private boolean useInventory;
private Snapshot snapshot;
/**
* Clear history.
*/
public void clearHistory() {
history.clear();
historyPointer = 0;
}
/**
* Get the edit session.
*/
public void remember(EditSession editSession) {
// Don't store anything if no changes were made
if (editSession.size() == 0) { return; }
// Destroy any sessions after this undo point
while (historyPointer < history.size()) {
history.remove(historyPointer);
}
history.add(editSession);
while (history.size() > MAX_HISTORY_SIZE) {
history.remove(0);
}
historyPointer = history.size();
}
/**
* Undo.
*
* @return whether anything was undone
*/
public EditSession undo(BlockBag newBlockBag) {
historyPointer--;
if (historyPointer >= 0) {
EditSession editSession = history.get(historyPointer);
editSession.setBlockBag(newBlockBag);
editSession.undo();
return editSession;
} else {
historyPointer = 0;
return null;
}
}
/**
* Redo.
*
* @return whether anything was redone
*/
public EditSession redo(BlockBag newBlockBag) {
if (historyPointer < history.size()) {
EditSession editSession = history.get(historyPointer);
editSession.setBlockBag(newBlockBag);
editSession.redo();
historyPointer++;
return editSession;
}
return null;
}
/**
* Checks to make sure that position 1 is defined.
*
* @throws IncompleteRegionException
*/
private void checkPos1() throws IncompleteRegionException {
if (pos1 == null) {
throw new IncompleteRegionException();
}
}
/**
* Checks to make sure that position 2 is defined.
*
* @throws IncompleteRegionException
*/
private void checkPos2() throws IncompleteRegionException {
if (pos2 == null) {
throw new IncompleteRegionException();
}
}
/**
* Returns true if the region is fully defined.
*
* @throws IncompleteRegionException
*/
public boolean isRegionDefined() {
return pos1 != null && pos2 != null;
}
/**
* Gets defined position 1.
*
* @return position 1
* @throws IncompleteRegionException
*/
public Vector getPos1() throws IncompleteRegionException {
checkPos1();
return pos1;
}
/**
* Sets position 1.
*
* @param pt
*/
public void setPos1(Vector pt) {
pos1 = pt;
if (pos1 != null && pos2 != null) {
region = new CuboidRegion(pos1, pos2);
}
}
/**
* Gets position 2.
*
* @return position 2
* @throws IncompleteRegionException
*/
public Vector getPos2() throws IncompleteRegionException {
checkPos2();
return pos2;
}
/**
* Sets position 2.
*
* @param pt
*/
public void setPos2(Vector pt) {
pos2 = pt;
if (pos1 != null && pos2 != null) {
region = new CuboidRegion(pos1, pos2);
}
}
/**
* Update session position 1/2 based on the currently set region,
* provided that the region is of a cuboid.
*/
public void learnRegionChanges() {
if (region instanceof CuboidRegion) {
CuboidRegion cuboidRegion = (CuboidRegion)region;
pos1 = cuboidRegion.getPos1();
pos2 = cuboidRegion.getPos2();
}
}
/**
* Get the region. If you change the region, you should
* call learnRegionChanges().
*
* @return region
* @throws IncompleteRegionException
*/
public Region getRegion() throws IncompleteRegionException {
if (region == null) {
throw new IncompleteRegionException();
}
return region;
}
/**
* Gets the clipboard.
*
* @return clipboard, may be null
* @throws EmptyClipboardException
*/
public CuboidClipboard getClipboard() throws EmptyClipboardException {
if (clipboard == null) {
throw new EmptyClipboardException();
}
return clipboard;
}
/**
* Sets the clipboard.
*
* @param clipboard
*/
public void setClipboard(CuboidClipboard clipboard) {
this.clipboard = clipboard;
}
/**
* See if tool control is enabled.
*
* @return true if enabled
*/
public boolean isToolControlEnabled() {
return toolControl;
}
/**
* Change tool control setting.
*
* @param toolControl
*/
public void setToolControl(boolean toolControl) {
this.toolControl = toolControl;
}
/**
* Get the maximum number of blocks that can be changed in an edit session.
*
* @return block change limit
*/
public int getBlockChangeLimit() {
return maxBlocksChanged;
}
/**
* Set the maximum number of blocks that can be changed.
*
* @param maxBlocksChanged
*/
public void setBlockChangeLimit(int maxBlocksChanged) {
this.maxBlocksChanged = maxBlocksChanged;
}
/**
* Checks whether the super pick axe is enabled.
*
* @return status
*/
public boolean hasSuperPickAxe() {
return superPickAxe;
}
/**
* Enable super pick axe.
*
* @param superPickAxe
*/
public void enableSuperPickAxe() {
superPickAxe = true;
}
/**
* Disable super pick axe.
*
* @param superPickAxe
*/
public void disableSuperPickAxe() {
superPickAxe = false;
}
/**
* Toggle the super pick axe.
*
* @return status
*/
public boolean toggleSuperPickAxe() {
superPickAxe = !superPickAxe;
return superPickAxe;
}
/**
* @return position
* @throws IncompleteRegionException
*/
public Vector getPlacementPosition(WorldEditPlayer player)
throws IncompleteRegionException {
if (!placeAtPos1) {
return player.getBlockIn();
}
checkPos1();
return pos1;
}
/**
* Toggle placement position;
*/
public boolean togglePlacementPosition() {
placeAtPos1 = !placeAtPos1;
return placeAtPos1;
}
/**
* Get a block bag for a player.
*
* @param player
* @return
*/
public BlockBag getBlockBag(WorldEditPlayer player) {
if (!useInventory) {
return null;
}
return player.getInventoryBlockBag();
}
/**
* @return the snapshotName
*/
public Snapshot getSnapshot() {
return snapshot;
}
/**
* @param snapshot
*/
public void setSnapshot(Snapshot snapshot) {
this.snapshot = snapshot;
}
/**
* @return the superPickaxeMode
*/
public SuperPickaxeMode getSuperPickaxeMode() {
return superPickaxeMode;
}
/**
* @param superPickaxeMode the superPickaxeMode to set
*/
public void setSuperPickaxeMode(SuperPickaxeMode superPickaxeMode) {
this.superPickaxeMode = superPickaxeMode;
}
/**
* @return the superPickaxeRange
*/
public int getSuperPickaxeRange() {
return superPickaxeRange;
}
/**
* @param superPickaxeRange the superPickaxeRange to set
*/
public void setSuperPickaxeRange(int superPickaxeRange) {
this.superPickaxeRange = superPickaxeRange;
}
/**
* @return the tool
*/
public Tool getTool() {
return tool;
}
/**
* @param tool the tool to set
*/
public void setTool(Tool tool) {
this.tool = tool;
}
/**
* @return the useInventory
*/
public boolean isUsingInventory() {
return useInventory;
}
/**
* @param useInventory the useInventory to set
*/
public void setUseInventory(boolean useInventory) {
this.useInventory = useInventory;
}
}

View File

@ -0,0 +1,205 @@
package com.sk89q.worldedit.snapshots;
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.regions.*;
import com.sk89q.worldedit.blocks.*;
import com.sk89q.worldedit.data.*;
import java.io.IOException;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ArrayList;
/**
*
* @author sk89q
*/
public class SnapshotRestore {
/**
* Store a list of chunks that are needed and the points in them.
*/
private Map<BlockVector2D,ArrayList<Vector>> neededChunks =
new LinkedHashMap<BlockVector2D,ArrayList<Vector>>();
/**
* Chunk store.
*/
private ChunkStore chunkStore;
/**
* Count of the number of missing chunks.
*/
private ArrayList<Vector2D> missingChunks;
/**
* Count of the number of chunks that could be loaded for other reasons.
*/
private ArrayList<Vector2D> errorChunks;
/**
* Last error message.
*/
private String lastErrorMessage;
/**
* Construct the snapshot restore operation.
*
* @param region
*/
public SnapshotRestore(ChunkStore chunkStore, Region region) {
this.chunkStore = chunkStore;
if (region instanceof CuboidRegion) {
findNeededCuboidChunks(region);
} else {
findNeededChunks(region);
}
}
/**
* Find needed chunks in the cuboid of the region.
*
* @param region
*/
private void findNeededCuboidChunks(Region region) {
Vector min = region.getMinimumPoint();
Vector max = region.getMaximumPoint();
// First, we need to group points by chunk so that we only need
// to keep one chunk in memory at any given moment
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
Vector pos = new Vector(x, y, z);
BlockVector2D chunkPos = ChunkStore.toChunk(pos);
// Unidentified chunk
if (!neededChunks.containsKey(chunkPos)) {
neededChunks.put(chunkPos, new ArrayList<Vector>());
}
neededChunks.get(chunkPos).add(pos);
}
}
}
}
/**
* Find needed chunks in the region.
*
* @param region
*/
private void findNeededChunks(Region region) {
// First, we need to group points by chunk so that we only need
// to keep one chunk in memory at any given moment
for (Vector pos : region) {
BlockVector2D chunkPos = ChunkStore.toChunk(pos);
// Unidentified chunk
if (!neededChunks.containsKey(chunkPos)) {
neededChunks.put(chunkPos, new ArrayList<Vector>());
}
neededChunks.get(chunkPos).add(pos);
}
}
/**
* Get the number of chunks that are needed.
*
* @return
*/
public int getChunksAffected() {
return neededChunks.size();
}
/**
* Restores to world.
*
* @param editSession
* @param region
*/
public void restore(EditSession editSession)
throws MaxChangedBlocksException {
missingChunks = new ArrayList<Vector2D>();
errorChunks = new ArrayList<Vector2D>();
// Now let's start restoring!
for (Map.Entry<BlockVector2D,ArrayList<Vector>> entry :
neededChunks.entrySet()) {
BlockVector2D chunkPos = entry.getKey();
Chunk chunk;
try {
chunk = chunkStore.getChunk(chunkPos);
// Good, the chunk could be at least loaded
// Now just copy blocks!
for (Vector pos : entry.getValue()) {
BaseBlock block = chunk.getBlock(pos);
editSession.setBlock(pos, block);
}
} catch (MissingChunkException me) {
missingChunks.add(chunkPos);
} catch (DataException de) {
errorChunks.add(chunkPos);
lastErrorMessage = de.getMessage();
} catch (IOException ioe) {
errorChunks.add(chunkPos);
lastErrorMessage = ioe.getMessage();
}
}
}
/**
* Get a list of the missing chunks. restore() must have been called
* already.
*
* @return
*/
public List<Vector2D> getMissingChunks() {
return missingChunks;
}
/**
* Get a list of the chunks that could not have been loaded for other
* reasons. restore() must have been called already.
*
* @return
*/
public List<Vector2D> getErrorChunks() {
return errorChunks;
}
/**
* Checks to see where the backup succeeded in any capacity. False will
* be returned if no chunk could be successfully loaded.
*
* @return
*/
public boolean hadTotalFailure() {
return missingChunks.size() + errorChunks.size() == getChunksAffected();
}
/**
* @return the lastErrorMessage
*/
public String getLastErrorMessage() {
return lastErrorMessage;
}
}