FastAsyncWorldEdit/src/com/sk89q/worldedit/WorldEditController.java

2016 Zeilen
78 KiB
Java

2011-01-01 02:40:07 +01:00
// $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 java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.*;
import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.blocks.*;
import com.sk89q.worldedit.data.*;
import com.sk89q.worldedit.filters.*;
import com.sk89q.worldedit.snapshots.*;
import com.sk89q.worldedit.superpickaxe.*;
2011-01-01 02:40:07 +01:00
import com.sk89q.worldedit.regions.*;
import com.sk89q.worldedit.patterns.*;
/**
* Plugin base.
*
* @author sk89q
*/
public class WorldEditController {
/**
* Logger.
*/
private static final Logger logger = Logger.getLogger("Minecraft.WorldEdit");
/**
* Default list of allowed block types.
*/
private final static Integer[] DEFAULT_ALLOWED_BLOCKS = {
0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 35, 41, 42, 43, 44, 45, 47, 48, 49, 52, 53, 54, 56, 57, 58, 60,
61, 62, 67, 73, 78, 79, 80, 82, 85, 86, 87, 88, 89, 91
};
/**
* Server interface.
*/
private ServerInterface server;
/**
* WorldEdit configuration.
*/
private LocalConfiguration config;
2011-01-01 02:40:07 +01:00
/**
* Stores a list of WorldEdit sessions, keyed by players' names. Sessions
* persist only for the user's session. On disconnect, the session will be
* removed. Sessions are created only when they are needed and those
* without any WorldEdit abilities or never use WorldEdit in a session will
* not have a session object generated for them.
*/
private HashMap<LocalPlayer,LocalSession> sessions =
new HashMap<LocalPlayer,LocalSession>();
2011-01-01 02:40:07 +01:00
/**
* List of commands. These are checked when onCommand() is called, so
* the list must know about every command. On plugin load, the commands
* will be loaded into help. On unload, they will be removed.
*/
private HashMap<String,String> commands = new HashMap<String,String>();
2011-01-01 02:40:07 +01:00
/**
* Construct an instance of the plugin
*
* @param server
* @param config
2011-01-01 02:40:07 +01:00
*/
public WorldEditController(ServerInterface server, LocalConfiguration config) {
this.server = server;
this.config = config;
2011-01-01 02:40:07 +01:00
// Note: Commands should only have the phrase 'air' at the end
// for now (see SMWorldEditListener.canUseCommand)
commands.put("//pos1", "Set editing position #1");
commands.put("//pos2", "Set editing position #2");
commands.put("//hpos1", "Trace editing position #1");
commands.put("//hpos2", "Trace editing position #2");
commands.put("//chunk", "Select the chunk that you are in");
commands.put("/toggleplace", "Toggle placing at pos #1");
commands.put("//wand", "Gives you the \"edit wand\"");
commands.put("/toggleeditwand", "Toggles edit wand selection");
commands.put("//", "Toggles super pick axe.");
commands.put("//undo", "Undo");
commands.put("//redo", "Redo");
commands.put("/clearhistory", "Clear history");
commands.put("/clearclipboard", "Clear clipboard");
commands.put("//size", "Get size of selected region");
commands.put("//count", "[BlockIDs] - Count the number of blocks in the region");
commands.put("//distr", "Get the top block distribution");
commands.put("//set", "[ID] - Set all blocks inside region");
commands.put("//outline", "[ID] - Outline the region with blocks");
commands.put("//walls", "[ID] - Build walls");
commands.put("//replace", "<FromID> [ToID] - Replace all existing blocks inside region");
commands.put("/replacenear", "<Size> <FromID> [ToID] - Replace all existing blocks nearby");
commands.put("//overlay", "[ID] - Overlay the area one layer");
commands.put("/removeabove", "<Size> <Height> - Remove blocks above head");
commands.put("/removebelow", "<Size> <Height> - Remove blocks below position");
commands.put("/removenear", "<ID> <Size> - Remove blocks near you");
commands.put("//copy", "Copies the currently selected region");
commands.put("//cut", "Cuts the currently selected region");
commands.put("//paste", "<AtOrigin?> - Pastes the clipboard");
commands.put("//pasteair", "<AtOrigin?> - Pastes the clipboard (with air)");
commands.put("//move", "<Count> <Dir> <LeaveID> - Move the selection");
commands.put("//moveair", "<Count> <Dir> <LeaveID> - Move the selection (with air)");
commands.put("//stack", "<Count> <Dir> - Stacks the selection");
commands.put("//stackair", "<Count> <Dir> - Stacks the selection (with air)");
commands.put("//load", "[Filename] - Load .schematic into clipboard");
commands.put("//save", "[Filename] - Save clipboard to .schematic");
commands.put("//fill", "[ID] [Radius] <Depth> - Fill a hole");
commands.put("//fillr", "[ID] [Radius] - Fill a hole fully recursively");
commands.put("//drain", "[Radius] - Drain nearby water/lava pools");
commands.put("//limit", "[Num] - See documentation");
commands.put("/single", "Switch to single block super pickaxe mode");
commands.put("/area", "[Range] - Switch to area super pickaxe mode");
commands.put("/recur", "[Range] - Switch to recursive super pickaxe mode");
commands.put("/none", "Switch to no tool");
commands.put("/info", "Switch to the info tool");
commands.put("/tree", "Switch to the tree tool");
commands.put("/repl", "[ID] - Switch to the block replacer tool");
2011-01-01 02:40:07 +01:00
commands.put("//expand", "[Num] <Dir> - Expands the selection");
commands.put("//contract", "[Num] <Dir> - Contracts the selection");
commands.put("//shift", "[Num] <Dir> - Shift the selection");
commands.put("//rotate", "[Angle] - Rotate the clipboard");
commands.put("//flip", "<Dir> - Flip the clipboard");
commands.put("//hcyl", "[ID] [Radius] <Height> - Create a vertical hollow cylinder");
commands.put("//cyl", "[ID] [Radius] <Height> - Create a vertical cylinder");
commands.put("//sphere", "[ID] [Radius] <Raised?> - Create a sphere");
commands.put("//hsphere", "[ID] [Radius] <Raised?> - Create a hollow sphere");
commands.put("/fixwater", "[Radius] - Level nearby pools of water");
commands.put("/fixlava", "[Radius] - Level nearby pools of lava");
commands.put("/ex", "[Size] - Extinguish fires");
commands.put("/forestgen", "<Size> <Density> - Make Notch tree forest");
commands.put("/pinegen", "<Size> <Density> - Make an ugly pine tree forest");
commands.put("/snow", "<Radius> - Simulate snow cover");
commands.put("/pumpkins", "<Size> - Make a pumpkin forest");
commands.put("/unstuck", "Go up to the first free spot");
commands.put("/ascend", "Go up one level");
commands.put("/descend", "Go down one level");
commands.put("/jumpto", "Jump to the block that you are looking at");
commands.put("/thru", "Go through the wall that you are looking at");
commands.put("/ceil", "<Clearance> - Get to the ceiling");
commands.put("/up", "<Distance> - Go up some distance");
commands.put("/chunkinfo", "Get the filename of the chunk that you are in");
commands.put("/listchunks", "Print a list of used chunks");
commands.put("/delchunks", "Generate a shell script to delete chunks");
commands.put("/listsnapshots", "<Num> - List the 5 newest snapshots");
commands.put("/butcher", "<Radius> - Kill nearby mobs");
commands.put("//use", "[SnapshotID] - Use a particular snapshot");
commands.put("//restore", "<SnapshotID> - Restore a particular snapshot");
commands.put("//smooth", "<Iterations> - Smooth an area's heightmap");
}
/**
* Gets the WorldEdit session for a player.
2011-01-01 02:40:07 +01:00
*
* @param player
* @return
*/
public LocalSession getSession(LocalPlayer player) {
2011-01-01 02:40:07 +01:00
if (sessions.containsKey(player)) {
return sessions.get(player);
}
LocalSession session = new LocalSession();
// Set the limit on the number of blocks that an operation can
// change at once, or don't if the player has an override or there
// is no limit. There is also a default limit
if (!player.hasPermission("worldeditnomax")
&& config.maxChangeLimit > -1) {
// If the default limit is infinite but there is a maximum
// limit, make sure to not have it be overridden
if (config.defaultChangeLimit < 0) {
session.setBlockChangeLimit(config.maxChangeLimit);
2011-01-01 02:40:07 +01:00
} else {
// Bound the change limit
int limit = Math.min(config.defaultChangeLimit,
config.maxChangeLimit);
session.setBlockChangeLimit(limit);
2011-01-01 02:40:07 +01:00
}
} else {
// No change limit or override
session.setBlockChangeLimit(config.defaultChangeLimit);
2011-01-01 02:40:07 +01:00
}
// Have the session use inventory if it's enabled and the player
// doesn't have an override
session.setUseInventory(config.useInventory
&& (!config.useInventoryOverride
|| !player.hasPermission("worldeditunlimited")));
// Remember the session
sessions.put(player, session);
return session;
2011-01-01 02:40:07 +01:00
}
/**
* Returns true if the player has a session.
*
* @param player
* @return
*/
public boolean hasSession(LocalPlayer player) {
2011-01-01 02:40:07 +01:00
return sessions.containsKey(player);
}
/**
* Get an item ID from an item name or an item ID number.
*
* @param player
2011-01-01 02:40:07 +01:00
* @param arg
* @param allAllowed true to ignore blacklists
2011-01-01 02:40:07 +01:00
* @return
* @throws UnknownItemException
* @throws DisallowedItemException
*/
public BaseBlock getBlock(LocalPlayer player, String arg, boolean allAllowed)
2011-01-01 02:40:07 +01:00
throws UnknownItemException, DisallowedItemException {
BlockType blockType;
arg = arg.replace("_", " ");
arg = arg.replace(";", "|");
String[] args0 = arg.split("\\|");
String[] args1 = args0[0].split(":", 2);
String testID = args1[0];
int data;
// Parse the block data (optional)
2011-01-01 02:40:07 +01:00
try {
data = args1.length > 1 ? Integer.parseInt(args1[1]) : 0;
if (data > 15 || data < 0) {
data = 0;
}
} catch (NumberFormatException e) {
data = 0;
}
// Attempt to parse the item ID or otherwise resolve an item/block
// name to its numeric ID
2011-01-01 02:40:07 +01:00
try {
blockType = BlockType.fromID(Integer.parseInt(testID));
} catch (NumberFormatException e) {
blockType = BlockType.lookup(testID);
if (blockType == null) {
int t = server.resolveItem(testID);
if (t > 0 && t < 256) {
blockType = BlockType.fromID(t);
}
}
}
if (blockType == null) {
throw new UnknownItemException(arg);
}
// Check if the item is allowed
if (allAllowed || player.hasPermission("worldeditanyblock")
|| !config.disallowedBlocks.contains(blockType.getID())) {
// Allow special sign text syntax
2011-01-01 02:40:07 +01:00
if (blockType == BlockType.SIGN_POST
|| blockType == BlockType.WALL_SIGN) {
String[] text = new String[4];
text[0] = args0.length > 1 ? args0[1] : "";
text[1] = args0.length > 2 ? args0[2] : "";
text[2] = args0.length > 3 ? args0[3] : "";
text[3] = args0.length > 4 ? args0[4] : "";
return new SignBlock(blockType.getID(), data, text);
// Alow setting mob spawn type
2011-01-01 02:40:07 +01:00
} else if (blockType == BlockType.MOB_SPAWNER) {
if (args0.length > 1) {
if (!server.isValidMobType(args0[1])) {
throw new InvalidItemException(arg, "Unknown mob type '" + args0[1] + "'");
}
return new MobSpawnerBlock(data, args0[1]);
} else {
return new MobSpawnerBlock(data, "Pig");
}
}
return new BaseBlock(blockType.getID(), data);
}
throw new DisallowedItemException(arg);
}
/**
* Get a block.
*
* @param player
2011-01-01 02:40:07 +01:00
* @param id
* @return
* @throws UnknownItemException
* @throws DisallowedItemException
*/
public BaseBlock getBlock(LocalPlayer player, String id)
throws UnknownItemException, DisallowedItemException {
return getBlock(player, id, false);
2011-01-01 02:40:07 +01:00
}
/**
* Get a list of blocks as a set. This returns a Pattern.
*
* @param player
2011-01-01 02:40:07 +01:00
* @param list
* @return pattern
*/
public Pattern getBlockPattern(LocalPlayer player, String list)
2011-01-01 02:40:07 +01:00
throws UnknownItemException, DisallowedItemException {
String[] items = list.split(",");
if (items.length == 1) {
return new SingleBlockPattern(getBlock(player, items[0]));
2011-01-01 02:40:07 +01:00
}
List<BlockChance> blockChances = new ArrayList<BlockChance>();
for (String s : items) {
BaseBlock block;
double chance;
if (s.matches("[0-9]+(?:\\.(?:[0-9]+)?)?%.*")) {
String[] p = s.split("%");
chance = Double.parseDouble(p[0]);
block = getBlock(player, p[1]);
2011-01-01 02:40:07 +01:00
} else {
chance = 1;
block = getBlock(player, s);
2011-01-01 02:40:07 +01:00
}
blockChances.add(new BlockChance(block, chance));
}
return new RandomFillPattern(blockChances);
}
/**
* Get a list of blocks as a set.
*
*@param player
2011-01-01 02:40:07 +01:00
* @param list
* @param allBlocksAllowed
2011-01-01 02:40:07 +01:00
* @return set
*/
public Set<Integer> getBlockIDs(LocalPlayer player,
String list, boolean allBlocksAllowed)
2011-01-01 02:40:07 +01:00
throws UnknownItemException, DisallowedItemException {
2011-01-01 02:40:07 +01:00
String[] items = list.split(",");
Set<Integer> blocks = new HashSet<Integer>();
for (String s : items) {
blocks.add(getBlock(player, s, allBlocksAllowed).getID());
2011-01-01 02:40:07 +01:00
}
return blocks;
}
/**
* Checks to make sure that there are enough but not too many arguments.
*
* @param args
* @param min
* @param max -1 for no maximum
* @param cmd command name
* @throws InsufficientArgumentsException
*/
private void checkArgs(String[] args, int min, int max, String cmd)
throws InsufficientArgumentsException {
if (args.length <= min || (max != -1 && args.length - 1 > max)) {
if (commands.containsKey(cmd)) {
throw new InsufficientArgumentsException(cmd + " usage: " +
commands.get(cmd));
} else {
throw new InsufficientArgumentsException("Invalid number of arguments");
}
}
}
/**
* Checks to see if the specified radius is within bounds.
*
* @param radius
* @throws MaxRadiusException
*/
private void checkMaxRadius(int radius) throws MaxRadiusException {
if (config.maxRadius > 0 && radius > config.maxRadius) {
2011-01-01 02:40:07 +01:00
throw new MaxRadiusException();
}
}
/**
* The main meat of command processing.
*
* @param player
* @param editPlayer
* @param session
* @param editSession
* @param split
* @return
* @throws UnknownItemException
* @throws IncompleteRegionException
* @throws InsufficientArgumentsException
* @throws DisallowedItemException
*/
public boolean performCommand(LocalPlayer player,
LocalSession session, EditSession editSession, String[] split)
2011-01-01 02:40:07 +01:00
throws WorldEditException
{
if (config.logComands) {
2011-01-01 02:40:07 +01:00
logger.log(Level.INFO, "WorldEdit: " + player.getName() + ": "
+ joinString(split, " "));
}
LocalWorld world = player.getPosition().getWorld();
2011-01-01 02:40:07 +01:00
// Jump to the first free position
if (split[0].equalsIgnoreCase("/unstuck")) {
checkArgs(split, 0, 0, split[0]);
player.print("There you go!");
player.findFreePosition();
return true;
// Ascend a level
} else if(split[0].equalsIgnoreCase("/ascend")) {
checkArgs(split, 0, 0, split[0]);
if (player.ascendLevel()) {
player.print("Ascended a level.");
} else {
player.printError("No free spot above you found.");
}
return true;
// Descend a level
} else if(split[0].equalsIgnoreCase("/descend")) {
checkArgs(split, 0, 0, split[0]);
if (player.descendLevel()) {
player.print("Descended a level.");
} else {
player.printError("No free spot below you found.");
}
return true;
// Jump to the block in sight
} else if (split[0].equalsIgnoreCase("/jumpto")) {
checkArgs(split, 0, 0, split[0]);
WorldVector pos = player.getSolidBlockTrace(300);
2011-01-01 02:40:07 +01:00
if (pos != null) {
player.findFreePosition(pos.getWorld(), pos);
2011-01-01 02:40:07 +01:00
player.print("Poof!");
} else {
player.printError("No block in sight!");
}
return true;
// Go through a wall
} else if (split[0].equalsIgnoreCase("/thru")) {
checkArgs(split, 0, 0, split[0]);
if (player.passThroughForwardWall(6)) {
player.print("Whoosh!");
} else {
player.printError("No free spot ahead of you found.");
}
return true;
// Go to the ceiling
} else if (split[0].equalsIgnoreCase("/ceil")) {
checkArgs(split, 0, 1, split[0]);
int clearence = split.length > 1 ?
Math.max(0, Integer.parseInt(split[1])) : 0;
if (player.ascendToCeiling(clearence)) {
player.print("Whoosh!");
} else {
player.printError("No free spot above you found.");
}
return true;
// Go up
} else if (split[0].equalsIgnoreCase("/up")) {
checkArgs(split, 1, 1, split[0]);
int distance = Integer.parseInt(split[1]);
if (player.ascendUpwards(distance)) {
player.print("Whoosh!");
} else {
player.printError("You would hit something above you.");
}
return true;
// Set edit position #1
} else if (split[0].equalsIgnoreCase("//pos1")) {
checkArgs(split, 0, 0, split[0]);
session.setPos1(player.getBlockIn());
if (session.isRegionDefined()) {
player.print("First position set to " + player.getBlockIn()
+ " (" + session.getRegion().getSize() + ").");
} else {
player.print("First position set to " + player.getBlockIn() + ".");
}
return true;
// Set edit position #2
} else if (split[0].equalsIgnoreCase("//pos2")) {
checkArgs(split, 0, 0, split[0]);
session.setPos2(player.getBlockIn());
if (session.isRegionDefined()) {
player.print("Second position set to " + player.getBlockIn()
+ " (" + session.getRegion().getSize() + ").");
} else {
player.print("Second position set to " + player.getBlockIn() + ".");
}
return true;
// Trace edit position #1
} else if (split[0].equalsIgnoreCase("//hpos1")) {
checkArgs(split, 0, 0, split[0]);
Vector pos = player.getBlockTrace(300);
if (pos != null) {
session.setPos1(pos);
if (session.isRegionDefined()) {
player.print("First position set to " + pos
+ " (" + session.getRegion().getSize() + ").");
} else {
player.print("First position set to " + pos.toString() + " .");
}
} else {
player.printError("No block in sight!");
}
return true;
// Trace edit position #2
} else if (split[0].equalsIgnoreCase("//hpos2")) {
checkArgs(split, 0, 0, split[0]);
Vector pos = player.getBlockTrace(300);
if (pos != null) {
session.setPos2(pos);
if (session.isRegionDefined()) {
player.print("Second position set to " + pos
+ " (" + session.getRegion().getSize() + ").");
} else {
player.print("Second position set to " + pos.toString() + " .");
}
} else {
player.printError("No block in sight!");
}
return true;
// Select the chunk
} else if(split[0].equalsIgnoreCase("//chunk")) {
checkArgs(split, 0, 0, split[0]);
Vector2D min2D = ChunkStore.toChunk(player.getBlockIn());
Vector min = new Vector(min2D.getBlockX() * 16, 0, min2D.getBlockZ() * 16);
Vector max = min.add(15, 127, 15);
session.setPos1(min);
session.setPos2(max);
player.print("Chunk selected: "
+ min2D.getBlockX() + ", " + min2D.getBlockZ());
return true;
// Edit wand
} else if (split[0].equalsIgnoreCase("//wand")) {
checkArgs(split, 0, 0, split[0]);
player.giveItem(config.wandItem, 1);
2011-01-01 02:40:07 +01:00
player.print("Left click: select pos #1; Right click: select pos #2");
return true;
// Toggle placing at pos #1
} else if (split[0].equalsIgnoreCase("/toggleplace")) {
checkArgs(split, 0, 0, split[0]);
if (session.togglePlacementPosition()) {
player.print("Now placing at pos #1.");
} else {
player.print("Now placing at the block you stand in.");
}
return true;
// Toggle edit wand
} else if (split[0].equalsIgnoreCase("/toggleeditwand")) {
checkArgs(split, 0, 0, split[0]);
session.setToolControl(!session.isToolControlEnabled());
if (session.isToolControlEnabled()) {
player.print("Edit wand enabled.");
} else {
player.print("Edit wand disabled.");
}
return true;
// Toggle super pick axe
} else if (split[0].equalsIgnoreCase("//")) {
checkArgs(split, 0, 0, split[0]);
if (session.toggleSuperPickAxe()) {
player.print("Super pick axe enabled.");
} else {
player.print("Super pick axe disabled.");
}
return true;
// Set max number of blocks to change at a time
} else if (split[0].equalsIgnoreCase("//limit")) {
checkArgs(split, 1, 1, split[0]);
int limit = Math.max(-1, Integer.parseInt(split[1]));
if (!player.hasPermission("worldeditnomax")
&& config.maxChangeLimit > -1) {
if (limit > config.maxChangeLimit) {
player.printError("Your maximum allowable limit is "
+ config.maxChangeLimit + ".");
2011-01-01 02:40:07 +01:00
return true;
}
}
session.setBlockChangeLimit(limit);
player.print("Block change limit set to " + limit + ".");
return true;
// Single super pickaxe mode
} else if (split[0].equalsIgnoreCase("/single")) {
2011-01-04 23:42:44 +01:00
if (!canUseCommand(player, "/")) {
player.printError("You don't have permission for super pickaxe usage.");
return true;
2011-01-01 02:40:07 +01:00
}
checkArgs(split, 0, 0, split[0]);
session.setSuperPickaxeMode(new SinglePickaxe());
session.enableSuperPickAxe();
player.print("Mode changed. Left click with a pickaxe. // to disable.");
2011-01-01 02:40:07 +01:00
return true;
// Area/recursive super pickaxe mode
} else if (split[0].equalsIgnoreCase("/area")
|| split[0].equalsIgnoreCase("/recur")) {
2011-01-04 23:42:44 +01:00
if (!canUseCommand(player, "/")) {
player.printError("You don't have permission for super pickaxe usage.");
return true;
}
2011-01-01 02:40:07 +01:00
checkArgs(split, 1, 1, split[0]);
boolean recur = split[0].equalsIgnoreCase("/recur");
int range = Integer.parseInt(split[1]);
if (range > config.maxSuperPickaxeSize) {
player.printError("Maximum range: " + config.maxSuperPickaxeSize);
return true;
2011-01-01 02:40:07 +01:00
}
session.setSuperPickaxeMode(
recur ? new RecursivePickaxe(range) : new AreaPickaxe(range));
session.enableSuperPickAxe();
player.print("Mode changed. Left click with a pickaxe. // to disable.");
return true;
// Tree tool
} else if (split[0].equalsIgnoreCase("/tree")) {
checkArgs(split, 0, 0, split[0]);
session.setTool(new TreePlanter());
player.print("Tree tool equipped. Right click with a pickaxe.");
return true;
// Info tool
} else if (split[0].equalsIgnoreCase("/info")) {
checkArgs(split, 0, 0, split[0]);
session.setTool(new QueryTool());
player.print("Info tool equipped. Right click with a pickaxe.");
return true;
// Replace block tool
} else if (split[0].equalsIgnoreCase("/repl")) {
checkArgs(split, 1, 1, split[0]);
BaseBlock targetBlock = getBlock(player, split[1]);
session.setTool(new BlockReplacer(targetBlock));
player.print("Block replacer tool equipped. Right click with a pickaxe.");
return true;
// No tool
} else if (split[0].equalsIgnoreCase("/none")) {
checkArgs(split, 0, 0, split[0]);
session.setTool(null);
player.print("Now no longer equipping a tool.");
2011-01-01 02:40:07 +01:00
return true;
// Undo
} else if (split[0].equalsIgnoreCase("//undo")) {
checkArgs(split, 0, 0, split[0]);
EditSession undone = session.undo(session.getBlockBag(player));
if (undone != null) {
player.print("Undo successful.");
flushBlockBag(player, undone);
} else {
player.printError("Nothing to undo.");
}
return true;
// Redo
} else if (split[0].equalsIgnoreCase("//redo")) {
checkArgs(split, 0, 0, split[0]);
EditSession redone = session.redo(session.getBlockBag(player));
if (redone != null) {
player.print("Redo successful.");
flushBlockBag(player, redone);
} else {
player.printError("Nothing to redo.");
}
return true;
// Clear undo history
} else if (split[0].equalsIgnoreCase("/clearhistory")) {
checkArgs(split, 0, 0, split[0]);
session.clearHistory();
player.print("History cleared.");
return true;
// Clear clipboard
} else if (split[0].equalsIgnoreCase("/clearclipboard")) {
checkArgs(split, 0, 0, split[0]);
session.setClipboard(null);
player.print("Clipboard cleared.");
return true;
// Paste
} else if (split[0].equalsIgnoreCase("//pasteair") ||
split[0].equalsIgnoreCase("//paste")) {
checkArgs(split, 0, 1, split[0]);
boolean atOrigin = split.length > 1
? (split[1].equalsIgnoreCase("true")
|| split[1].equalsIgnoreCase("yes"))
: false;
if (atOrigin) {
Vector pos = session.getClipboard().getOrigin();
session.getClipboard().place(editSession, pos,
split[0].equalsIgnoreCase("//paste"));
player.findFreePosition();
player.print("Pasted to copy origin. Undo with //undo");
} else {
Vector pos = session.getPlacementPosition(player);
session.getClipboard().paste(editSession, pos,
split[0].equalsIgnoreCase("//paste"));
player.findFreePosition();
player.print("Pasted relative to you. Undo with //undo");
}
return true;
// Draw a hollow cylinder
} else if (split[0].equalsIgnoreCase("//hcyl")
|| split[0].equalsIgnoreCase("//cyl")) {
checkArgs(split, 2, 3, split[0]);
BaseBlock block = getBlock(player, split[1]);
2011-01-01 02:40:07 +01:00
int radius = Math.max(1, Integer.parseInt(split[2]));
int height = split.length > 3 ? Integer.parseInt(split[3]) : 1;
boolean filled = split[0].equalsIgnoreCase("//cyl");
Vector pos = session.getPlacementPosition(player);
int affected;
if (filled) {
affected = editSession.makeCylinder(pos, block, radius, height);
} else {
affected = editSession.makeHollowCylinder(pos, block, radius, height);
}
player.print(affected + " block(s) have been created.");
return true;
// Draw a sphere
} else if (split[0].equalsIgnoreCase("//sphere")
|| split[0].equalsIgnoreCase("//hsphere")) {
checkArgs(split, 2, 3, split[0]);
BaseBlock block = getBlock(player, split[1]);
2011-01-01 02:40:07 +01:00
int radius = Math.max(1, Integer.parseInt(split[2]));
boolean raised = split.length > 3
? (split[3].equalsIgnoreCase("true")
|| split[3].equalsIgnoreCase("yes"))
: false;
boolean filled = split[0].equalsIgnoreCase("//sphere");
Vector pos = session.getPlacementPosition(player);
if (raised) {
pos = pos.add(0, radius, 0);
}
int affected = editSession.makeSphere(pos, block, radius, filled);
player.findFreePosition();
player.print(affected + " block(s) have been created.");
return true;
// Fill a hole
} else if (split[0].equalsIgnoreCase("//fill")
|| split[0].equalsIgnoreCase("//fillr")) {
boolean recursive = split[0].equalsIgnoreCase("//fillr");
checkArgs(split, 2, recursive ? 2 : 3, split[0]);
Pattern pattern = getBlockPattern(player, split[1]);
2011-01-01 02:40:07 +01:00
int radius = Math.max(1, Integer.parseInt(split[2]));
checkMaxRadius(radius);
int depth = split.length > 3 ? Math.max(1, Integer.parseInt(split[3])) : 1;
Vector pos = session.getPlacementPosition(player);
int affected = 0;
if (pattern instanceof SingleBlockPattern) {
affected = editSession.fillXZ(pos,
((SingleBlockPattern)pattern).getBlock(),
radius, depth, recursive);
} else {
affected = editSession.fillXZ(pos, pattern, radius, depth, recursive);
}
player.print(affected + " block(s) have been created.");
return true;
// Remove blocks above current position
} else if (split[0].equalsIgnoreCase("/removeabove")) {
checkArgs(split, 0, 2, split[0]);
int size = split.length > 1 ? Math.max(1, Integer.parseInt(split[1])) : 1;
checkMaxRadius(size);
int height = split.length > 2 ? Math.min(128, Integer.parseInt(split[2]) + 2) : 128;
int affected = editSession.removeAbove(
session.getPlacementPosition(player), size, height);
player.print(affected + " block(s) have been removed.");
return true;
// Remove blocks below current position
} else if (split[0].equalsIgnoreCase("/removebelow")) {
checkArgs(split, 0, 2, split[0]);
int size = split.length > 1 ? Math.max(1, Integer.parseInt(split[1])) : 1;
checkMaxRadius(size);
int height = split.length > 2 ? Math.max(1, Integer.parseInt(split[2])) : 128;
int affected = editSession.removeBelow(
session.getPlacementPosition(player), size, height);
player.print(affected + " block(s) have been removed.");
return true;
// Remove blocks near
} else if (split[0].equalsIgnoreCase("/removenear")) {
checkArgs(split, 2, 2, split[0]);
BaseBlock block = getBlock(player, split[1], true);
2011-01-01 02:40:07 +01:00
int size = Math.max(1, Integer.parseInt(split[2]));
checkMaxRadius(size);
int affected = editSession.removeNear(
session.getPlacementPosition(player), block.getID(), size);
player.print(affected + " block(s) have been removed.");
return true;
// Extinguish
} else if (split[0].equalsIgnoreCase("/ex")) {
checkArgs(split, 0, 1, split[0]);
int defaultRadius = config.maxRadius != -1 ? Math.min(40, config.maxRadius) : 40;
2011-01-01 02:40:07 +01:00
int size = split.length > 1 ? Math.max(1, Integer.parseInt(split[1]))
: defaultRadius;
checkMaxRadius(size);
int affected = editSession.removeNear(
session.getPlacementPosition(player), 51, size);
player.print(affected + " block(s) have been removed.");
return true;
// Load .schematic to clipboard
} else if (split[0].equalsIgnoreCase("//load")) {
checkArgs(split, 1, 1, split[0]);
String filename = split[1].replace("\0", "") + ".schematic";
File dir = new File("schematics");
File f = new File("schematics", filename);
if (!filename.matches("^[A-Za-z0-9_\\- \\./\\\\'\\$@~!%\\^\\*\\(\\)\\[\\]\\+\\{\\},\\?]+$")) {
player.printError("Valid characters: A-Z, a-z, 0-9, spaces, "
+ "./\'$@~!%^*()[]+{},?");
return true;
}
try {
String filePath = f.getCanonicalPath();
String dirPath = dir.getCanonicalPath();
if (!filePath.substring(0, dirPath.length()).equals(dirPath)) {
player.printError("Schematic could not read or it does not exist.");
} else {
session.setClipboard(CuboidClipboard.loadSchematic(filePath));
logger.log(Level.INFO, player.getName() + " loaded " + filePath);
player.print(filename + " loaded. Paste it with //paste");
}
} catch (DataException e) {
player.printError("Load error: " + e.getMessage());
} catch (IOException e) {
player.printError("Schematic could not read or it does not exist: " + e.getMessage());
}
return true;
// Save clipboard to .schematic
} else if (split[0].equalsIgnoreCase("//save")) {
checkArgs(split, 1, 1, split[0]);
String filename = split[1].replace("\0", "") + ".schematic";
if (!filename.matches("^[A-Za-z0-9_\\- \\./\\\\'\\$@~!%\\^\\*\\(\\)\\[\\]\\+\\{\\},\\?]+$")) {
player.printError("Valid characters: A-Z, a-z, 0-9, spaces, "
+ "./\'$@~!%^*()[]+{},?");
return true;
}
File dir = new File("schematics");
File f = new File("schematics", filename);
if (!dir.exists()) {
if (!dir.mkdir()) {
player.printError("A schematics/ folder could not be created.");
return true;
}
}
try {
String filePath = f.getCanonicalPath();
String dirPath = dir.getCanonicalPath();
if (!filePath.substring(0, dirPath.length()).equals(dirPath)) {
player.printError("Invalid path for Schematic.");
} else {
// Create parent directories
File parent = f.getParentFile();
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
session.getClipboard().saveSchematic(filePath);
logger.log(Level.INFO, player.getName() + " saved " + filePath);
player.print(filename + " saved.");
}
} catch (DataException se) {
player.printError("Save error: " + se.getMessage());
} catch (IOException e) {
player.printError("Schematic could not written: " + e.getMessage());
}
return true;
// Get size
} else if (split[0].equalsIgnoreCase("//size")) {
Region region = session.getRegion();
Vector size = region.getMaximumPoint()
.subtract(region.getMinimumPoint())
.add(1, 1, 1);
player.print("First position: " + session.getPos1());
player.print("Second position: " + session.getPos2());
player.print("Size: " + size);
player.print("# of blocks: " + region.getSize());
return true;
// Get count
} else if (split[0].equalsIgnoreCase("//count")) {
checkArgs(split, 1, 1, split[0]);
Set<Integer> searchIDs = getBlockIDs(player, split[1], true);
2011-01-01 02:40:07 +01:00
player.print("Counted: " +
editSession.countBlocks(session.getRegion(), searchIDs));
return true;
// Get block distribution
} else if (split[0].equalsIgnoreCase("//distr")) {
checkArgs(split, 0, 0, split[0]);
List<Countable<Integer>> distribution =
editSession.getBlockDistribution(session.getRegion());
if (distribution.size() > 0) { // *Should* always be true
int size = session.getRegion().getSize();
player.print("# total blocks: " + size);
for (Countable<Integer> c : distribution) {
player.print(String.format("%-7s (%.3f%%) %s #%d",
String.valueOf(c.getAmount()),
c.getAmount() / (double)size * 100,
BlockType.fromID(c.getID()).getName(), c.getID()));
}
} else {
player.printError("No blocks counted.");
}
return true;
// Replace all blocks in the region
} else if(split[0].equalsIgnoreCase("//set")) {
checkArgs(split, 1, 1, split[0]);
Pattern pattern = getBlockPattern(player, split[1]);
2011-01-01 02:40:07 +01:00
int affected;
if (pattern instanceof SingleBlockPattern) {
affected = editSession.setBlocks(session.getRegion(),
((SingleBlockPattern)pattern).getBlock());
} else {
affected = editSession.setBlocks(session.getRegion(), pattern);
}
player.print(affected + " block(s) have been changed.");
return true;
// Smooth the heightmap of a region
} else if (split[0].equalsIgnoreCase("//smooth")) {
checkArgs(split, 0, 1, split[0]);
int iterations = 1;
if (split.length >= 2)
iterations = Integer.parseInt(split[1]);
HeightMap heightMap = new HeightMap(editSession, session.getRegion());
HeightMapFilter filter = new HeightMapFilter(new GaussianKernel(5, 1.0));
int affected = heightMap.applyFilter(filter, iterations);
player.print("Terrain's height map smoothed. " + affected + " block(s) changed.");
return true;
// Set the outline of a region
} else if(split[0].equalsIgnoreCase("//outline")) {
checkArgs(split, 1, 1, split[0]);
BaseBlock block = getBlock(player, split[1]);
2011-01-01 02:40:07 +01:00
int affected = editSession.makeCuboidFaces(session.getRegion(), block);
player.print(affected + " block(s) have been changed.");
return true;
// Set the walls of a region
} else if(split[0].equalsIgnoreCase("//walls")) {
checkArgs(split, 1, 1, split[0]);
BaseBlock block = getBlock(player, split[1]);
2011-01-01 02:40:07 +01:00
int affected = editSession.makeCuboidWalls(session.getRegion(), block);
player.print(affected + " block(s) have been changed.");
return true;
// Drain pools
} else if(split[0].equalsIgnoreCase("//drain")) {
checkArgs(split, 1, 1, split[0]);
int radius = Math.max(0, Integer.parseInt(split[1]));
checkMaxRadius(radius);
int affected = editSession.drainArea(
session.getPlacementPosition(player), radius);
player.print(affected + " block(s) have been changed.");
return true;
// Fix water
} else if(split[0].equalsIgnoreCase("/fixwater")) {
checkArgs(split, 1, 1, split[0]);
int radius = Math.max(0, Integer.parseInt(split[1]));
checkMaxRadius(radius);
int affected = editSession.fixLiquid(
session.getPlacementPosition(player), radius, 8, 9);
player.print(affected + " block(s) have been changed.");
return true;
// Fix lava
} else if(split[0].equalsIgnoreCase("/fixlava")) {
checkArgs(split, 1, 1, split[0]);
int radius = Math.max(0, Integer.parseInt(split[1]));
checkMaxRadius(radius);
int affected = editSession.fixLiquid(
session.getPlacementPosition(player), radius, 10, 11);
player.print(affected + " block(s) have been changed.");
return true;
// Replace all blocks in the region
} else if(split[0].equalsIgnoreCase("//replace")) {
checkArgs(split, 1, 2, split[0]);
Set<Integer> from;
Pattern to;
if (split.length == 2) {
from = null;
to = getBlockPattern(player, split[1]);
2011-01-01 02:40:07 +01:00
} else {
from = getBlockIDs(player, split[1], true);
to = getBlockPattern(player, split[2]);
2011-01-01 02:40:07 +01:00
}
int affected = 0;
if (to instanceof SingleBlockPattern) {
affected = editSession.replaceBlocks(session.getRegion(), from,
((SingleBlockPattern)to).getBlock());
} else {
affected = editSession.replaceBlocks(session.getRegion(), from, to);
}
player.print(affected + " block(s) have been replaced.");
return true;
// Replace all blocks in the region
} else if(split[0].equalsIgnoreCase("/replacenear")) {
checkArgs(split, 2, 3, split[0]);
int size = Math.max(1, Integer.parseInt(split[1]));
Set<Integer> from;
BaseBlock to;
if (split.length == 3) {
from = null;
to = getBlock(player, split[2]);
2011-01-01 02:40:07 +01:00
} else {
from = getBlockIDs(player, split[2], true);
to = getBlock(player, split[3]);
2011-01-01 02:40:07 +01:00
}
Vector min = player.getBlockIn().subtract(size, size, size);
Vector max = player.getBlockIn().add(size, size, size);
Region region = new CuboidRegion(min, max);
int affected = editSession.replaceBlocks(region, from, to);
player.print(affected + " block(s) have been replaced.");
return true;
// Lay blocks over an area
} else if (split[0].equalsIgnoreCase("//overlay")) {
checkArgs(split, 1, 1, split[0]);
BaseBlock block = getBlock(player, split[1]);
2011-01-01 02:40:07 +01:00
Region region = session.getRegion();
int affected = editSession.overlayCuboidBlocks(region, block);
player.print(affected + " block(s) have been overlayed.");
return true;
// Copy
} else if (split[0].equalsIgnoreCase("//copy")
|| split[0].equalsIgnoreCase("//cut")) {
boolean cut = split[0].equalsIgnoreCase("//cut");
BaseBlock block = new BaseBlock(0);
if (cut) {
checkArgs(split, 0, 1, split[0]);
if (split.length > 1) {
getBlock(player, split[1]);
2011-01-01 02:40:07 +01:00
}
} else {
checkArgs(split, 0, 0, split[0]);
}
Region region = session.getRegion();
Vector min = region.getMinimumPoint();
Vector max = region.getMaximumPoint();
Vector pos = player.getBlockIn();
CuboidClipboard clipboard = new CuboidClipboard(
max.subtract(min).add(new Vector(1, 1, 1)),
min, min.subtract(pos));
clipboard.copy(editSession);
session.setClipboard(clipboard);
if (cut) {
editSession.setBlocks(session.getRegion(), block);
player.print("Block(s) cut.");
} else {
player.print("Block(s) copied.");
}
return true;
// Make tree forest
} else if (split[0].equalsIgnoreCase("/forestgen")) {
checkArgs(split, 0, 2, split[0]);
int size = split.length > 1 ? Math.max(1, Integer.parseInt(split[1])) : 10;
double density = split.length > 2 ? Double.parseDouble(split[2]) / 100 : 0.05;
int affected = editSession.makeForest(player.getPosition(),
size, density, false);
player.print(affected + " trees created.");
return true;
// Make pine tree forest
} else if (split[0].equalsIgnoreCase("/pinegen")) {
checkArgs(split, 0, 1, split[0]);
int size = split.length > 1 ? Math.max(1, Integer.parseInt(split[1])) : 10;
double density = split.length > 2 ? Double.parseDouble(split[2]) / 100 : 0.05;
int affected = editSession.makeForest(player.getPosition(),
size, density, true);
player.print(affected + " pine trees created.");
return true;
// Let it snow~
} else if (split[0].equalsIgnoreCase("/snow")) {
checkArgs(split, 0, 1, split[0]);
int size = split.length > 1 ? Math.max(1, Integer.parseInt(split[1])) : 10;
int affected = editSession.simulateSnow(player.getBlockIn(), size);
player.print(affected + " surfaces covered. Let it snow~");
return true;
// Make pumpkin patches
} else if (split[0].equalsIgnoreCase("/pumpkins")) {
checkArgs(split, 0, 1, split[0]);
int size = split.length > 1 ? Math.max(1, Integer.parseInt(split[1])) : 10;
int affected = editSession.makePumpkinPatches(player.getPosition(), size);
player.print(affected + " pumpkin patches created.");
return true;
// Move
} else if (split[0].equalsIgnoreCase("//moveair") ||
split[0].equalsIgnoreCase("//move")) {
checkArgs(split, 0, 3, split[0]);
int count = split.length > 1 ? Math.max(1, Integer.parseInt(split[1])) : 1;
Vector dir = getDirection(player,
split.length > 2 ? split[2].toLowerCase() : "me");
BaseBlock replace;
// Replacement block argument
if (split.length > 3) {
replace = getBlock(player, split[3]);
2011-01-01 02:40:07 +01:00
} else {
replace = new BaseBlock(0);
}
boolean copyAir = split[0].equalsIgnoreCase("//moveair");
int affected = editSession.moveCuboidRegion(session.getRegion(),
dir, count, copyAir, replace);
player.print(affected + " blocks moved.");
return true;
// Stack
} else if (split[0].equalsIgnoreCase("//stackair") ||
split[0].equalsIgnoreCase("//stack")) {
checkArgs(split, 0, 2, split[0]);
int count = split.length > 1 ? Math.max(1, Integer.parseInt(split[1])) : 1;
Vector dir = getDirection(player,
split.length > 2 ? split[2].toLowerCase() : "me");
boolean copyAir = split[0].equalsIgnoreCase("//stackair");
int affected = editSession.stackCuboidRegion(session.getRegion(),
dir, count, copyAir);
player.print(affected + " blocks changed. Undo with //undo");
return true;
// Expand
} else if (split[0].equalsIgnoreCase("//expand")) {
checkArgs(split, 1, 2, split[0]);
Vector dir;
int change = Integer.parseInt(split[1]);
if (split.length == 3) {
dir = getDirection(player, split[2].toLowerCase());
} else {
dir = getDirection(player, "me");
}
Region region = session.getRegion();
int oldSize = region.getSize();
region.expand(dir.multiply(change));
session.learnRegionChanges();
int newSize = region.getSize();
player.print("Region expanded " + (newSize - oldSize) + " blocks.");
return true;
// Contract
} else if (split[0].equalsIgnoreCase("//contract")) {
checkArgs(split, 1, 2, split[0]);
Vector dir;
int change = Integer.parseInt(split[1]);
if (split.length == 3) {
dir = getDirection(player, split[2].toLowerCase());
} else {
dir = getDirection(player, "me");
}
Region region = session.getRegion();
int oldSize = region.getSize();
region.contract(dir.multiply(change));
session.learnRegionChanges();
int newSize = region.getSize();
player.print("Region contracted " + (oldSize - newSize) + " blocks.");
return true;
// Shift
} else if (split[0].equalsIgnoreCase("//shift")) {
checkArgs(split, 1, 2, split[0]);
Vector dir;
int change = Integer.parseInt(split[1]);
if (split.length == 3) {
dir = getDirection(player, split[2].toLowerCase());
} else {
dir = getDirection(player, "me");
}
Region region = session.getRegion();
region.expand(dir.multiply(change));
region.contract(dir.multiply(change));
session.learnRegionChanges();
player.print("Region shifted.");
return true;
// Rotate
} else if (split[0].equalsIgnoreCase("//rotate")) {
checkArgs(split, 1, 1, split[0]);
int angle = Integer.parseInt(split[1]);
if (angle % 90 == 0) {
CuboidClipboard clipboard = session.getClipboard();
clipboard.rotate2D(angle);
player.print("Clipboard rotated by " + angle + " degrees.");
} else {
player.printError("Angles must be divisible by 90 degrees.");
}
return true;
// Flip
} else if (split[0].equalsIgnoreCase("//flip")) {
checkArgs(split, 0, 1, split[0]);
CuboidClipboard.FlipDirection dir = getFlipDirection(player,
split.length > 1 ? split[1].toLowerCase() : "me");
CuboidClipboard clipboard = session.getClipboard();
clipboard.flip(dir);
player.print("Clipboard flipped.");
return true;
// Kill mobs
} else if (split[0].equalsIgnoreCase("/butcher")) {
checkArgs(split, 0, 1, split[0]);
int radius = split.length > 1 ?
Math.max(1, Integer.parseInt(split[1])) : -1;
Vector origin = session.getPlacementPosition(player);
int killed = world.killMobs(origin, radius);
2011-01-01 02:40:07 +01:00
player.print("Killed " + killed + " mobs.");
return true;
// Get chunk filename
} else if (split[0].equalsIgnoreCase("/chunkinfo")) {
checkArgs(split, 0, 0, split[0]);
Vector pos = player.getBlockIn();
int chunkX = (int)Math.floor(pos.getBlockX() / 16.0);
int chunkZ = (int)Math.floor(pos.getBlockZ() / 16.0);
String folder1 = Integer.toString(divisorMod(chunkX, 64), 36);
String folder2 = Integer.toString(divisorMod(chunkZ, 64), 36);
String filename = "c." + Integer.toString(chunkX, 36)
+ "." + Integer.toString(chunkZ, 36) + ".dat";
player.print("Chunk: " + chunkX + ", " + chunkZ);
player.print(folder1 + "/" + folder2 + "/" + filename);
return true;
// Dump a list of involved chunks
} else if (split[0].equalsIgnoreCase("/listchunks")) {
checkArgs(split, 0, 0, split[0]);
Set<Vector2D> chunks = session.getRegion().getChunks();
for (Vector2D chunk : chunks) {
player.print(NestedFileChunkStore.getFilename(chunk));
}
return true;
// Dump a list of involved chunks
} else if (split[0].equalsIgnoreCase("/delchunks")) {
checkArgs(split, 0, 0, split[0]);
Set<Vector2D> chunks = session.getRegion().getChunks();
FileOutputStream out = null;
if (config.shellSaveType == null) {
2011-01-01 02:40:07 +01:00
player.printError("shell-save-type has to be configured in worldedit.properties");
} else if (config.shellSaveType.equalsIgnoreCase("bat")) {
2011-01-01 02:40:07 +01:00
try {
out = new FileOutputStream("worldedit-delchunks.bat");
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
writer.write("@ECHO off\r\n");
writer.write("ECHO This batch file was generated by WorldEdit.\r\n");
writer.write("ECHO It contains a list of chunks that were in the selected region\r\n");
writer.write("ECHO at the time that the /delchunks command was used. Run this file\r\n");
writer.write("ECHO in order to delete the chunk files listed in this file.\r\n");
writer.write("ECHO.\r\n");
writer.write("PAUSE\r\n");
for (Vector2D chunk : chunks) {
String filename = NestedFileChunkStore.getFilename(chunk);
writer.write("ECHO " + filename + "\r\n");
writer.write("DEL \"world/" + filename + "\"\r\n");
}
writer.write("ECHO Complete.\r\n");
writer.write("PAUSE\r\n");
writer.close();
player.print("worldedit-delchunks.bat written. Run it when no one is near the region.");
} catch (IOException e) {
player.printError("Error occurred: " + e.getMessage());
} finally {
if (out != null) {
try { out.close(); } catch (IOException ie) {}
}
}
} else if (config.shellSaveType.equalsIgnoreCase("bash")) {
2011-01-01 02:40:07 +01:00
try {
out = new FileOutputStream("worldedit-delchunks.sh");
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
writer.write("#!/bin/bash\n");
writer.write("echo This shell file was generated by WorldEdit.\n");
writer.write("echo It contains a list of chunks that were in the selected region\n");
writer.write("echo at the time that the /delchunks command was used. Run this file\n");
writer.write("echo in order to delete the chunk files listed in this file.\n");
writer.write("echo\n");
writer.write("read -p \"Press any key to continue...\"\n");
for (Vector2D chunk : chunks) {
String filename = NestedFileChunkStore.getFilename(chunk);
writer.write("echo " + filename + "\n");
writer.write("rm \"world/" + filename + "\"\n");
}
writer.write("echo Complete.\n");
writer.write("read -p \"Press any key to continue...\"\n");
writer.close();
player.print("worldedit-delchunks.sh written. Run it when no one is near the region.");
player.print("You will have to chmod it to be executable.");
} catch (IOException e) {
player.printError("Error occurred: " + e.getMessage());
} finally {
if (out != null) {
try { out.close(); } catch (IOException ie) {}
}
}
} else {
player.printError("Unknown shell script save type. 'bat' or 'bash' expected.");
}
return true;
// List snapshots
} else if (split[0].equalsIgnoreCase("/listsnapshots")) {
checkArgs(split, 0, 1, split[0]);
int num = split.length > 1 ?
Math.min(40, Math.max(5, Integer.parseInt(split[1]))) : 5;
if (config.snapshotRepo != null) {
Snapshot[] snapshots = config.snapshotRepo.getSnapshots();
2011-01-01 02:40:07 +01:00
if (snapshots.length > 0) {
for (byte i = 0; i < Math.min(num, snapshots.length); i++) {
player.print((i + 1) + ". " + snapshots[i].getName());
}
player.print("Use //use [snapshot] or //use latest to set the snapshot.");
} else {
player.printError("No snapshots are available.");
}
} else {
player.printError("Snapshot/backup restore is not configured.");
}
return true;
// Use a certain snapshot
} else if (split[0].equalsIgnoreCase("//use")) {
checkArgs(split, 1, 1, split[0]);
if (config.snapshotRepo == null) {
2011-01-01 02:40:07 +01:00
player.printError("Snapshot/backup restore is not configured.");
return true;
}
String name = split[1];
// Want the latest snapshot?
if (name.equalsIgnoreCase("latest")) {
Snapshot snapshot = config.snapshotRepo.getDefaultSnapshot();
2011-01-01 02:40:07 +01:00
if (snapshot != null) {
session.setSnapshot(null);
player.print("Now using newest snapshot.");
} else {
player.printError("No snapshots were found.");
}
} else {
try {
session.setSnapshot(config.snapshotRepo.getSnapshot(name));
2011-01-01 02:40:07 +01:00
player.print("Snapshot set to: " + name);
} catch (InvalidSnapshotException e) {
player.printError("That snapshot does not exist or is not available.");
}
}
return true;
// Restore
} else if (split[0].equalsIgnoreCase("//restore")) {
checkArgs(split, 0, 1, split[0]);
if (config.snapshotRepo == null) {
2011-01-01 02:40:07 +01:00
player.printError("Snapshot/backup restore is not configured.");
return true;
}
Region region = session.getRegion();
Snapshot snapshot;
if (split.length > 1) {
try {
snapshot = config.snapshotRepo.getSnapshot(split[1]);
2011-01-01 02:40:07 +01:00
} catch (InvalidSnapshotException e) {
player.printError("That snapshot does not exist or is not available.");
return true;
}
} else {
snapshot = session.getSnapshot();
}
ChunkStore chunkStore = null;
// No snapshot set?
if (snapshot == null) {
snapshot = config.snapshotRepo.getDefaultSnapshot();
2011-01-01 02:40:07 +01:00
if (snapshot == null) {
player.printError("No snapshots were found.");
return true;
}
}
// Load chunk store
try {
chunkStore = snapshot.getChunkStore();
player.print("Snapshot '" + snapshot.getName() + "' loaded; now restoring...");
} catch (DataException e) {
player.printError("Failed to load snapshot: " + e.getMessage());
return true;
} catch (IOException e) {
player.printError("Failed to load snapshot: " + e.getMessage());
return true;
}
try {
// Restore snapshot
SnapshotRestore restore = new SnapshotRestore(chunkStore, region);
//player.print(restore.getChunksAffected() + " chunk(s) will be loaded.");
restore.restore(editSession);
if (restore.hadTotalFailure()) {
String error = restore.getLastErrorMessage();
if (error != null) {
player.printError("Errors prevented any blocks from being restored.");
player.printError("Last error: " + error);
} else {
player.printError("No chunks could be loaded. (Bad archive?)");
}
} else {
player.print(String.format("Restored; %d "
+ "missing chunks and %d other errors.",
restore.getMissingChunks().size(),
restore.getErrorChunks().size()));
}
} finally {
try {
chunkStore.close();
} catch (IOException e) {
}
}
return true;
}
return false;
}
/**
* Modulus, divisor-style.
*
* @param a
* @param n
* @return
*/
private static int divisorMod(int a, int n) {
return (int)(a - n * Math.floor(Math.floor(a) / (double)n));
}
/**
* Get the direction vector for a player's direction. May return
* null if a direction could not be found.
*
* @param player
* @param dir
* @return
*/
public Vector getDirection(LocalPlayer player, String dirStr)
2011-01-01 02:40:07 +01:00
throws UnknownDirectionException {
int xm = 0;
int ym = 0;
int zm = 0;
LocalPlayer.DIRECTION dir = null;
2011-01-01 02:40:07 +01:00
if (dirStr.equals("me")) {
dir = player.getCardinalDirection();
}
if (dirStr.charAt(0) == 'w' || dir == LocalPlayer.DIRECTION.WEST) {
2011-01-01 02:40:07 +01:00
zm += 1;
} else if (dirStr.charAt(0) == 'e' || dir == LocalPlayer.DIRECTION.EAST) {
2011-01-01 02:40:07 +01:00
zm -= 1;
} else if (dirStr.charAt(0) == 's' || dir == LocalPlayer.DIRECTION.SOUTH) {
2011-01-01 02:40:07 +01:00
xm += 1;
} else if (dirStr.charAt(0) == 'n' || dir == LocalPlayer.DIRECTION.NORTH) {
2011-01-01 02:40:07 +01:00
xm -= 1;
} else if (dirStr.charAt(0) == 'u') {
ym += 1;
} else if (dirStr.charAt(0) == 'd') {
ym -= 1;
} else {
throw new UnknownDirectionException(dirStr);
}
return new Vector(xm, ym, zm);
}
/**
* Get the flip direction for a player's direction. May return
* null if a direction could not be found.
*
* @param player
* @param dir
* @return
*/
public CuboidClipboard.FlipDirection getFlipDirection(
LocalPlayer player, String dirStr)
2011-01-01 02:40:07 +01:00
throws UnknownDirectionException {
LocalPlayer.DIRECTION dir = null;
2011-01-01 02:40:07 +01:00
if (dirStr.equals("me")) {
dir = player.getCardinalDirection();
}
if (dirStr.charAt(0) == 'w' || dir == LocalPlayer.DIRECTION.EAST) {
2011-01-01 02:40:07 +01:00
return CuboidClipboard.FlipDirection.WEST_EAST;
} else if (dirStr.charAt(0) == 'e' || dir == LocalPlayer.DIRECTION.EAST) {
2011-01-01 02:40:07 +01:00
return CuboidClipboard.FlipDirection.WEST_EAST;
} else if (dirStr.charAt(0) == 's' || dir == LocalPlayer.DIRECTION.SOUTH) {
2011-01-01 02:40:07 +01:00
return CuboidClipboard.FlipDirection.NORTH_SOUTH;
} else if (dirStr.charAt(0) == 'n' || dir == LocalPlayer.DIRECTION.SOUTH) {
2011-01-01 02:40:07 +01:00
return CuboidClipboard.FlipDirection.NORTH_SOUTH;
} else if (dirStr.charAt(0) == 'u') {
return CuboidClipboard.FlipDirection.UP_DOWN;
} else if (dirStr.charAt(0) == 'd') {
return CuboidClipboard.FlipDirection.UP_DOWN;
} else {
throw new UnknownDirectionException(dirStr);
}
}
/**
* Remove a session.
*
* @param player
*/
public void removeSession(LocalPlayer player) {
2011-01-01 02:40:07 +01:00
sessions.remove(player);
}
/**
* Remove all sessions.
*/
public void clearSessions() {
sessions.clear();
}
/**
* Get a comma-delimited list of the default allowed blocks.
*
* @return comma-delimited list
*/
public static String getDefaultAllowedBlocks() {
StringBuilder b = new StringBuilder();
for (Integer id : DEFAULT_ALLOWED_BLOCKS) {
b.append(id).append(",");
}
return b.substring(0, b.length() - 1);
}
/**
*
* @param player
*/
public void handleDisconnect(LocalPlayer player) {
2011-01-01 02:40:07 +01:00
removeSession(player);
}
/**
* Called on arm swing.
*
* @param player
*/
public void handleArmSwing(LocalPlayer player) {
2011-01-04 23:42:44 +01:00
if (!canUseCommand(player, "/"))
2011-01-01 02:40:07 +01:00
return;
}
/**
* Called on right click.
*
* @param player
* @param clicked
* @return false if you want the action to go through
*/
public boolean handleBlockRightClick(LocalPlayer player, WorldVector clicked) {
2011-01-01 02:40:07 +01:00
int itemInHand = player.getItemInHand();
// This prevents needless sessions from being created
if (!hasSession(player) && !(itemInHand == config.wandItem &&
2011-01-04 23:42:44 +01:00
canUseCommand(player, "/pos2"))) { return false; }
2011-01-01 02:40:07 +01:00
LocalSession session = getSession(player);
2011-01-01 02:40:07 +01:00
if (itemInHand == config.wandItem && session.isToolControlEnabled()
2011-01-04 23:42:44 +01:00
&& canUseCommand(player, "/pos2")) {
2011-01-01 02:40:07 +01:00
session.setPos2(clicked);
try {
player.print("Second position set to " + clicked
+ " (" + session.getRegion().getSize() + ").");
} catch (IncompleteRegionException e) {
player.print("Second position set to " + clicked + ".");
}
return true;
} else if (player.isHoldingPickAxe() && session.getTool() != null) {
return session.getTool().act(server, config, player, session, clicked);
2011-01-01 02:40:07 +01:00
}
return false;
}
/**
* Called on left click.
*
* @param player
* @param clicked
* @return false if you want the action to go through
*/
public boolean handleBlockLeftClick(LocalPlayer player, WorldVector clicked) {
2011-01-04 23:42:44 +01:00
if (!canUseCommand(player, "/pos1")
&& !canUseCommand(player, "/")) { return false; }
2011-01-01 02:40:07 +01:00
LocalSession session = getSession(player);
2011-01-01 02:40:07 +01:00
if (player.getItemInHand() == config.wandItem) {
2011-01-01 02:40:07 +01:00
if (session.isToolControlEnabled()) {
// Bug workaround
if (clicked.getBlockX() == 0 && clicked.getBlockY() == 0
&& clicked.getBlockZ() == 0) {
return false;
}
try {
if (session.getPos1().equals(clicked)) {
return false;
}
} catch (IncompleteRegionException e) {
}
session.setPos1(clicked);
try {
player.print("First position set to " + clicked
+ " (" + session.getRegion().getSize() + ").");
} catch (IncompleteRegionException e) {
player.print("First position set to " + clicked + ".");
}
return true;
}
} else if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) {
if (session.getSuperPickaxeMode() != null) {
return session.getSuperPickaxeMode().act(server, config,
player, session, clicked);
2011-01-01 02:40:07 +01:00
}
}
return false;
}
/**
*
* @param player
* @param split
* @return whether the command was processed
*/
public boolean handleCommand(LocalPlayer player, String[] split) {
2011-01-01 02:40:07 +01:00
try {
// Legacy /, command
if (split[0].equals("/,")) {
split[0] = "//";
}
String searchCmd = split[0].toLowerCase();
if (commands.containsKey(searchCmd)
|| (config.noDoubleSlash && commands.containsKey("/" + searchCmd))
2011-01-01 02:40:07 +01:00
|| ((searchCmd.length() < 3 || searchCmd.charAt(2) != '/')
&& commands.containsKey(searchCmd.substring(1)))) {
if (config.noDoubleSlash && commands.containsKey("/" + searchCmd)) {
2011-01-01 02:40:07 +01:00
split[0] = "/" + split[0];
} else if (commands.containsKey(searchCmd.substring(1))) {
split[0] = split[0].substring(1);
}
if (canUseCommand(player, split[0].substring(1))) {
LocalSession session = getSession(player);
2011-01-01 02:40:07 +01:00
BlockBag blockBag = session.getBlockBag(player);
EditSession editSession =
new EditSession(server, player.getWorld(),
session.getBlockChangeLimit(), blockBag);
2011-01-01 02:40:07 +01:00
editSession.enableQueue();
long start = System.currentTimeMillis();
try {
return performCommand(player, session, editSession, split);
} finally {
session.remember(editSession);
editSession.flushQueue();
if (config.profile) {
2011-01-01 02:40:07 +01:00
long time = System.currentTimeMillis() - start;
player.print((time / 1000.0) + "s elapsed");
2011-01-01 02:40:07 +01:00
}
flushBlockBag(player, editSession);
}
}
}
return false;
} catch (NumberFormatException e) {
player.printError("Number expected; string given.");
} catch (IncompleteRegionException e2) {
player.printError("The edit region has not been fully defined.");
} catch (UnknownItemException e3) {
player.printError("Block name '" + e3.getID() + "' was not recognized.");
} catch (InvalidItemException e4) {
player.printError(e4.getMessage());
} catch (DisallowedItemException e4) {
player.printError("Block '" + e4.getID() + "' not allowed (see WorldEdit configuration).");
} catch (MaxChangedBlocksException e5) {
player.printError("Max blocks changed in an operation reached ("
+ e5.getBlockLimit() + ").");
} catch (MaxRadiusException e) {
player.printError("Maximum radius: " + config.maxRadius);
2011-01-01 02:40:07 +01:00
} catch (UnknownDirectionException ue) {
player.printError("Unknown direction: " + ue.getDirection());
} catch (InsufficientArgumentsException e6) {
player.printError(e6.getMessage());
} catch (EmptyClipboardException ec) {
player.printError("Your clipboard is empty.");
} catch (WorldEditException e7) {
player.printError(e7.getMessage());
} catch (Throwable excp) {
player.printError("Please report this error: [See console]");
player.printRaw(excp.getClass().getName() + ": " + excp.getMessage());
excp.printStackTrace();
}
return true;
}
/**
* Flush a block bag's changes to a player.
*
* @param player
* @param blockBag
* @param editSession
*/
private static void flushBlockBag(LocalPlayer player,
2011-01-01 02:40:07 +01:00
EditSession editSession) {
BlockBag blockBag = editSession.getBlockBag();
if (blockBag != null) {
blockBag.flushChanges();
}
Set<Integer> missingBlocks = editSession.popMissingBlocks();
if (missingBlocks.size() > 0) {
StringBuilder str = new StringBuilder();
str.append("Missing these blocks: ");
int size = missingBlocks.size();
int i = 0;
for (Integer id : missingBlocks) {
BlockType type = BlockType.fromID(id);
str.append(type != null
? type.getName() + " (" + id + ")"
: id.toString());
i++;
if (i != size) {
str.append(", ");
}
}
player.printError(str.toString());
}
}
/**
* Checks to see if the player can use a command or /worldedit.
*
* @param player
* @param command
* @return
*/
private boolean canUseCommand(LocalPlayer player, String command) {
2011-01-01 02:40:07 +01:00
// Allow the /worldeditselect permission
2011-01-04 23:42:44 +01:00
if (command.equalsIgnoreCase("/pos1")
|| command.equalsIgnoreCase("/pos2")
|| command.equalsIgnoreCase("/hpos1")
|| command.equalsIgnoreCase("/hpos2")) {
2011-01-01 02:40:07 +01:00
return player.hasPermission(command)
|| player.hasPermission("worldeditselect")
|| player.hasPermission("worldedit");
2011-01-01 02:40:07 +01:00
}
return player.hasPermission(command.replace("air", ""))
|| player.hasPermission("worldedit");
2011-01-01 02:40:07 +01:00
}
/**
* Joins a string from an array of strings.
*
* @param str
* @param delimiter
* @return
*/
private static String joinString(String[] str, String delimiter) {
if (str.length == 0) {
return "";
}
StringBuilder buffer = new StringBuilder(str[0]);
for (int i = 1; i < str.length; i++) {
buffer.append(delimiter).append(str[i]);
}
return buffer.toString();
}
/**
* @return the commands
*/
public HashMap<String, String> getCommands() {
return commands;
}
/**
* Gets the WorldEditLibrary session for a player. Used for the bridge.
*
* @param player
* @return
*/
public LocalSession getBridgeSession(LocalPlayer player) {
2011-01-01 02:40:07 +01:00
if (sessions.containsKey(player)) {
return sessions.get(player);
} else {
LocalSession session = new LocalSession();
session.setBlockChangeLimit(config.defaultChangeLimit);
2011-01-01 02:40:07 +01:00
sessions.put(player, session);
return session;
}
}
}