Plex-FAWE/src/main/java/com/sk89q/worldedit/WorldEdit.java

1466 lines
49 KiB
Java
Raw Normal View History

2011-01-01 01:40:07 +00: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;
2011-02-22 06:28:23 +00:00
import java.util.*;
2011-01-01 01:40:07 +00:00
import java.util.logging.Logger;
import java.util.regex.Matcher;
2011-01-01 01:40:07 +00:00
import java.io.*;
2011-08-08 12:40:02 +00:00
import java.lang.reflect.Method;
2011-01-22 10:41:08 +00:00
import javax.script.ScriptException;
2011-08-08 12:40:02 +00:00
2011-08-11 20:44:54 +00:00
import com.sk89q.minecraft.util.commands.*;
2011-01-19 10:03:41 +00:00
import com.sk89q.util.StringUtil;
import com.sk89q.worldedit.CuboidClipboard.FlipDirection;
2011-01-01 01:40:07 +00:00
import com.sk89q.worldedit.bags.BlockBag;
import com.sk89q.worldedit.blocks.*;
2011-01-29 10:05:22 +00:00
import com.sk89q.worldedit.commands.*;
2011-02-20 01:44:39 +00:00
import com.sk89q.worldedit.regions.RegionSelector;
2011-01-22 21:34:05 +00:00
import com.sk89q.worldedit.scripting.*;
2011-02-22 06:28:23 +00:00
import com.sk89q.worldedit.tools.*;
import com.sk89q.worldedit.masks.*;
2011-01-01 01:40:07 +00:00
import com.sk89q.worldedit.patterns.*;
/**
2011-01-19 10:03:41 +00:00
* This class is the main entry point for WorldEdit. All events are routed
* to an instance of this controller for processing by WorldEdit. For
* integrating WorldEdit in other platforms, an instance of this class
* should be created and events should be redirected to it.
2011-01-01 01:40:07 +00:00
*
* @author sk89q
*/
public class WorldEdit {
2011-02-22 06:28:23 +00:00
/**
* Logger for debugging.
*/
2011-01-29 10:05:22 +00:00
public static final Logger logger = Logger.getLogger("Minecraft.WorldEdit");
2011-11-23 01:29:48 +00:00
2011-02-22 06:28:23 +00:00
/**
* Holds WorldEdit's version.
*/
2011-01-31 08:58:29 +00:00
private static String version;
2011-11-23 01:29:48 +00:00
2011-01-29 10:05:22 +00:00
/**
* Interface to the server.
*/
2011-01-01 01:40:07 +00:00
private ServerInterface server;
2011-11-23 01:29:48 +00:00
2011-01-29 10:05:22 +00:00
/**
* Configuration. This is a subclass.
*/
private LocalConfiguration config;
2011-11-23 01:29:48 +00:00
2011-01-29 10:05:22 +00:00
/**
* List of commands.
*/
private CommandsManager<LocalPlayer> commands;
2011-11-23 01:29:48 +00:00
2011-01-01 01:40:07 +00: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.
*/
2011-11-23 01:29:48 +00:00
private HashMap<String, LocalSession> sessions = new HashMap<String, LocalSession>();
2011-01-31 08:58:29 +00:00
/**
* Initialize statically.
*/
static {
getVersion();
}
2011-11-23 01:29:48 +00:00
2011-01-01 01:40:07 +00:00
/**
* Construct an instance of the plugin
*
* @param server
* @param config
2011-01-01 01:40:07 +00:00
*/
2011-08-08 12:40:02 +00:00
public WorldEdit(ServerInterface server, final LocalConfiguration config) {
this.server = server;
this.config = config;
2011-11-23 01:29:48 +00:00
commands = new CommandsManager<LocalPlayer>() {
@Override
public boolean hasPermission(LocalPlayer player, String perm) {
return player.hasPermission(perm);
}
2011-11-23 01:29:48 +00:00
2011-08-08 12:40:02 +00:00
@Override
public void invokeMethod(Method parent, String[] args,
LocalPlayer player, Method method, Object instance,
Object[] methodArgs, int level) throws CommandException {
if (config.logCommands) {
final Logging loggingAnnotation = method.getAnnotation(Logging.class);
final Logging.LogMode logMode;
2011-09-24 19:24:10 +00:00
if (loggingAnnotation == null) {
2011-08-08 12:40:02 +00:00
logMode = null;
2011-09-24 19:24:10 +00:00
} else {
2011-08-08 12:40:02 +00:00
logMode = loggingAnnotation.value();
2011-09-24 19:24:10 +00:00
}
2011-08-08 12:40:02 +00:00
2011-09-24 19:24:10 +00:00
String msg = "WorldEdit: " + player.getName() + " (in \"" + player.getWorld().getName()
+ "\")" + ": " + StringUtil.joinString(args, " ");
2011-08-08 12:40:02 +00:00
if (logMode != null) {
Vector position = player.getPosition();
final LocalSession session = getSession(player);
switch (logMode) {
case PLACEMENT:
try {
position = session.getPlacementPosition(player);
} catch (IncompleteRegionException e) {
break;
}
/* FALL-THROUGH */
2011-08-08 12:40:02 +00:00
case POSITION:
2011-11-23 01:29:48 +00:00
msg += " - Position: " + position;
2011-08-08 12:40:02 +00:00
break;
case ALL:
2011-11-23 01:29:48 +00:00
msg += " - Position: " + position;
/* FALL-THROUGH */
case ORIENTATION_REGION:
2011-11-23 01:29:48 +00:00
msg += " - Orientation: " + player.getCardinalDirection().name();
/* FALL-THROUGH */
2011-08-08 12:40:02 +00:00
case REGION:
try {
2011-11-23 01:29:48 +00:00
msg += " - Region: " + session.getSelection(player.getWorld());
2011-08-08 12:40:02 +00:00
} catch (IncompleteRegionException e) {
break;
}
break;
}
}
logger.info(msg);
}
super.invokeMethod(parent, args, player, method, instance, methodArgs, level);
}
};
2011-01-29 10:05:22 +00:00
commands.register(ChunkCommands.class);
commands.register(ClipboardCommands.class);
commands.register(GeneralCommands.class);
commands.register(GenerationCommands.class);
commands.register(HistoryCommands.class);
commands.register(NavigationCommands.class);
commands.register(RegionCommands.class);
commands.register(ScriptingCommands.class);
commands.register(SelectionCommands.class);
commands.register(SnapshotUtilCommands.class);
commands.register(ToolUtilCommands.class);
commands.register(ToolCommands.class);
2011-01-29 10:05:22 +00:00
commands.register(UtilityCommands.class);
2011-01-01 01:40:07 +00:00
}
2011-11-20 05:02:54 +00:00
/**
* Gets the LocalSession for a player name if it exists
*
* @param player
* @return The session for the player, if it exists
*/
public LocalSession getSession(String player) {
return sessions.get(player);
}
2011-01-01 01:40:07 +00:00
/**
* Gets the WorldEdit session for a player.
2011-01-01 01:40:07 +00:00
*
* @param player
* @return
*/
public LocalSession getSession(LocalPlayer player) {
LocalSession session;
2011-11-23 01:29:48 +00:00
synchronized (sessions) {
if (sessions.containsKey(player.getName())) {
return sessions.get(player.getName());
}
2011-11-23 01:29:48 +00:00
session = new LocalSession(config);
2011-11-23 01:29:48 +00:00
// 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("worldedit.limit.unrestricted")
&& config.maxChangeLimit > -1) {
2011-11-23 01:29:48 +00:00
// 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);
} else {
// Bound the change limit
int limit = Math.min(config.defaultChangeLimit,
config.maxChangeLimit);
session.setBlockChangeLimit(limit);
}
2011-01-01 01:40:07 +00:00
} else {
// No change limit or override
session.setBlockChangeLimit(config.defaultChangeLimit);
2011-01-01 01:40:07 +00:00
}
2011-11-23 01:29:48 +00: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("worldedit.inventory.unrestricted")));
2011-11-23 01:29:48 +00:00
// Remember the session
sessions.put(player.getName(), session);
2011-01-29 10:05:22 +00:00
}
2011-11-23 01:29:48 +00:00
2011-01-29 10:05:22 +00:00
return session;
}
2011-01-01 01:40:07 +00:00
2011-01-29 10:05:22 +00:00
/**
* Returns true if the player has a session.
*
* @param player
* @return
*/
public boolean hasSession(LocalPlayer player) {
// TODO: If this is indeed used in multiple threads, we should use Collections.synchronizedMap here to simplify things and exclude sources of error.
synchronized (sessions) {
return sessions.containsKey(player.getName());
}
2011-01-29 10:05:22 +00:00
}
2011-01-01 01:40:07 +00:00
public BaseBlock getBlock(LocalPlayer player, String arg, boolean allAllowed)
throws UnknownItemException, DisallowedItemException {
return getBlock(player, arg, allAllowed, false);
}
2011-01-29 10:05:22 +00:00
/**
* Get an item ID from an item name or an item ID number.
*
* @param player
* @param arg
* @param allAllowed true to ignore blacklists
* @return
* @throws UnknownItemException
* @throws DisallowedItemException
*/
public BaseBlock getBlock(LocalPlayer player, String arg,
boolean allAllowed, boolean allowNoData)
2011-01-29 10:05:22 +00:00
throws UnknownItemException, DisallowedItemException {
BlockType blockType;
arg = arg.replace("_", " ");
arg = arg.replace(";", "|");
String[] blockAndExtraData = arg.split("\\|");
String[] typeAndData = blockAndExtraData[0].split(":", 2);
String testID = typeAndData[0];
int blockId = -1;
2011-11-23 01:29:48 +00:00
int data = -1;
2011-01-01 01:40:07 +00:00
2011-01-29 10:05:22 +00:00
// Attempt to parse the item ID or otherwise resolve an item/block
// name to its numeric ID
try {
blockId = Integer.parseInt(testID);
blockType = BlockType.fromID(blockId);
2011-01-29 10:05:22 +00:00
} catch (NumberFormatException e) {
blockType = BlockType.lookup(testID);
if (blockType == null) {
int t = server.resolveItem(testID);
if (t > 0 && t < 256) {
blockType = BlockType.fromID(t);
}
2011-01-01 01:40:07 +00:00
}
2011-01-29 10:05:22 +00:00
}
2011-01-01 01:40:07 +00:00
if (blockId == -1 && blockType == null) {
// Maybe it's a cloth
ClothColor col = ClothColor.lookup(testID);
2011-11-23 01:29:48 +00:00
if (col != null) {
blockType = BlockType.CLOTH;
data = col.getID();
} else {
throw new UnknownItemException(arg);
}
2011-01-29 10:05:22 +00:00
}
2011-11-23 01:29:48 +00:00
// Read block ID
if (blockId == -1) {
blockId = blockType.getID();
}
2011-11-23 01:29:48 +00:00
if (!player.getWorld().isValidBlockType(blockId)) {
throw new UnknownItemException(arg);
}
2011-11-23 01:29:48 +00:00
if (data == -1) { // Block data not yet detected
// Parse the block data (optional)
try {
data = typeAndData.length > 1 ? Integer.parseInt(typeAndData[1]) : (allowNoData ? -1 : 0);
if (data > 15 || (data < 0 && !(allAllowed && data == -1))) {
data = 0;
2011-01-01 01:40:07 +00:00
}
} catch (NumberFormatException e) {
if (blockType != null) {
switch (blockType) {
case CLOTH:
ClothColor col = ClothColor.lookup(typeAndData[1]);
if (col != null) {
data = col.getID();
} else {
throw new InvalidItemException(arg, "Unknown cloth color '" + typeAndData[1] + "'");
}
break;
case STEP:
case DOUBLE_STEP:
BlockType dataType = BlockType.lookup(typeAndData[1]);
if (dataType != null) {
switch (dataType) {
case STONE:
data = 0;
break;
case SANDSTONE:
data = 1;
break;
case WOOD:
data = 2;
break;
case COBBLESTONE:
data = 3;
break;
case BRICK:
data = 4;
break;
case STONE_BRICK:
data = 5;
default:
throw new InvalidItemException(arg, "Invalid step type '" + typeAndData[1] + "'");
}
} else {
throw new InvalidItemException(arg, "Unknown step type '" + typeAndData[1] + "'");
}
break;
default:
throw new InvalidItemException(arg, "Unknown data value '" + typeAndData[1] + "'");
2011-02-26 10:48:07 +00:00
}
} else {
throw new InvalidItemException(arg, "Unknown data value '" + typeAndData[1] + "'");
2011-02-26 10:48:07 +00:00
}
2011-01-01 01:40:07 +00:00
}
2011-01-29 10:05:22 +00:00
}
2011-01-01 01:40:07 +00:00
2011-01-29 10:05:22 +00:00
// Check if the item is allowed
2011-11-23 01:29:48 +00:00
if (allAllowed || player.hasPermission("worldedit.anyblock") || !config.disallowedBlocks.contains(blockId)) {
if (blockType != null) {
switch (blockType) {
case SIGN_POST:
case WALL_SIGN:
// Allow special sign text syntax
String[] text = new String[4];
text[0] = blockAndExtraData.length > 1 ? blockAndExtraData[1] : "";
text[1] = blockAndExtraData.length > 2 ? blockAndExtraData[2] : "";
text[2] = blockAndExtraData.length > 3 ? blockAndExtraData[3] : "";
text[3] = blockAndExtraData.length > 4 ? blockAndExtraData[4] : "";
return new SignBlock(blockType.getID(), data, text);
case MOB_SPAWNER:
// Allow setting mob spawn type
if (blockAndExtraData.length > 1) {
String mobName = blockAndExtraData[1];
for (MobType mobType : MobType.values()) {
if (mobType.getName().toLowerCase().equals(mobName.toLowerCase())) {
mobName = mobType.getName();
break;
}
}
if (!server.isValidMobType(mobName)) {
throw new InvalidItemException(arg, "Unknown mob type '" + mobName + "'");
}
return new MobSpawnerBlock(data, mobName);
} else {
return new MobSpawnerBlock(data, MobType.PIG.getName());
}
2011-11-23 01:29:48 +00:00
case NOTE_BLOCK:
// Allow setting note
if (blockAndExtraData.length > 1) {
byte note = Byte.parseByte(blockAndExtraData[1]);
if (note < 0 || note > 24) {
throw new InvalidItemException(arg, "Out of range note value: '" + blockAndExtraData[1] + "'");
} else {
return new NoteBlock(data, note);
}
} else {
return new NoteBlock(data, (byte) 0);
}
2011-01-01 01:40:07 +00:00
default:
return new BaseBlock(blockId, data);
}
} else {
2011-11-23 01:29:48 +00:00
return new BaseBlock(blockId, data);
}
2011-01-29 10:05:22 +00:00
}
2011-01-01 01:40:07 +00:00
2011-01-29 10:05:22 +00:00
throw new DisallowedItemException(arg);
}
2011-01-01 01:40:07 +00:00
2011-01-29 10:05:22 +00:00
/**
* Get a block.
*
* @param player
* @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 01:40:07 +00:00
2011-11-23 01:29:48 +00:00
public Set<BaseBlock> getBlocks(LocalPlayer player, String list, boolean allAllowed, boolean allowNoData)
throws DisallowedItemException, UnknownItemException {
String[] items = list.split(",");
Set<BaseBlock> blocks = new HashSet<BaseBlock>();
for (String id : items) {
blocks.add(getBlock(player, id, allAllowed, allowNoData));
}
return blocks;
}
public Set<BaseBlock> getBlocks(LocalPlayer player, String list, boolean allAllowed)
throws DisallowedItemException, UnknownItemException {
return getBlocks(player, list, allAllowed, false);
}
public Set<BaseBlock> getBlocks(LocalPlayer player, String list)
throws DisallowedItemException, UnknownItemException {
return getBlocks(player, list, false);
}
2011-01-29 10:05:22 +00:00
/**
2011-11-20 05:02:54 +00:00
* Returns a Pattern corresponding to the specified pattern string,
* as given by the player on the command line.
2011-01-29 10:05:22 +00:00
*
* @param player
2011-11-20 05:02:54 +00:00
* @param patternString
2011-01-29 10:05:22 +00:00
* @return pattern
* @throws UnknownItemException
* @throws DisallowedItemException
2011-01-29 10:05:22 +00:00
*/
2011-11-20 05:02:54 +00:00
public Pattern getBlockPattern(LocalPlayer player, String patternString)
2011-01-29 10:05:22 +00:00
throws UnknownItemException, DisallowedItemException {
2011-01-01 01:40:07 +00:00
2011-11-20 05:02:54 +00:00
String[] items = patternString.split(",");
2011-11-23 01:29:48 +00:00
// Handle special block pattern types
2011-11-20 05:02:54 +00:00
if (patternString.charAt(0) == '#') {
if (patternString.equals("#clipboard") || patternString.equals("#copy")) {
LocalSession session = getSession(player);
CuboidClipboard clipboard;
2011-11-23 01:29:48 +00:00
try {
clipboard = session.getClipboard();
} catch (EmptyClipboardException e) {
player.printError("Copy a selection first with //copy.");
throw new UnknownItemException("#clipboard");
}
2011-11-23 01:29:48 +00:00
return new ClipboardPattern(clipboard);
} else {
2011-11-20 05:02:54 +00:00
throw new UnknownItemException(patternString);
2011-01-30 09:01:11 +00:00
}
}
2011-01-22 10:41:08 +00:00
// If it's only one block, then just return that single one
2011-01-29 10:05:22 +00:00
if (items.length == 1) {
return new SingleBlockPattern(getBlock(player, items[0]));
}
2011-01-23 10:09:51 +00:00
2011-01-29 10:05:22 +00:00
List<BlockChance> blockChances = new ArrayList<BlockChance>();
for (String s : items) {
BaseBlock block;
2011-11-23 01:29:48 +00:00
2011-01-29 10:05:22 +00:00
double chance;
2011-11-23 01:29:48 +00:00
// Parse special percentage syntax
2011-01-29 10:05:22 +00:00
if (s.matches("[0-9]+(?:\\.(?:[0-9]+)?)?%.*")) {
String[] p = s.split("%");
chance = Double.parseDouble(p[0]);
block = getBlock(player, p[1]);
} else {
chance = 1;
block = getBlock(player, s);
2011-01-23 10:09:51 +00:00
}
2011-11-23 01:29:48 +00:00
2011-01-29 10:05:22 +00:00
blockChances.add(new BlockChance(block, chance));
2011-01-01 01:40:07 +00:00
}
2011-01-29 10:05:22 +00:00
return new RandomFillPattern(blockChances);
}
2011-11-23 01:29:48 +00:00
/**
* Get a block mask. Block masks are used to determine which
* blocks to include when replacing.
2011-11-23 01:29:48 +00:00
*
* @param player
* @param session
2011-06-04 18:42:45 +00:00
* @param maskString
* @return
2011-11-23 01:29:48 +00:00
* @throws WorldEditException
*/
public Mask getBlockMask(LocalPlayer player, LocalSession session,
String maskString) throws WorldEditException {
2011-11-20 05:02:54 +00:00
List<Mask> masks = new ArrayList<Mask>();
2011-08-13 04:29:28 +00:00
2011-06-04 18:42:45 +00:00
for (String component : maskString.split(" ")) {
2011-08-13 04:29:28 +00:00
if (component.length() == 0) {
continue;
}
2011-08-13 04:29:28 +00:00
2011-11-20 05:02:54 +00:00
Mask current = getBlockMaskComponent(player, session, masks, component);
masks.add(current);
}
switch (masks.size()) {
case 0:
return null;
case 1:
return masks.get(0);
default:
return new CombinedMask(masks);
}
}
private Mask getBlockMaskComponent(LocalPlayer player, LocalSession session, List<Mask> masks, String component) throws IncompleteRegionException, UnknownItemException, DisallowedItemException {
final char firstChar = component.charAt(0);
switch (firstChar) {
case '#':
if (component.equalsIgnoreCase("#existing")) {
return new ExistingBlockMask();
} else if (component.equalsIgnoreCase("#selection")
|| component.equalsIgnoreCase("#region")
|| component.equalsIgnoreCase("#sel")) {
return new RegionMask(session.getSelection(player.getWorld()));
} else {
throw new UnknownItemException(component);
}
case '>':
case '<':
final LocalWorld world = player.getWorld();
final boolean over = firstChar == '>';
final String idString = component.substring(1);
final Set<Integer> ids = new HashSet<Integer>();
if (!(idString.equals("*") || idString.equals(""))) {
for (String sid : idString.split(",")) {
try {
final int pid = Integer.parseInt(sid);
if (!world.isValidBlockType(pid)) {
throw new UnknownItemException(sid);
2011-08-11 20:44:54 +00:00
}
2011-11-20 05:02:54 +00:00
ids.add(pid);
} catch (NumberFormatException e) {
final BlockType type = BlockType.lookup(sid);
final int id = type.getID();
if (!world.isValidBlockType(id)) {
throw new UnknownItemException(sid);
}
ids.add(id);
2011-08-11 20:44:54 +00:00
}
2011-08-13 04:29:28 +00:00
}
}
2011-11-20 05:02:54 +00:00
return new UnderOverlayMask(ids, over);
case '!':
if (component.length() > 1) {
return new InvertedBlockTypeMask(getBlockIDs(player, component.substring(1), true));
2011-02-19 07:33:55 +00:00
}
2011-11-20 05:02:54 +00:00
default:
return new BlockTypeMask(getBlockIDs(player, component, true));
}
}
2011-11-20 05:02:54 +00:00
2011-01-29 10:05:22 +00:00
/**
* Get a list of blocks as a set.
*
2011-06-04 18:42:45 +00:00
* @param player
2011-01-29 10:05:22 +00:00
* @param list
* @param allBlocksAllowed
* @return set
* @throws UnknownItemException
* @throws DisallowedItemException
2011-01-29 10:05:22 +00:00
*/
public Set<Integer> getBlockIDs(LocalPlayer player,
String list, boolean allBlocksAllowed)
throws UnknownItemException, DisallowedItemException {
2011-11-23 01:29:48 +00:00
2011-01-29 10:05:22 +00:00
String[] items = list.split(",");
Set<Integer> blocks = new HashSet<Integer>();
for (String s : items) {
blocks.add(getBlock(player, s, allBlocksAllowed).getType());
}
return blocks;
}
2011-11-23 01:29:48 +00:00
/**
* Gets the path to a file. This method will check to see if the filename
* has valid characters and has an extension. It also prevents directory
* traversal exploits by checking the root directory and the file directory.
* On success, a <code>java.io.File</code> object will be returned.
*
* @param player
* @param dir sub-directory to look in
* @param filename filename (user-submitted)
* @param defaultExt append an extension if missing one, null to not use
2011-01-31 05:32:52 +00:00
* @param extensions list of extensions, null for any
* @return
* @throws FilenameException
*/
2011-01-31 05:32:52 +00:00
public File getSafeSaveFile(LocalPlayer player, File dir, String filename,
String defaultExt, String... extensions)
2011-01-31 05:32:52 +00:00
throws FilenameException {
return getSafeFile(player, dir, filename, defaultExt, extensions, true);
}
2011-11-23 01:29:48 +00:00
2011-01-31 05:32:52 +00:00
/**
* Gets the path to a file. This method will check to see if the filename
* has valid characters and has an extension. It also prevents directory
* traversal exploits by checking the root directory and the file directory.
* On success, a <code>java.io.File</code> object will be returned.
*
* @param player
2011-01-31 05:32:52 +00:00
* @param dir sub-directory to look in
* @param filename filename (user-submitted)
* @param defaultExt append an extension if missing one, null to not use
* @param extensions list of extensions, null for any
* @return
* @throws FilenameException
*/
public File getSafeOpenFile(LocalPlayer player, File dir, String filename,
String defaultExt, String... extensions)
2011-01-31 05:32:52 +00:00
throws FilenameException {
return getSafeFile(player, dir, filename, defaultExt, extensions, false);
}
2011-11-23 01:29:48 +00:00
2011-01-31 05:32:52 +00:00
/**
* Get a safe path to a file.
*
* @param player
* @param dir
* @param filename
* @param defaultExt
* @param extensions
* @param isSave
* @return
* @throws FilenameException
*/
private File getSafeFile(LocalPlayer player, File dir, String filename,
String defaultExt, String[] extensions, boolean isSave)
throws FilenameException {
if (extensions.length == 1 && extensions[0] == null) extensions = null;
File f;
2011-11-23 01:29:48 +00:00
if (filename.equals("#")) {
2011-01-31 05:32:52 +00:00
if (isSave) {
f = player.openFileSaveDialog(extensions);
} else {
f = player.openFileOpenDialog(extensions);
}
2011-11-23 01:29:48 +00:00
if (f == null) {
throw new FileSelectionAbortedException("No file selected");
}
} else {
if (defaultExt != null && filename.lastIndexOf('.') == -1) {
filename += "." + defaultExt;
}
2011-11-23 01:29:48 +00:00
if (!filename.matches("^[A-Za-z0-9_\\- \\./\\\\'\\$@~!%\\^\\*\\(\\)\\[\\]\\+\\{\\},\\?]+\\.[A-Za-z0-9]+$")) {
2011-09-24 19:24:10 +00:00
throw new InvalidFilenameException(filename, "Invalid characters or extension missing");
}
2011-11-23 01:29:48 +00:00
f = new File(dir, filename);
}
try {
String filePath = f.getCanonicalPath();
String dirPath = dir.getCanonicalPath();
if (!filePath.substring(0, dirPath.length()).equals(dirPath)) {
throw new FilenameResolutionException(filename,
"Path is outside allowable root");
}
2011-11-23 01:29:48 +00:00
return f;
} catch (IOException e) {
throw new FilenameResolutionException(filename,
"Failed to resolve path");
}
}
2011-01-29 10:05:22 +00:00
/**
* Checks to see if the specified radius is within bounds.
*
* @param radius
* @throws MaxRadiusException
*/
public void checkMaxRadius(double radius) throws MaxRadiusException {
2011-01-29 10:05:22 +00:00
if (config.maxRadius > 0 && radius > config.maxRadius) {
throw new MaxRadiusException();
}
2011-01-01 01:40:07 +00:00
}
2011-11-23 01:29:48 +00:00
/**
* Get a file relative to the defined working directory. If the specified
* path is absolute, then the working directory is not used.
*
* @param path
* @return
*/
public File getWorkingDirectoryFile(String path) {
File f = new File(path);
if (f.isAbsolute()) {
return f;
} else {
return new File(config.getWorkingDirectory(), path);
}
}
2011-01-01 01:40:07 +00:00
/**
* Modulus, divisor-style.
*
* @param a
* @param n
* @return
*/
2011-01-29 10:05:22 +00:00
public static int divisorMod(int a, int n) {
2011-09-24 19:24:10 +00:00
return (int) (a - n * Math.floor(Math.floor(a) / (double) n));
2011-01-01 01:40:07 +00:00
}
/**
* Get the direction vector for a player's direction. May return
* null if a direction could not be found.
*
* @param player
* @param dirStr
2011-01-01 01:40:07 +00:00
* @return
* @throws UnknownDirectionException
2011-01-01 01:40:07 +00:00
*/
public Vector getDirection(LocalPlayer player, String dirStr)
2011-01-01 01:40:07 +00:00
throws UnknownDirectionException {
dirStr = dirStr.toLowerCase();
final PlayerDirection dir = getPlayerDirection(player, dirStr);
switch (dir) {
case WEST:
case EAST:
case SOUTH:
case NORTH:
case UP:
case DOWN:
return dir.vector();
default:
throw new UnknownDirectionException(dir.name());
}
2011-11-23 01:29:48 +00:00
}
2011-03-21 14:01:15 +00:00
private PlayerDirection getPlayerDirection(LocalPlayer player, String dirStr) throws UnknownDirectionException {
final PlayerDirection dir;
2011-03-21 14:01:15 +00:00
switch (dirStr.charAt(0)) {
case 'w':
dir = PlayerDirection.WEST;
break;
case 'e':
dir = PlayerDirection.EAST;
break;
case 's':
if (dirStr.indexOf('w') > 0) {
return PlayerDirection.SOUTH_WEST;
2011-03-21 14:01:15 +00:00
}
if (dirStr.indexOf('e') > 0) {
return PlayerDirection.SOUTH_EAST;
}
dir = PlayerDirection.SOUTH;
break;
case 'n':
if (dirStr.indexOf('w') > 0) {
return PlayerDirection.NORTH_WEST;
}
if (dirStr.indexOf('e') > 0) {
return PlayerDirection.NORTH_EAST;
}
dir = PlayerDirection.NORTH;
break;
case 'u':
dir = PlayerDirection.UP;
break;
case 'd':
dir = PlayerDirection.DOWN;
break;
case 'm': // me
case 'f': // forward
dir = player.getCardinalDirection(0);
break;
case 'b': // back
dir = player.getCardinalDirection(180);
break;
case 'l': // left
dir = player.getCardinalDirection(-90);
break;
case 'r': // right
dir = player.getCardinalDirection(90);
break;
default:
throw new UnknownDirectionException(dirStr);
}
return dir;
}
2011-11-23 01:29:48 +00:00
/**
* Get diagonal direction vector for a player's direction. May return
* null if a direction could not be found.
*
* @param player
* @param dirStr
* @return
* @throws UnknownDirectionException
*/
public Vector getDiagonalDirection(LocalPlayer player, String dirStr)
2011-11-23 01:29:48 +00:00
throws UnknownDirectionException {
return getPlayerDirection(player, dirStr.toLowerCase()).vector();
2011-03-21 14:01:15 +00:00
}
2011-01-01 01:40:07 +00:00
/**
2011-09-04 21:18:37 +00:00
* Get the flip direction for a player's direction.
2011-01-01 01:40:07 +00:00
*
* @param player
* @param dirStr
2011-01-01 01:40:07 +00:00
* @return
* @throws UnknownDirectionException
2011-01-01 01:40:07 +00:00
*/
public FlipDirection getFlipDirection(LocalPlayer player, String dirStr)
2011-01-01 01:40:07 +00:00
throws UnknownDirectionException {
final PlayerDirection dir = getPlayerDirection(player, dirStr);
switch (dir) {
case WEST:
case EAST:
return FlipDirection.WEST_EAST;
2011-09-04 21:18:37 +00:00
case NORTH:
case SOUTH:
return FlipDirection.NORTH_SOUTH;
2011-09-04 21:18:37 +00:00
case UP:
case DOWN:
return FlipDirection.UP_DOWN;
2011-09-04 21:18:37 +00:00
default:
throw new UnknownDirectionException(dir.name());
2011-01-01 01:40:07 +00:00
}
}
/**
* Remove a session.
*
* @param player
*/
public void removeSession(LocalPlayer player) {
synchronized (sessions) {
sessions.remove(player.getName());
}
2011-01-01 01:40:07 +00:00
}
/**
* Remove all sessions.
*/
public void clearSessions() {
synchronized (sessions) {
sessions.clear();
}
2011-01-01 01:40:07 +00:00
}
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
/**
* Flush a block bag's changes to a player.
*
* @param player
* @param editSession
*/
2011-01-29 10:05:22 +00:00
public void flushBlockBag(LocalPlayer player,
2011-01-19 10:03:41 +00:00
EditSession editSession) {
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
BlockBag blockBag = editSession.getBlockBag();
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
if (blockBag != null) {
blockBag.flushChanges();
}
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
Set<Integer> missingBlocks = editSession.popMissingBlocks();
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
if (missingBlocks.size() > 0) {
StringBuilder str = new StringBuilder();
str.append("Missing these blocks: ");
int size = missingBlocks.size();
int i = 0;
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
for (Integer id : missingBlocks) {
BlockType type = BlockType.fromID(id);
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
str.append(type != null
? type.getName() + " (" + id + ")"
: id.toString());
2011-11-23 01:29:48 +00:00
++i;
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
if (i != size) {
str.append(", ");
}
}
2011-11-23 01:29:48 +00:00
2011-01-19 10:03:41 +00:00
player.printError(str.toString());
}
}
/**
* @return the commands
*/
2011-01-29 10:05:22 +00:00
public Map<String, String> getCommands() {
return commands.getCommands();
2011-01-19 10:03:41 +00:00
}
2011-11-23 01:29:48 +00:00
2011-12-13 08:23:21 +00:00
/**
* @return the commands
*/
public CommandsManager<LocalPlayer> getCommandsManager() {
return commands;
}
2011-01-01 01:40:07 +00:00
/**
*
* @param player
*/
@Deprecated
public void handleDisconnect(LocalPlayer player) {
forgetPlayer(player);
}
2011-11-23 01:29:48 +00:00
/**
*
* @param player
*/
public void markExpire(LocalPlayer player) {
synchronized (sessions) {
LocalSession session = sessions.get(player.getName());
if (session != null) {
session.update();
}
}
}
2011-11-23 01:29:48 +00:00
/**
* Forget a player.
*
* @param player
*/
public void forgetPlayer(LocalPlayer player) {
2011-01-01 01:40:07 +00:00
removeSession(player);
}
2011-11-23 01:29:48 +00:00
/*
* Flush expired sessions.
*/
public void flushExpiredSessions(SessionCheck checker) {
synchronized (sessions) {
Iterator<Map.Entry<String, LocalSession>> it = sessions.entrySet().iterator();
2011-11-23 01:29:48 +00:00
while (it.hasNext()) {
Map.Entry<String, LocalSession> entry = it.next();
if (entry.getValue().hasExpired()
&& !checker.isOnlinePlayer(entry.getKey())) {
it.remove();
}
}
}
}
2011-01-01 01:40:07 +00:00
/**
* Called on arm swing.
2011-01-01 01:40:07 +00:00
*
* @param player
* @return
2011-01-01 01:40:07 +00:00
*/
public boolean handleArmSwing(LocalPlayer player) {
if (player.getItemInHand() == config.navigationWand) {
if (config.navigationWandMaxDistance <= 0) {
return false;
}
if (!player.hasPermission("worldedit.navigation.jumpto.tool")
&& !player.hasPermission("worldedit.navigation.jumpto")) { // TODO: Remove old permission
return false;
}
WorldVector pos = player.getSolidBlockTrace(config.navigationWandMaxDistance);
if (pos != null) {
player.findFreePosition(pos);
} else {
player.printError("No block in sight (or too far)!");
}
return true;
}
LocalSession session = getSession(player);
Tool tool = session.getTool(player.getItemInHand());
if (tool != null && tool instanceof DoubleActionTraceTool) {
if (tool.canUse(player)) {
((DoubleActionTraceTool) tool).actSecondary(server, config, player, session);
return true;
}
}
return false;
}
/**
* Called on right click (not on a block).
*
* @param player
* @return
*/
public boolean handleRightClick(LocalPlayer player) {
if (player.getItemInHand() == config.navigationWand) {
if (config.navigationWandMaxDistance <= 0) {
return false;
}
if (!player.hasPermission("worldedit.navigation.thru.tool")
&& !player.hasPermission("worldedit.navigation.thru")) { // TODO: Remove old permission
return false;
}
if (!player.passThroughForwardWall(40)) {
player.printError("Nothing to pass through!");
2011-01-19 10:43:26 +00:00
}
return true;
2011-01-09 19:14:55 +00:00
}
LocalSession session = getSession(player);
Tool tool = session.getTool(player.getItemInHand());
if (tool != null && tool instanceof TraceTool) {
if (tool.canUse(player)) {
((TraceTool) tool).actPrimary(server, config, player, session);
return true;
}
}
return false;
2011-01-01 01:40:07 +00:00
}
/**
* 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) {
LocalSession session = getSession(player);
2011-01-01 01:40:07 +00:00
if (player.getItemInHand() == config.wandItem) {
if (!session.isToolControlEnabled()) {
return false;
}
if (!player.hasPermission("worldedit.selection.pos")) {
return false;
}
RegionSelector selector = session.getRegionSelector(player.getWorld());
if (selector.selectSecondary(clicked)) {
selector.explainSecondarySelection(player, session, clicked);
}
return true;
}
Tool tool = session.getTool(player.getItemInHand());
if (tool != null && tool instanceof BlockTool) {
if (tool.canUse(player)) {
2011-11-23 01:29:48 +00:00
((BlockTool) tool).actPrimary(server, config, player, session, clicked);
return true;
}
2011-01-01 01:40:07 +00: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) {
LocalSession session = getSession(player);
2011-01-01 01:40:07 +00:00
if (player.getItemInHand() == config.wandItem) {
if (!session.isToolControlEnabled()) {
return false;
}
if (!player.hasPermission("worldedit.selection.pos")) {
return false;
}
RegionSelector selector = session.getRegionSelector(player.getWorld());
if (selector.selectPrimary(clicked)) {
selector.explainPrimarySelection(player, session, clicked);
}
return true;
}
if (player.isHoldingPickAxe() && session.hasSuperPickAxe()) {
final BlockTool superPickaxe = session.getSuperPickaxe();
if (superPickaxe != null && superPickaxe.canUse(player)) {
return superPickaxe.actPrimary(server, config, player, session, clicked);
2011-01-01 01:40:07 +00:00
}
}
Tool tool = session.getTool(player.getItemInHand());
if (tool != null && tool instanceof DoubleActionBlockTool) {
if (tool.canUse(player)) {
2011-11-23 01:29:48 +00:00
((DoubleActionBlockTool) tool).actSecondary(server, config, player, session, clicked);
return true;
}
}
2011-01-01 01:40:07 +00:00
return false;
}
private static final java.util.regex.Pattern numberFormatExceptionPattern = java.util.regex.Pattern.compile("^For input string: \"(.*)\"$");
2011-01-01 01:40:07 +00:00
/**
*
* @param player
* @param split
* @return whether the command was processed
*/
public boolean handleCommand(LocalPlayer player, String[] split) {
2011-01-01 01:40:07 +00:00
try {
split[0] = split[0].substring(1);
2011-11-23 01:29:48 +00:00
// Quick script shortcut
if (split[0].matches("^[^/].*\\.js$")) {
String[] newSplit = new String[split.length + 1];
System.arraycopy(split, 0, newSplit, 1, split.length);
newSplit[0] = "cs";
2011-01-29 19:37:41 +00:00
newSplit[1] = newSplit[1];
split = newSplit;
}
2011-11-23 01:29:48 +00:00
2011-01-01 01:40:07 +00:00
String searchCmd = split[0].toLowerCase();
2011-11-23 01:29:48 +00:00
// Try to detect the command
if (commands.hasCommand(searchCmd)) {
} else if (config.noDoubleSlash && commands.hasCommand("/" + searchCmd)) {
split[0] = "/" + split[0];
} else if (split[0].length() >= 2 && split[0].charAt(0) == '/'
&& commands.hasCommand(searchCmd.substring(1))) {
split[0] = split[0].substring(1);
}
2011-11-23 01:29:48 +00:00
// No command found!
if (!commands.hasCommand(split[0])) {
return false;
}
2011-11-23 01:29:48 +00:00
LocalSession session = getSession(player);
EditSession editSession = session.createEditSession(player);
editSession.enableQueue();
session.tellVersion(player);
long start = System.currentTimeMillis();
try {
commands.execute(split, player, this, session, player, editSession);
} catch (CommandPermissionsException e) {
player.printError("You don't have permission to do this.");
} catch (MissingNestedCommandException e) {
player.printError(e.getUsage());
} catch (CommandUsageException e) {
player.printError(e.getMessage());
player.printError(e.getUsage());
} catch (WrappedCommandException e) {
throw e.getCause();
} catch (UnhandledCommandException e) {
return false;
} finally {
session.remember(editSession);
editSession.flushQueue();
if (config.profile) {
long time = System.currentTimeMillis() - start;
int changed = editSession.getBlockChangeCount();
if (time > 0) {
double throughput = changed / (time / 1000.0);
player.printDebug((time / 1000.0) + "s elapsed (history: "
+ changed + " changed; "
+ Math.round(throughput) + " blocks/sec).");
} else {
player.printDebug((time / 1000.0) + "s elapsed.");
2011-01-01 01:40:07 +00:00
}
}
2011-11-23 01:29:48 +00:00
flushBlockBag(player, editSession);
2011-01-01 01:40:07 +00:00
}
} catch (NumberFormatException e) {
final Matcher matcher = numberFormatExceptionPattern.matcher(e.getMessage());
if (matcher.matches()) {
2011-11-23 01:29:48 +00:00
player.printError("Number expected; string \"" + matcher.group(1) + "\" given.");
} else {
player.printError("Number expected; string given.");
}
2011-01-19 10:03:41 +00:00
} catch (IncompleteRegionException e) {
2011-01-23 18:11:12 +00:00
player.printError("Make a region selection first.");
2011-01-19 10:03:41 +00:00
} catch (UnknownItemException e) {
player.printError("Block name '" + e.getID() + "' was not recognized.");
} catch (InvalidItemException e) {
player.printError(e.getMessage());
} catch (DisallowedItemException e) {
player.printError("Block '" + e.getID() + "' not allowed (see WorldEdit configuration).");
} catch (MaxChangedBlocksException e) {
2011-01-01 01:40:07 +00:00
player.printError("Max blocks changed in an operation reached ("
2011-01-19 10:03:41 +00:00
+ e.getBlockLimit() + ").");
2011-01-01 01:40:07 +00:00
} catch (MaxRadiusException e) {
player.printError("Maximum radius: " + config.maxRadius);
2011-01-19 10:03:41 +00:00
} catch (UnknownDirectionException e) {
player.printError("Unknown direction: " + e.getDirection());
} catch (InsufficientArgumentsException e) {
player.printError(e.getMessage());
} catch (EmptyClipboardException e) {
2011-01-23 18:11:12 +00:00
player.printError("Your clipboard is empty. Use //copy first.");
} catch (InvalidFilenameException e) {
player.printError("Filename '" + e.getFilename() + "' invalid: "
+ e.getMessage());
} catch (FilenameResolutionException e) {
player.printError("File '" + e.getFilename() + "' resolution error: "
+ e.getMessage());
2011-02-19 04:50:40 +00:00
} catch (InvalidToolBindException e) {
player.printError("Can't bind tool to "
+ ItemType.toHeldName(e.getItemId()) + ": " + e.getMessage());
} catch (FileSelectionAbortedException e) {
player.printError("File selection aborted.");
2011-01-19 10:03:41 +00:00
} catch (WorldEditException e) {
player.printError(e.getMessage());
2011-01-01 01:40:07 +00:00
} catch (Throwable excp) {
player.printError("Please report this error: [See console]");
player.printRaw(excp.getClass().getName() + ": " + excp.getMessage());
excp.printStackTrace();
}
return true;
}
2011-11-23 01:29:48 +00:00
2011-01-22 10:41:08 +00:00
/**
* Executes a WorldEdit script.
*
* @param player
* @param f
2011-01-22 10:41:08 +00:00
* @param args
2011-01-22 23:38:04 +00:00
* @throws WorldEditException
2011-01-22 10:41:08 +00:00
*/
public void runScript(LocalPlayer player, File f, String[] args)
2011-01-22 23:38:04 +00:00
throws WorldEditException {
2011-11-23 01:29:48 +00:00
String filename = f.getPath();
2011-01-22 10:41:08 +00:00
int index = filename.lastIndexOf(".");
String ext = filename.substring(index + 1, filename.length());
2011-11-23 01:29:48 +00:00
2011-01-22 10:41:08 +00:00
if (!ext.equalsIgnoreCase("js")) {
player.printError("Only .js scripts are currently supported");
return;
}
2011-11-23 01:29:48 +00:00
2011-01-22 10:41:08 +00:00
String script;
2011-11-23 01:29:48 +00:00
2011-01-22 10:41:08 +00:00
try {
2011-01-22 21:34:05 +00:00
InputStream file;
2011-11-23 01:29:48 +00:00
2011-01-22 21:34:05 +00:00
if (!f.exists()) {
file = WorldEdit.class.getResourceAsStream(
2011-01-22 21:34:05 +00:00
"craftscripts/" + filename);
2011-11-23 01:29:48 +00:00
2011-01-22 21:34:05 +00:00
if (file == null) {
player.printError("Script does not exist: " + filename);
return;
}
} else {
file = new FileInputStream(f);
}
2011-11-23 01:29:48 +00:00
2011-01-22 21:34:05 +00:00
DataInputStream in = new DataInputStream(file);
2011-01-22 10:41:08 +00:00
byte[] data = new byte[in.available()];
in.readFully(data);
2011-01-22 21:34:05 +00:00
in.close();
2011-01-22 10:41:08 +00:00
script = new String(data, 0, data.length, "utf-8");
} catch (IOException e) {
player.printError("Script read error: " + e.getMessage());
return;
}
2011-11-23 01:29:48 +00:00
2011-01-22 21:34:05 +00:00
LocalSession session = getSession(player);
CraftScriptContext scriptContext =
2011-01-22 23:38:04 +00:00
new CraftScriptContext(this, server, config, session, player, args);
2011-11-23 01:29:48 +00:00
2011-01-22 21:34:05 +00:00
CraftScriptEngine engine = null;
2011-01-31 03:49:57 +00:00
2011-01-22 21:34:05 +00:00
try {
engine = new RhinoCraftScriptEngine();
} catch (NoClassDefFoundError e) {
player.printError("Failed to find an installed script engine.");
player.printError("Please see http://wiki.sk89q.com/wiki/WorldEdit/Installation");
return;
2011-01-22 10:41:08 +00:00
}
2011-11-23 01:29:48 +00:00
engine.setTimeLimit(config.scriptTimeout);
2011-11-23 01:29:48 +00:00
2011-01-22 21:34:05 +00:00
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("argv", args);
vars.put("context", scriptContext);
vars.put("player", player);
2011-11-23 01:29:48 +00:00
2011-01-22 10:41:08 +00:00
try {
2011-01-22 21:34:05 +00:00
engine.evaluate(script, filename, vars);
2011-01-22 10:41:08 +00:00
} catch (ScriptException e) {
2011-11-23 01:29:48 +00:00
player.printError("Failed to execute:");
2011-01-22 21:34:05 +00:00
player.printRaw(e.getMessage());
2011-01-23 00:23:04 +00:00
e.printStackTrace();
2011-01-22 23:38:04 +00:00
} catch (NumberFormatException e) {
throw e;
} catch (WorldEditException e) {
throw e;
} catch (Throwable e) {
2011-01-23 00:23:04 +00:00
player.printError("Failed to execute (see console):");
2011-01-22 23:38:04 +00:00
player.printRaw(e.getClass().getCanonicalName());
2011-01-23 00:23:04 +00:00
e.printStackTrace();
2011-01-22 10:41:08 +00:00
} finally {
2011-01-22 23:38:04 +00:00
for (EditSession editSession : scriptContext.getEditSessions()) {
editSession.flushQueue();
2011-01-22 10:41:08 +00:00
session.remember(editSession);
}
}
}
2011-11-23 01:29:48 +00:00
2011-01-29 10:05:22 +00:00
/**
* Get Worldedit's configuration.
*
* @return
*/
public LocalConfiguration getConfiguration() {
return config;
}
2011-11-23 01:29:48 +00:00
2011-02-19 09:22:28 +00:00
/**
* Get the server interface.
*
* @return
*/
public ServerInterface getServer() {
return server;
}
2011-01-31 08:58:29 +00:00
/**
* Get the version.
*
* @return
*/
public static String getVersion() {
if (version != null) {
return version;
}
2011-11-23 01:29:48 +00:00
2011-01-31 08:58:29 +00:00
Package p = WorldEdit.class.getPackage();
2011-11-23 01:29:48 +00:00
2011-01-31 08:58:29 +00:00
if (p == null) {
p = Package.getPackage("com.sk89q.worldedit");
}
2011-11-23 01:29:48 +00:00
2011-01-31 08:58:29 +00:00
if (p == null) {
version = "(unknown)";
} else {
version = p.getImplementationVersion();
2011-11-23 01:29:48 +00:00
2011-01-31 08:58:29 +00:00
if (version == null) {
version = "(unknown)";
}
}
return version;
}
public static void setVersion(String version) {
WorldEdit.version = version;
}
2011-01-01 01:40:07 +00:00
}